From 8f9cc68f76c4fd0960f919a77fb16a6455922deb Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Sun, 29 Jul 2001 18:00:00 +0200 Subject: =?UTF-8?q?Version=200.85=20-=20Added=20Norwegian=20language=20tex?= =?UTF-8?q?ts=20(thanks=20to=20J=F8rgen=20Tvedt).=20-=20Increased=20the=20?= =?UTF-8?q?usleep=20value=20in=20cDvbOsd::Cmd()=20to=205000=20in=20order?= =?UTF-8?q?=20to=20work=20on=20=20=20systems=20with=20the=20KURT/utime-pat?= =?UTF-8?q?ch=20(thanks=20to=20Guido=20Fiala).=20-=20Changed=20the=20check?= =?UTF-8?q?=20whether=20the=20driver=20is=20loaded=20in=20runvdr=20to=20ch?= =?UTF-8?q?eck=20for=20the=20=20=20'dvb'=20module=20(the=20last=20one=20lo?= =?UTF-8?q?aded).=20-=20Fixed=20repeat=20function=20with=20LIRC=20(thanks?= =?UTF-8?q?=20to=20Stefan=20Huelswitt).=20-=20Increased=20the=20upper=20li?= =?UTF-8?q?mit=20for=20the=20symbol=20rate=20to=2030000=20(thanks=20to=20U?= =?UTF-8?q?lrich=20=20=20R=F6der).=20-=20Made=20the=20position=20of=20the?= =?UTF-8?q?=20channel=20display=20configurable=20(thanks=20to=20Stefan=20?= =?UTF-8?q?=20=20Huelswitt).=20-=20Made=20the=20width=20and=20height=20of?= =?UTF-8?q?=20the=20OSD=20configurable=20(thanks=20to=20Stefan=20Huelswitt?= =?UTF-8?q?).=20-=20DiSEqC=20support=20can=20now=20be=20generally=20enable?= =?UTF-8?q?d/disabled=20in=20the=20Setup=20menu.=20This=20=20=20may=20be?= =?UTF-8?q?=20necessary=20if=20your=20multiswitch=20gets=20irritated=20by?= =?UTF-8?q?=20the=20default=20DiSEqC=20=20=20codes=20'0'=20(thanks=20to=20?= =?UTF-8?q?Markus=20Lang).=20-=20Fixed=20replaying=20in=20case=20there=20i?= =?UTF-8?q?s=20no=20index=20file.=20-=20Fixed=20jumping=20to=20an=20editin?= =?UTF-8?q?g=20mark=20when=20replay=20has=20been=20paused.=20-=20Avoiding?= =?UTF-8?q?=20unnecessary=20code=20execution=20in=20the=20replay=20progres?= =?UTF-8?q?s=20display=20(thanks=20=20=20to=20Guido=20Fiala).=20-=20When?= =?UTF-8?q?=20entering=20time=20values=20the=20digits=20that=20still=20hav?= =?UTF-8?q?e=20to=20be=20entered=20are=20now=20=20=20shown=20as=20'-'=20(a?= =?UTF-8?q?s=20in=20"1-:--").=20-=20When=20setting=20an=20editing=20mark?= =?UTF-8?q?=20while=20the=20progress=20display=20is=20not=20active,=20the?= =?UTF-8?q?=20=20=20display=20will=20now=20be=20turned=20on=20for=20a=20sh?= =?UTF-8?q?ort=20while=20to=20indicate=20the=20successful=20=20=20setting?= =?UTF-8?q?=20of=20the=20mark.=20-=20Updated=20'channels.conf'=20for=20Pre?= =?UTF-8?q?miere=20World=20(thanks=20to=20Helmut=20Sch=E4chner).=20=20=20C?= =?UTF-8?q?heck=20your=20timers=20if=20you=20use=20this=20channels.conf=20?= =?UTF-8?q?file,=20since=20the=20sequence=20of=20=20=20several=20PW=20chan?= =?UTF-8?q?nels=20has=20been=20changed.=20-=20Changed=20the=20color=20of?= =?UTF-8?q?=20"Info"=20messages=20to=20"black=20on=20green"=20and=20that?= =?UTF-8?q?=20of=20the=20=20=20confirmation=20messages=20(like=20"Delete..?= =?UTF-8?q?.")=20to=20"black=20on=20yellow".=20-=20Fixed=20display=20with?= =?UTF-8?q?=20DEBUG=5FOSD=20(it=20still=20crashes=20sometimes,=20esp.=20wh?= =?UTF-8?q?en=20replaying,=20=20=20but=20I=20can't=20seem=20to=20find=20wh?= =?UTF-8?q?at=20causes=20this...=20any=20ideas=20anybody=3F).=20-=20Avoidi?= =?UTF-8?q?ng=20audio/video=20distortions=20in=20'Transfer=20Mode'=20by=20?= =?UTF-8?q?no=20longer=20actually=20=20=20tuning=20the=20primary=20interfa?= =?UTF-8?q?ce=20(which=20can't=20receive=20this=20channel,=20anyway).=20?= =?UTF-8?q?=20=20Apparently=20the=20driver=20gets=20irritated=20when=20the?= =?UTF-8?q?=20channel=20is=20switched=20and=20a=20=20=20replay=20session?= =?UTF-8?q?=20is=20started=20immediately=20after=20that.=20-=20Increased?= =?UTF-8?q?=20timeout=20until=20reporting=20"video=20data=20stream=20broke?= =?UTF-8?q?n"=20when=20recording.=20-=20Explicitly=20switching=20back=20to?= =?UTF-8?q?=20the=20previously=20active=20channel=20after=20ending=20a=20?= =?UTF-8?q?=20=20replay=20session=20(to=20have=20it=20shown=20correctly=20?= =?UTF-8?q?in=20case=20it=20was=20in=20'Transfer=20Mode').?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CONTRIBUTORS | 36 + FORMATS | 22 +- HISTORY | 163 +++- INSTALL | 16 +- MANUAL | 48 +- Makefile | 8 +- Tools/schnitt/README | 63 +- Tools/schnitt/cut.pl | 19 +- Tools/schnitt/cut2 | 2 +- Tools/schnitt/cutall | 4 +- Tools/schnitt/cutall2 | 2 +- Tools/schnitt/cutt | 129 ++-- Tools/schnitt/getpreviframe.pl | 6 - Tools/schnitt/mv2 | 35 +- Tools/schnitt/schnitt.pl | 3 +- Tools/schnitt/schnitt2.pl | 2 + Tools/schnitt/show | 2 +- Tools/schnitt/vmount | 2 +- channels.conf | 122 +-- config.c | 81 +- config.h | 26 +- dvbapi.c | 1621 +++++++++++++++++++++++----------------- dvbapi.h | 75 +- dvbosd.c | 352 +++++++-- dvbosd.h | 84 ++- eit.c | 257 ++++--- eit.h | 10 +- i18n.c | 458 +++++++++++- interface.c | 22 +- interface.h | 4 +- menu.c | 250 ++++--- menu.h | 6 +- osd.h | 4 +- recording.c | 57 +- recording.h | 4 +- remote.c | 12 +- remote.h | 4 +- remux.c | 539 +++++++++++-- remux.h | 37 +- ringbuffer.c | 37 +- ringbuffer.h | 8 +- runvdr | 36 +- svdrp.c | 8 +- thread.c | 60 +- thread.h | 17 +- tools.c | 22 +- tools.h | 3 +- vdr.c | 20 +- videodir.c | 4 +- 49 files changed, 3431 insertions(+), 1371 deletions(-) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 8a67f37..676cf89 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -23,6 +23,7 @@ Guido Fiala for implementing the SVDRP command 'HITK' for implementing image grabbing for implementing overlay capabilities (see his 'kvdr' tool at http://www.s.netic.de/gfiala) + for making the replay progress display avoid unnecessary code execution Robert Schneider for implementing EIT support for displaying the current/next info @@ -42,6 +43,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 +56,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 +73,36 @@ 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 + +Henning Holtschneider + for patching 'runvdr' to check whether the driver is already loaded + +Paulo Manuel Martins Lopes + for translating the OSD texts to the Portugese language + +Markus Lang and Ulrich Röder + for making DiSEqC support configurable + +Jean-Claude Repetto + for translating the OSD texts to the French language + +Andre Valentin + for increasing the key name buffer size for LIRC + +Jørgen Tvedt + for translating the OSD texts to the Norwegian language + +Stefan Huelswitt + for fixing the repeat function with LIRC + for making the position of the channel display configurable + for making the width and height of the OSD configurable + +Ulrich Röder + for pointing out that there are channels that have a symbol rate higher than + 27500. + +Helmut Schächner + for his support in keeping the Premiere World channels up to date in 'channels.conf' diff --git a/FORMATS b/FORMATS index 2a357a3..03739ee 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): @@ -25,7 +25,9 @@ Video Disk Recorder File Formats - Diseqc number ** - Symbol rate - Video PID - - Audio PID + - Audio PID (either one number, or two, separated by a comma) + If this channel also carries Dolby Digital sound, the Dolby PIDs follow + the audio PIDs, separated by a semicolon, as in "...:101,102;103,104:..." - Teletext PID - Conditional Access (0 = Free To Air, 1 = can be decrypted by the first DVB card, 2 = can be decrypted by the second DVB card) @@ -60,8 +62,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 '|'; @@ -126,3 +130,13 @@ Video Disk Recorder File Formats - marks must have a frame number, and that frame MUST be an I-frame (this means that only marks generated by VDR itself can be used, since they will always be guaranteed to mark I-frames). + +* 001.vdr ... 255.vdr + + These are the actual recorded MPEG data files. In order to keep the size of + an individual file below a given limit, a recording is split into several + files. The contents of these files is "Packetized Elementary Stream" (PES) + and contains ES packets with ids 0xE0 for video, 0xC0 for audio 1 and 0xC1 + for audio 2 (if available). Dolby Digital data is stored in packets with + ids 0xBD. + diff --git a/HISTORY b/HISTORY index 203377d..d625b26 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,160 @@ 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). + +2001-06-12: Version 0.81 + +- Fixed handling the case where the driver reports EAGAIN during recording, + but no data comes within 5 seconds. +- Fixed EPG scanning on single DVB card systems. +- There can now be two audio PIDs per channel, which can be toggled via the + "Green" button in the "Main" menu. The "Edit Channel" menu therefore now + has two audio PID fields (Apid1 and Apid2). By default, Apid2 is 0, which + means there is no alternate audio track. +- Fixed replaying in case the driver reports EAGAIN. +- Now 'runvdr' checks if the driver is already loaded (thanks to Henning + Holtschneider). +- Fixed removing recordings with Lifetime = 99. +- Improved channel switching. + +2001-06-16: Version 0.82 + +- Increased timeout until reporting "broken video data stream" when recording. +- Increased amount of non-useful data received by cRemux before assuming the + recording will fail. +- If there are two audio PIDs defined for a channel, both audio tracks will + now be recorded and can be selectively replayed later. See the FORMATS file + for details on how these different audio tracks are stored in the recorded + files. In order for this to work properly you need to use a driver version + dated 2001-06-16 or later, where the default PES filter buffer size has been + reduced. This will create packets for the second audio track that are small + enough to multiplex smoothly with the video data. +- Fixed a bug in the editing mechanism (didn't work with recordings that + consist of more than one data file). +- The compile time switch VFAT has been fixed to recognize the ':' character + in recording names, too. +- Setting all PIDs to 0x1FFF before switching channel. +- New setup parameter "VideoFormat" to define the aspect ratio of the tv set + in use (4:3 or 16:9). + +2001-06-26: Version 0.83 + +- Avoiding "Device or resource busy" error message when setting PIDs. +- Added Portugese language texts (thanks to Paulo Manuel Martins Lopes). +- Recording and replaying Dolby Digital (AC3) sound. +- No longer getting stuck when a channel doesn't sync while switching + with the 'Up' and 'Down' keys. + +2001-07-22: Version 0.84 + +- Fixed video packet scanning to make it recognize the whole range of + allowed video packet ids. +- Added an additional "emergency exit" in case channel switching doesn't + work several times in a row (when will the driver finally become stable + enough to allow rock solid channel switching??). +- No longer sending a Diseqc command if the Diseqc value for a given channel + is '0'. Previously this caused problems with some multi-switches (thanks to + Markus Lang and Ulrich Röder). +- When switching channels by entering the channel number via the numeric keys + on the remote control, the channel number displayed is now followed by the + '-' character to indicate that additional digits can be entered. +- Increased the timeout for numeric channel switching from 500ms to 1s. +- Fixed handling the "Green" button in the "Schedules" menu for channels that + have a second audio PID. +- Fixed high system load when displaying a still picture in replay. +- Fixed a hanging SVDRP connection if the client dies without issuing QUIT. +- Increased the frame buffer size to 192KB. +- Removed a superfluous VIDEO_FREEZE call in the replay buffer. +- Added French language texts (thanks to Jean-Claude Repetto). +- Modified OSD to use 2bpp windows (4 colors) in order to work with less + memory, allow a larger OSD window and be faster. The group separators in the + "Channels" menu had to be given a different color. +- Moved the channel display to the bottom of the screen. +- Displaying the frame counter in the replay progress display only when editing + a mark. +- Fixed handling characters above 0xEF in SVDRP. +- Fixed a possible crash in parsing incorrect lines in 'channels.conf'. +- New channel settings for Premiere World (Dolby Digital PIDs not yet + available). +- Increased the buffer for key names received from LIRC (thanks to Andre + Valentin). +- Fixed handling a channel group separator at the very beginning of the + 'channels.conf' file. + +2001-07-29: Version 0.85 + +- Added Norwegian language texts (thanks to Jørgen Tvedt). +- Increased the usleep value in cDvbOsd::Cmd() to 5000 in order to work on + systems with the KURT/utime-patch (thanks to Guido Fiala). +- Changed the check whether the driver is loaded in runvdr to check for the + 'dvb' module (the last one loaded). +- Fixed repeat function with LIRC (thanks to Stefan Huelswitt). +- Increased the upper limit for the symbol rate to 30000 (thanks to Ulrich + Röder). +- Made the position of the channel display configurable (thanks to Stefan + Huelswitt). +- Made the width and height of the OSD configurable (thanks to Stefan Huelswitt). +- DiSEqC support can now be generally enabled/disabled in the Setup menu. This + may be necessary if your multiswitch gets irritated by the default DiSEqC + codes '0' (thanks to Markus Lang). +- Fixed replaying in case there is no index file. +- Fixed jumping to an editing mark when replay has been paused. +- Avoiding unnecessary code execution in the replay progress display (thanks + to Guido Fiala). +- When entering time values the digits that still have to be entered are now + shown as '-' (as in "1-:--"). +- When setting an editing mark while the progress display is not active, the + display will now be turned on for a short while to indicate the successful + setting of the mark. +- Updated 'channels.conf' for Premiere World (thanks to Helmut Schächner). + Check your timers if you use this channels.conf file, since the sequence of + several PW channels has been changed. +- Changed the color of "Info" messages to "black on green" and that of the + confirmation messages (like "Delete...") to "black on yellow". +- Fixed display with DEBUG_OSD (it still crashes sometimes, esp. when replaying, + but I can't seem to find what causes this... any ideas anybody?). +- Avoiding audio/video distortions in 'Transfer Mode' by no longer actually + tuning the primary interface (which can't receive this channel, anyway). + Apparently the driver gets irritated when the channel is switched and a + replay session is started immediately after that. +- Increased timeout until reporting "video data stream broken" when recording. +- Explicitly switching back to the previously active channel after ending a + replay session (to have it shown correctly in case it was in 'Transfer Mode'). diff --git a/INSTALL b/INSTALL index 6910f6b..28037eb 100644 --- a/INSTALL +++ b/INSTALL @@ -15,7 +15,7 @@ If you have the DVB driver source in a different location you will have to change the definition of DVBDIR in the Makefile. -This program requires the card driver version 0.8.2 or higher +This program requires the card driver version 0.9.0 or higher to work properly. You need to load the dvb.o module *without* option 'outstream=0' (previous versions of VDR required this option to have the driver supply the data in AV_PES format; as of version 0.70 VDR @@ -81,13 +81,25 @@ Automatic restart in case of hangups: If you run VDR using the 'runvdr' shell script it will use the built-in watchdog timer to restart the program in case something happens that -causes a program hangup. +causes a program hangup. If you change the command line options for the +call to the VDR program, be sure to NOT use the '-d' option! Otherwise +VDR will go into 'deamon' mode and the initial program call will return +immediately! Command line options: --------------------- Use "vdr --help" for a list of available command line options. +Replaying Dolby Digital audio: +------------------------------ + +To replay Dolby Digital audio you need a program that reads the DD data +from stdin and processes it in a way suitable for your audio hardware. +This program must be given to VDR with the '-a' option, as in + + vdr -a ac3play + The video data directory: ------------------------- diff --git a/MANUAL b/MANUAL index f77bd27..c59f3fe 100644 --- a/MANUAL +++ b/MANUAL @@ -18,7 +18,7 @@ Video Disk Recorder User's Manual Menu Menu on Menu off Menu off Menu off Menu off Menu off Menu on Back - Menu off Main menu Main menu Discard Main menu Recordings menu Red - Record Edit Edit - Play - - Green - - New New - Rewind Skip -60s + Green - Language New New - Rewind Skip -60s Yellow - - Delete Delete - Delete Skip +60s Blue - Resume Mark Mark - Summary Stop 0..9 Ch select - - - Numeric inp. - Editing @@ -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). @@ -100,7 +100,7 @@ Video Disk Recorder User's Manual and browse through the list with the "Up" and "Down" key; to switch to the selected channel press "Ok". 3. Directly type in the channel number with the numeric keys ('0'..'9'); - if no key is pressed for about half a second, the digits collected so + if no key is pressed for about one second, the digits collected so far will define the channel number. 4. From the "Now", "Next" and "Event" menus (accessible through the "Schedule" menu) by pressing the "Blue" button. @@ -115,6 +115,16 @@ Video Disk Recorder User's Manual To bring up the channel display without switching channels you can press the "Ok" button. +* Selecting language specific audio track + + If the current channel provides different audio tracks (typically for + different languages), the "Green" button in the "Main" menu can be pressed + to toggle between these. There can be two different audio PIDs per channel, + assuming that typically a channel broadcasts a country specific language + plus the movie's original soundtrack. + Recordings made form such channels will contain both audio tracks, and when + replaying the desired audio track can be selected the same way. + * Switching through channel groups If the 'channels.conf' file contains "group separators" you can switch @@ -257,6 +267,9 @@ Video Disk Recorder User's Manual to free up space for a new recording. Note that setting this parameter to very high values for all recordings may soon fill up the entire disk and cause new recordings to fail due to low disk + space. The special value 99 means that this recording will live + "forever", and a value of 0 means that this recording can be + deleted any time if a recording with a higher priority needs disk space. File: The name under which a recording created through this timer will be stored on disk (the actual name will also contain the date and @@ -309,9 +322,14 @@ 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) + DiSEqC = 1 Generally turns DiSEqC support on or off. + 0 = disabled + 1 = enabled + SetSystemTime = 0 Defines whether the system time will be set according to the time received from the DVB data stream. 0 = system time will not be set @@ -341,6 +359,22 @@ Video Disk Recorder User's Manual never keep the user from viewing stuff on the primary interface. On systems with only one DVB card, timers with a priority below PrimaryLimit will never execute. + + DefaultPriority = 50 The default Priority and Lifetime values used when + DefaultLifetime = 50 creating a new timer event. A Lifetime value of 99 + means that this recording will never be deleted + automatically. + + VideoFormat = 0 The video format (or aspect ratio) of the tv set in use. + 0 = 4:3 + 1 = 16:9 + + ChannelInfoPos = 0 The position of the channel info window in the OSD. + 0 = bottom + 1 = top + + OSDwidth = 52 The width and height of the OSD . + OSDheight = 18 The valid ranges are width=40...56, height=12...21. * Executing system commands diff --git a/Makefile b/Makefile index cec04ca..507de21 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/11 11:36:15 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/Tools/schnitt/README b/Tools/schnitt/README index 29953e0..8e1cacc 100644 --- a/Tools/schnitt/README +++ b/Tools/schnitt/README @@ -1,4 +1,6 @@ Sammlung von "Hilfs"-Scripten + von Matthias Schniedermeyer + ms@citd.de Diese Sammlung an "Hilfs"-Scripten habe ich mir zum scheiden und anderen Zwecken zusammengeschrieben. @@ -10,10 +12,10 @@ Ich bin nicht sonderlich gut im "Dokumentieren". Also gilt die Devise Ein paar Worte zu den "Hart"-Codierten Pfaden. -/yele/video (/video/video0) -/yelg/video (/video/video1) +/dvb/video +/dvb2/video -Sind die 2 Pfade auf meinem DVB-Rechner +Sind die 2 DVB-Rechner. /x1/video @@ -30,14 +32,23 @@ Enthalten sind folgende Scripte: cutall -> "Master"-Script zum starten des Scheide vorgangs. Ist ein "find" nach "cut" -Dateien cutt -> Das "eigentliche" Schnitt-Script - Scheidet die Stuecke aus, demultipext, remultipext, - splitted die Dateien und macht am Ende ein - ISO-Image daraus + Bei einem einzelnen Block wird diese direkt + gesplitet und dann zu einem ISO-Image verarbeitet + Wenn mehrere Teile (=War mit Werbung) vorhanden + sind, dann werden zuerst die einzelnen Teile, + legt nummerierte Verzeichniss ein und kopiert bzw + splitet die einzelnen Dateien in die Verzeichniss + und macht dann daraus die ISO-Images + (Leider funktioniert das aus irgendwelche Gruenden + (noch) nicht mit Serien die nur eine CD gross sind + aber das wird noch) index.php -> PHP-Script zum finden der Schnitt-Punkte, mit Testmoeglichkeit ob erfolgreich an diesem Punkt geschnitten werden kann -mv2 -> Zum Moven der Aufnahmen von meinem DVB-Rechner +mv2 -> Zum Moven der Aufnahmen von meinen DVB-Rechnern auf mein "Arbeitsrechner" +play -> Zum Abspielen einer Aufnahme von meinem Recher auf + dem "Frontend" DVB-Rechner schnitt.pl -> Extraiert ein einzelnes Bild um es anzuzeigen (Fuer index.php) schnitt2.pl -> Gibt alles zwischen 2 Schnittpunkten auf STDOUT aus @@ -58,27 +69,43 @@ vdr2 -> Start-Script vmount -> Mounten aller zusammengehoeriger ISO-Images zum abspielen -Hilsscripte: +Hilfsscripte: ------------ cut.pl -> Entspricht weitestgehend "split" aber mit "Nummer" anstatt Buchstaben cut2 -> Entfernt escapende Backslashes +cut2.pl/cut3.pl -> Fuer Aufnehmen die aus mehreren Bloecken bestehen + (=Mit Werbung) cutall2 -> Springt ins Schnitt-Verzeichniss und ruft das "eigentliche" Schnitt-Script auf -lmplex -> Multiplexed Datenstrome unter Zurhilfename - saemtlicher CPUs +cutall3.pl -> Wenn in einem Verzeichniss mehrere Aufnahmen sind + splitet dies Programm diese und ruft fuer jede + Aufnahme das "cutt"-Script auf schnittcommon.pli -> Das "Common" Script fuer schnitt?.pl getpreviframe.pl -> Findet das vorherige I-Frame. unsort -> Macht das Gegenteil von sort. +dump.c -> Muss in der "libmpeg3"-Verzeichniss des + "mpeg2-movie"-Packets kopiert werden und dann mit + "make" compilieren + Dieses C-Programm ist dafuer da um fuer das + PHP-Script das Bild anzuzeigen wo man gerade ist. + Da mir der "urspruengliche" Source-Code dafuer + abhanden gekommen ist und ich den Patch neumachen + musste (Wie immer mit "Verbesserungen") ist index.php + nocht nicht auf die neue Version angepasst. Die + Anpassung besteht aber nur darin eine "1" an die + Kommandozeile anzuhaengen. -Die ganze "Schnittloesung" ist leider etwas "unbrauchbar", weil ich aus -Unachtsamkeit leider die gepatchten Sourcen von 2 wichtigen Programm -geloescht habe. -dumpfrage -> Extraiert das erste Frage in eine Datei zum - anzeigen (gepatchtes dump aus "libmpeg3" -pvademux -> gepatcht um eine Pfad-Angabe +Im Gegensatz zur "alten"-Version ist diese Version "vollstaendig" +einsatzfaehig! -Entweder macht jemand/ich patchen "nochmal" oder ich kann auch die -Binaries zur Verfuegung stellen. + +Wenn jemand Fragen zum Einsatz hat dann "loechern Sie mich" + +ms@citd.de + + +Ich kann auch gerne die Einrichtung per "Fernwartung" (=ssh) uebernehmen. +(Wenn noetig compiliere ich auch auch apache mit PHP usw.) diff --git a/Tools/schnitt/cut.pl b/Tools/schnitt/cut.pl index dd62c18..e055076 100755 --- a/Tools/schnitt/cut.pl +++ b/Tools/schnitt/cut.pl @@ -1,30 +1,25 @@ -#!/usr/bin/perl -w +#!/usr/bin/perl -use strict; +chdir ($ARGV[0]) if ($ARGV[0]); -my $maxsize = 660 * 1024 * 1024; +$read = $size = 1024*1024; -my $read = 1024*1024; -my $size = 1024*1024; - -my $filenum = "1"; -my $count = 0; - -my ($fi,$data); +$filenum = "1"; +$count = 0; $fi = sprintf ("part%d",$filenum); open (FI,">$fi"); while ($read == $size) { - if ($count < $maxsize) + if ($count < 660*1024*1024) { $read = read (STDIN,$data,$size); print FI $data; $count += $size; $a = $count /1024/1024; if ($a % 10 == 0) { - print STDERR "File: $filenum Size: ${a}MB\n"; + print stderr "File: $filenum Size: ${a}MB\n"; } } else diff --git a/Tools/schnitt/cut2 b/Tools/schnitt/cut2 index 0193ac0..09e9acb 100755 --- a/Tools/schnitt/cut2 +++ b/Tools/schnitt/cut2 @@ -1,2 +1,2 @@ #!/bin/sh -cat cut | head -n 1 | tr -d [\\\\] +cat cut2 | head -n 1 | tr -d [\\\\] diff --git a/Tools/schnitt/cutall b/Tools/schnitt/cutall index 355935f..a6803e2 100755 --- a/Tools/schnitt/cutall +++ b/Tools/schnitt/cutall @@ -1,4 +1,2 @@ #!/bin/sh -cutdir=/x1/video/ - -find $cutdir -name "cut" -exec cutall2 {} \; +find /x1/video/ -name "cut" -exec cutall2 {} \; diff --git a/Tools/schnitt/cutall2 b/Tools/schnitt/cutall2 index 90b0f46..962d577 100755 --- a/Tools/schnitt/cutall2 +++ b/Tools/schnitt/cutall2 @@ -1,5 +1,5 @@ #!/bin/sh a=`echo $1 | cut -d / -f1-5` cd $a -cutt +cutall3.pl mv cut cut.bak diff --git a/Tools/schnitt/cutt b/Tools/schnitt/cutt index 4e89b7a..1942437 100755 --- a/Tools/schnitt/cutt +++ b/Tools/schnitt/cutt @@ -3,10 +3,13 @@ DIRA=/x2/temp DIRB=/x1/temp -if [ -f cut ]; then +if [ ! -f cut2 ]; then + echo Keine Beschreibungsdatei + exit 1 +else name="`cut2`" echo $name - count=`cat cut | wc -l` + count=`cat cut2 | wc -l` let count=count-1 let test=count%2 if [ "$test" == "1" ]; then @@ -16,70 +19,88 @@ if [ -f cut ]; then file=1 - while [ "$count" != "0" ] - do - start=`cat cut | tail -n $count | head -n 1` + if [ "$count" == "2" ]; then + start=`cat cut2 | tail -n $count | head -n 1` let count=count-1 - end=`cat cut | tail -n $count | head -n 1` - let count=count-1 - echo Cutting\&Demuxing from $start to $end - schnitt2.pl $start $end | pvademux $DIRA teil$file -# schnitt2.pl $start $end | pes2av_pes | pvademux $DIRA teil$file - let file=file+1 - done -else - echo Keine Beschreibungsdatei - exit 1 + end=`cat cut2 | tail -n $count | head -n 1` + schnitt2.pl $start $end | avpes2mpeg | cut.pl $DIRA + secondway=2 + else + while [ "$count" != "0" ] + do + start=`cat cut2 | tail -n $count | head -n 1` + let count=count-1 + end=`cat cut2 | tail -n $count | head -n 1` + let count=count-1 + echo Cutting \& Converting from $start to $end + schnitt2.pl $start $end | avpes2mpeg > $DIRA/teil$file.mpg +# schnitt2.pl $start $end | pvademux $DIRA teil$file + let file=file+1 + done + fi fi # Ab hier mkimg -sync - -lmplex $DIRA $DIRB `ls -la $DIRA/teil*.m2v | cut -b 30- | sort -n -r | cut -d / -f4` - -echo Multiplexing DONE - -rm -f $DIRA/teil*.m2v $DIRA/teil*.mp2 +rm cut2 sync -if [ -f $DIRB/teil1.mpg ]; then - echo Splitting +if [ "$secondway" != "2" ]; then cd $DIRA -# cat $DIRB/teil*.mpg | split -b 723517440 - cat $DIRB/teil*.mpg | cut.pl - rm $DIRB/teil* -fi - -sync - -cd $DIRA + cut2.pl "$name" + if [ -d 2 ]; then + count=1 + cond=0 + + while [ "$cond" != "1" ] + do + echo mkisofs Teil $count + mkisofs -r -o $DIRB/image1.raw $count + rm -rf $count + mv -- $DIRB/image1.raw "$DIRB/${name} CD $count" + sync + let count=count+1 + if [ ! -d $count ]; then + cond=1 + fi + done + else + echo mkisofs + mkisofs -r -o $DIRB/image1.raw $count + rm -rf $count + mv -- $DIRB/image1.raw "$DIRB/${name}" + fi +else -if [ -f part2 ]; then - count=1 - cond=0 + cd $DIRA - while [ "$cond" != "1" ] - do + if [ -f part2 ]; then + count=1 + cond=0 + + while [ "$cond" != "1" ] + do + mkdir a + mv "part$count" "a/${name} CD $count.mpg" + echo mkisofs Teil $count + mkisofs -r -o $DIRB/image1.raw a + rm -rf a + mv -- $DIRB/image1.raw "$DIRB/${name} CD $count" + sync + + let count=count+1 + if [ ! -f "part$count" ]; then + cond=1 + fi + done + else mkdir a - mv "part$count" "a/${name} Teil $count" - echo mkisofs Teil $count + mv part1 "a/${name}.mpg" + echo mkisofs mkisofs -r -o $DIRB/image1.raw a rm -rf a - mv -- $DIRB/image1.raw "$DIRB/${name} Teil $count" - sync - - let count=count+1 - if [ ! -f "part$count" ]; then - cond=1 - fi - done -else - mkdir a - mv part1 "a/${name}" - echo mkisofs - mkisofs -r -o $DIRB/image1.raw a - rm -rf a - mv -- $DIRB/image1.raw "$DIRB/${name}" + mv -- $DIRB/image1.raw "$DIRB/${name}" + fi fi + diff --git a/Tools/schnitt/getpreviframe.pl b/Tools/schnitt/getpreviframe.pl index 6774f31..4829304 100755 --- a/Tools/schnitt/getpreviframe.pl +++ b/Tools/schnitt/getpreviframe.pl @@ -1,18 +1,12 @@ #!/usr/bin/perl -use strict; - -my ($index, $oindex); - require "/usr/local/bin/my/schnittcommon.pli"; if (!open (INDEX,"index.vdr")) { exit 1; } - $index = $oindex = $ARGV[0]; - if ($index > 0) { &prevI; diff --git a/Tools/schnitt/mv2 b/Tools/schnitt/mv2 index a0cefba..c27a9ff 100755 --- a/Tools/schnitt/mv2 +++ b/Tools/schnitt/mv2 @@ -1,23 +1,30 @@ #!/bin/sh +# +# Move files from DVB-Computer(s) to localmachine into $LOCALDIR +# -DIR = /x1/video +LOCALDIR=/x1/video if [ ! "$UID" = 0 ]; then if [ -d "$1" ]; then - cd $DIR - a=`echo "$1" | cut -d / -f4-` - mkdir -p "$a" - cd "$a" - (echo cd "$1"; echo mget \*)| ftp -i dvb - cd $DIR - ls -Ls $1 > /tmp/yele - ls -Ls $a > /tmp/x1 - a=`echo "$1" | cut -d \/ -f3-` - diff -u /tmp/yele /tmp/x1 &> /dev/null && rm -rfv /yel?/$a - rm /tmp/yele - rm /tmp/x1 - rmdir /yel?/video/* + if [ -f "$1/index.vdr" ]; then + cd $LOCALDIR + recdir=`echo "$1" | cut -d / -f4-` + mkdir -p "$recdir" + cd "$recdir" + dvbcomp=`echo "$1" | cut -d / -f2` + (echo cd "/video/video0/$recdir"; echo mget \*)| ftp -i $dvbcomp + cd $LOCALDIR + ssh $dvbcomp ls -Ls /video/video0/$recdir > /tmp/dvb + ls -Ls $recdir > /tmp/local + diff -u /tmp/dvb /tmp/local &> /dev/null && rm -rfv /$dvbcomp/video?/$recdir + rm /tmp/dvb + rm /tmp/local + rmdir --ignore-fail-on-non-empty `find /$dvbcomp/video?/ -type d -mindepth 1 | cut -d \/ -f-4 | grep -v temp | sort | uniq` + fi fi else echo Not as root fi + +/usr/local/bin/my/process_summary.pl diff --git a/Tools/schnitt/schnitt.pl b/Tools/schnitt/schnitt.pl index d5521eb..ac72a14 100755 --- a/Tools/schnitt/schnitt.pl +++ b/Tools/schnitt/schnitt.pl @@ -21,6 +21,7 @@ syswrite (FO,$temp,200000); close (FI); close (FO); -`/usr/local/bin/pvademux.old /x2/temp bild`; +`cat bild | /usr/local/bin/avpes2mpeg > avpes`; +`/usr/local/bin/my/dumpframe avpes 1`; #`/usr/local/bin/pes2av_pes bild | /usr/local/bin/pvademux /x2/temp bild`; print "$index\n"; diff --git a/Tools/schnitt/schnitt2.pl b/Tools/schnitt/schnitt2.pl index e52b245..d81b5fd 100755 --- a/Tools/schnitt/schnitt2.pl +++ b/Tools/schnitt/schnitt2.pl @@ -10,10 +10,12 @@ if (!open (INDEX,"index.vdr")) $index = $ARGV[0]; &nextI; +#&readnext; $file1 = $file; $offset1 = $offset; $index = $ARGV[1]; &nextI; +#&readnext; $file2 = $file; $offset2 = $offset; diff --git a/Tools/schnitt/show b/Tools/schnitt/show index afed79b..bfcc58a 100755 --- a/Tools/schnitt/show +++ b/Tools/schnitt/show @@ -5,7 +5,7 @@ do if [ -f newpic ]; then killall xli rm -f newpic - xli output.ppm & + xli output000.ppm & fi sleep 24h done diff --git a/Tools/schnitt/vmount b/Tools/schnitt/vmount index 6b79064..ab16e45 100755 --- a/Tools/schnitt/vmount +++ b/Tools/schnitt/vmount @@ -4,7 +4,7 @@ cond=0 if [ -f "$1" ]; then mount "$1" /mnt/1 -o loop - $cond = 1 + cond=1 else while [ "$cond" != "1" ] do diff --git a/channels.conf b/channels.conf index 36b5b28..8fc1a98 100644 --- a/channels.conf +++ b/channels.conf @@ -1,6 +1,6 @@ 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 +Pro-7:12480:v:0:27500:255:256;257: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 @@ -16,7 +16,7 @@ ZDF:11954:h:0:27500:110:120:130:0:28006 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 +ORF2:12692:h:0:22000:500:501:505:3:13002 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 @@ -46,62 +46,81 @@ 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 +ARD Online-Kanal:12722:h:0:22000:0: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 +Premiere World:11797:h:0:27500:255:256:32:0:8 +Premiere 1:11797:h:0:27500:511:512:0:3:10 +Premiere 2:11797:h:0:27500:1791:1792:0:3:11 +Premiere 3:11797:h:0:27500:2303:2304:0:3:43 +Premiere Star:11797:h:0:27500:767:768:0:3:9 +Premiere Sci-Fi:11797:h:0:27500:1535:1536:0:3:41 +Premiere Action:11797:h:0:27500:1023:1024:0:3:20 +Premiere Comedy:11797:h:0:27500:1279:1280:0:3:29 +13th Street:12031:h:0:27500:2303:2304:0:3:42 +Studio Universal:12090:V:0:27500:255:256:0:3:36 +Filmpalast:12031:h:0:27500:2559:2560:0:3:516 +Heimatkanal:12031:h:0:27500:2815:2816:0:3:517 +Discovery Channel:12031:h:0:27500:1791:1792:0:3:14 +Planet:12090:V:0:27500:1279:1280:0:3:13 +Fox Kids:12031:h:0:27500:1279:1280:0:3:28 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 +K-Toon:12031:h:0:27500:511:512:0:3:12 +Disney Channel:12090:V:0:27500:767:768:0:3:34 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 +Goldstar TV:12031: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 +Seasons:12090:V:0:27500:511:512:0:3:33 +:Cinedom +Cinedom Deluxe:11758:h:0:27500:255:256,257;259:0:3:189 +Cinedom 1A:11758:h:0:27500:511:512,513:0:3:190 +Cinedom 1B:12070:h:0:27500:1535:1536,1537:0:3:178 +Cinedom 1C:11720:h:0:27500:511:512,513:0:3:180 +Cinedom 1D:11720:h:0:27500:1535:1536,1537:0:3:176 +Cinedom 2A:11758:h:0:27500:1023:1024:0:3:193 +Cinedom 2B:11720:h:0:27500:1279:1280:0:3:183 +Cinedom 2C:12070:h:0:27500:1791:1792:0:3:179 +Cinedom 2D:12070:h:0:27500:511:512:0:3:184 +Cinedom 2E:12070:h:0:27500:1279:1280:0:3:188 +Cinedom 3A:11758:h:0:27500:2559:2560:0:3:192 +Cinedom 3B:11758:h:0:27500:1535:1536:0:3:195 +Cinedom 3C:12070:h:0:27500:767:768:0:3:185 +Cinedom 3D:11720:h:0:27500:1023:1024:0:3:182 +Cinedom 4A:11758:h:0:27500:767:768:0:3:191 +Cinedom 4B:11720:h:0:27500:767:768:0:3:181 +Cinedom 4C:12070:h:0:27500:2047:2048:0:3:187 +Cinedom 5A:11758:h:0:27500:1279:1280:0:3:194 +Cinedom 5B:11720:h:0:27500:1791:1792:0:3:177 +Cinedom 5C:12070:h:0:27500:1023:1024:0:3:186 +:Beta Digital +CNBC:12148:h:0:27500:255:256:0:3:35 +Liberty TV.com:12610:V:0:22000:941:943:0:0:12199 +:PW Erotic +Beate-Uhse.TV:11758:h:0:27500:3839:3840:0:3:21 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 +:Sportsworld +Premiere Sport 1:11719:h:0:27500:255:256:0:3:17 +Premiere Sport 2:11719:h:0:27500:3327:3328:0:3:27 +Premiere Sport 3:11758:h:0:27500:2815:2816:0:3:18 +:Formel 1 +Infokanal:11720:h:0:27500:3071:3072:0:3:244 +Multikanal:11720:h:0:27500:2815:2816:0:3:243 +Supersignal:11720:h:0:27500:255:256:0:3:17 +Verfolgerfeld:11720:h:0:27500:2303:2304:0:3:241 +Cockpitkanal:11720:h:0:27500:2559:2560:0:3:242 +Boxengasse:11720:h:0:27500:2047:2048:0:3:240 +:Premiere World Bundesliga +Superdom:12070:h:0:27500:255:256:0:3:26 +Spiel 1:11719:h:0:27500:255:256,257:0:3:17 +Spiel 2:11719:h:0:27500:2047:2048,2049:0:3:240 +BuLi-Konferenz:12070:h:0:27500:3071:3072,3073:0:3:208 +BuLi-Spiel 1:12070:h:0:27500:3327:3328,3329:0:3:209 +BuLi-Spiel 2:12070:h:0:27500:2303:2304,2305:0:3:210 +BuLi-Spiel 3:12070:h:0:27500:3583:3584,3585:0:3:211 +BuLi-Spiel 4:12070:h:0:27500:2559:2560,2561:0:3:212 +BuLi-Spiel 5:12070:h:0:27500:2815:2816,2817:0:3:213 : TV Niepokalanow:11876:h:0:27500:305:321:0:0:20601 Mosaico:11934:v:0:27500:165:100:0:0:29010 @@ -164,7 +183,7 @@ 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 +MHP test:12604:h:0:22000:5632:0: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 @@ -173,3 +192,4 @@ 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 +MTV Central Europe:12699:v:0:22000:3031:3032:0:0:28643 diff --git a/config.c b/config.c index acf1996..8e0cce3 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.52 2001/07/27 13:45:28 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; @@ -200,7 +200,10 @@ cChannel::cChannel(const cChannel *Channel) diseqc = Channel ? Channel->diseqc : 0; srate = Channel ? Channel->srate : 27500; vpid = Channel ? Channel->vpid : 255; - apid = Channel ? Channel->apid : 256; + apid1 = Channel ? Channel->apid1 : 256; + apid2 = Channel ? Channel->apid2 : 0; + dpid1 = Channel ? Channel->dpid1 : 257; + dpid2 = Channel ? Channel->dpid2 : 0; tpid = Channel ? Channel->tpid : 32; ca = Channel ? Channel->ca : 0; pnr = Channel ? Channel->pnr : 0; @@ -218,8 +221,19 @@ const char *cChannel::ToText(cChannel *Channel) delete buffer; if (Channel->groupSep) asprintf(&buffer, ":%s\n", s); - else - asprintf(&buffer, "%s:%d:%c:%d:%d:%d:%d:%d:%d:%d\n", s, Channel->frequency, Channel->polarization, Channel->diseqc, Channel->srate, Channel->vpid, Channel->apid, Channel->tpid, Channel->ca, Channel->pnr); + else { + char apidbuf[32]; + char *q = apidbuf; + q += snprintf(q, sizeof(apidbuf), "%d", Channel->apid1); + if (Channel->apid2) + q += snprintf(q, sizeof(apidbuf) - (q - apidbuf), ",%d", Channel->apid2); + if (Channel->dpid1 || Channel->dpid2) + q += snprintf(q, sizeof(apidbuf) - (q - apidbuf), ";%d", Channel->dpid1); + if (Channel->dpid2) + q += snprintf(q, sizeof(apidbuf) - (q - apidbuf), ",%d", Channel->dpid2); + *q = 0; + asprintf(&buffer, "%s:%d:%c:%d:%d:%d:%s:%d:%d:%d\n", s, Channel->frequency, Channel->polarization, Channel->diseqc, Channel->srate, Channel->vpid, apidbuf, Channel->tpid, Channel->ca, Channel->pnr); + } return buffer; } @@ -242,8 +256,21 @@ bool cChannel::Parse(const char *s) } else { groupSep = false; - //XXX - int fields = sscanf(s, "%a[^:]:%d:%c:%d:%d:%d:%d:%d:%d:%d", &buffer, &frequency, &polarization, &diseqc, &srate, &vpid, &apid, &tpid, &ca, &pnr); + char *apidbuf = NULL; + int fields = sscanf(s, "%a[^:]:%d:%c:%d:%d:%d:%a[^:]:%d:%d:%d", &buffer, &frequency, &polarization, &diseqc, &srate, &vpid, &apidbuf, &tpid, &ca, &pnr); + apid1 = apid2 = 0; + dpid1 = dpid2 = 0; + if (apidbuf) { + char *p = strchr(apidbuf, ';'); + if (p) + *p++ = 0; + sscanf(apidbuf, "%d,%d", &apid1, &apid2); + if (p) + sscanf(p, "%d,%d", &dpid1, &dpid2); + delete apidbuf; + } + else + return false; if (fields >= 9) { if (fields == 9) { // allow reading of old format @@ -275,7 +302,7 @@ bool cChannel::Switch(cDvbApi *DvbApi, bool Log) isyslog(LOG_INFO, "switching to channel %d", number); } for (int i = 3; i--;) { - if (DvbApi->SetChannel(number, frequency, polarization, diseqc, srate, vpid, apid, tpid, ca, pnr)) + if (DvbApi->SetChannel(number, frequency, polarization, diseqc, srate, vpid, apid1, apid2, dpid1, dpid2, tpid, ca, pnr)) return true; esyslog(LOG_ERR, "retrying"); } @@ -305,8 +332,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 +357,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 +551,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,14 +761,22 @@ cSetup::cSetup(void) ShowInfoOnChSwitch = 1; MenuScrollPage = 1; MarkInstantRecord = 1; + LnbSLOF = 11700; LnbFrequLo = 9750; LnbFrequHi = 10600; + DiSEqC = 1; SetSystemTime = 0; MarginStart = 2; MarginStop = 10; EPGScanTimeout = 5; SVDRPTimeout = 300; PrimaryLimit = 0; + DefaultPriority = 50; + DefaultLifetime = 50; + VideoFormat = VIDEO_FORMAT_4_3; + ChannelInfoPos = 0; + OSDwidth = 52; + OSDheight = 18; CurrentChannel = -1; } @@ -756,14 +791,22 @@ 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, "DiSEqC")) DiSEqC = atoi(Value); else if (!strcasecmp(Name, "SetSystemTime")) SetSystemTime = atoi(Value); else if (!strcasecmp(Name, "MarginStart")) MarginStart = atoi(Value); else if (!strcasecmp(Name, "MarginStop")) MarginStop = atoi(Value); else if (!strcasecmp(Name, "EPGScanTimeout")) EPGScanTimeout = atoi(Value); else if (!strcasecmp(Name, "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, "VideoFormat")) VideoFormat = atoi(Value); + else if (!strcasecmp(Name, "ChannelInfoPos")) ChannelInfoPos = atoi(Value); + else if (!strcasecmp(Name, "OSDwidth")) OSDwidth = atoi(Value); + else if (!strcasecmp(Name, "OSDheight")) OSDheight = atoi(Value); else if (!strcasecmp(Name, "CurrentChannel")) CurrentChannel = atoi(Value); else return false; @@ -813,14 +856,22 @@ 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, "DiSEqC = %d\n", DiSEqC); fprintf(f, "SetSystemTime = %d\n", SetSystemTime); fprintf(f, "MarginStart = %d\n", MarginStart); fprintf(f, "MarginStop = %d\n", MarginStop); 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, "VideoFormat = %d\n", VideoFormat); + fprintf(f, "ChannelInfoPos = %d\n", ChannelInfoPos); + fprintf(f, "OSDwidth = %d\n", OSDwidth); + fprintf(f, "OSDheight = %d\n", OSDheight); 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 6c21fb2..58d0382 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.55 2001/07/27 13:32:53 kls Exp $ */ #ifndef __CONFIG_H @@ -19,10 +19,18 @@ #include "eit.h" #include "tools.h" -#define VDRVERSION "0.72" +#define VDRVERSION "0.85" #define MaxBuffer 10000 +#define MAXPRIORITY 99 +#define MAXLIFETIME 99 + +#define MINOSDWIDTH 40 +#define MAXOSDWIDTH 56 +#define MINOSDHEIGHT 12 +#define MAXOSDHEIGHT 21 + enum eKeys { // "Up" and "Down" must be the first two keys! kUp, kDown, @@ -92,7 +100,8 @@ public: int diseqc; int srate; int vpid; - int apid; + int apid1, apid2; + int dpid1, dpid2; int tpid; int ca; int pnr; @@ -106,9 +115,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 +246,7 @@ public: bool SwitchTo(int Number, cDvbApi *DvbApi = NULL); int MaxNumber(void) { return maxNumber; } }; - + class cTimers : public cConfig { public: cTimer *GetTimer(cTimer *Timer); @@ -266,13 +272,19 @@ public: int ShowInfoOnChSwitch; int MenuScrollPage; int MarkInstantRecord; + int LnbSLOF; int LnbFrequLo; int LnbFrequHi; + int DiSEqC; int SetSystemTime; int MarginStart, MarginStop; int EPGScanTimeout; int SVDRPTimeout; int PrimaryLimit; + int DefaultPriority, DefaultLifetime; + int VideoFormat; + int ChannelInfoPos; + int OSDwidth, OSDheight; int CurrentChannel; cSetup(void); bool Load(const char *FileName); diff --git a/dvbapi.c b/dvbapi.c index a929b50..07b6f61 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.96 2001/07/29 10:32:50 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 (192*1024) + #define FRAMESPERSEC 25 // The maximum file size is limited by the range that can be covered @@ -55,16 +66,13 @@ extern "C" { // The number of frames to back up when resuming an interrupted replay session: #define RESUMEBACKUP (10 * FRAMESPERSEC) -typedef unsigned char uchar; +// The maximum time we wait before assuming that a recorded video data stream +// is broken: +#define MAXBROKENTIMEOUT 30 // seconds -static void SetPlayMode(int VideoDev, int Mode) -{ - if (VideoDev >= 0) { - struct video_play_mode pmode; - pmode.mode = Mode; - ioctl(VideoDev, VIDIOCSPLAYMODE, &pmode); - } -} +#define CHECK(s) { if ((s) < 0) LOG_ERROR; } // used for 'ioctl()' calls + +typedef unsigned char uchar; const char *IndexToHMSF(int Index, bool WithFrame) { @@ -100,6 +108,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 +167,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 +326,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 +334,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 +346,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 +372,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 +439,7 @@ int cFileName::NextFile(void) class cRecordBuffer : public cRingBuffer { private: + cDvbApi *dvbApi; cFileName fileName; cIndexFile *index; cRemux remux; @@ -670,18 +455,19 @@ protected: virtual void Input(void); virtual void Output(void); public: - cRecordBuffer(int *InFile, const char *FileName); + cRecordBuffer(cDvbApi *DvbApi, const char *FileName, int VPid, int APid1, int APid2, int DPid1, int DPid2); virtual ~cRecordBuffer(); }; -cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName) -:cRingBuffer(VIDEOBUFSIZE) +cRecordBuffer::cRecordBuffer(cDvbApi *DvbApi, const char *FileName, int VPid, int APid1, int APid2, int DPid1, int DPid2) +:cRingBuffer(VIDEOBUFSIZE, true) ,fileName(FileName, true) +,remux(VPid, APid1, APid2, DPid1, DPid2, true) { + dvbApi = DvbApi; index = NULL; pictureType = NO_PICTURE; fileSize = 0; - videoDev = *InFile; recordFile = fileName.Open(); recording = false; lastDiskSpaceCheck = time(NULL); @@ -692,12 +478,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 +534,19 @@ void cRecordBuffer::Input(void) else if (r < 0) { if (errno != EAGAIN) { LOG_ERROR; - break; + if (errno != EBUFFEROVERFLOW) + break; } } - else if (time(NULL) - t > 5) { + if (time(NULL) - t > MAXBROKENTIMEOUT) { esyslog(LOG_ERR, "ERROR: video data stream broken"); + cThread::EmergencyExit(true); t = time(NULL); } cFile::FileReady(videoDev, 100); if (!recording) break; } - SetPlayMode(videoDev, VID_PLAY_RESET); dsyslog(LOG_INFO, "input thread ended (pid=%d)", getpid()); } @@ -766,15 +555,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,166 +591,353 @@ void cRecordBuffer::Output(void) if (!recording) break; } + else + usleep(1); // this keeps the CPU load low } recording = false; dsyslog(LOG_INFO, "output thread ended (pid=%d)", getpid()); } +// --- ReadFrame ------------------------------------------------------------- + +int ReadFrame(int f, uchar *b, int Length, int Max) +{ + if (Length == -1) + Length = Max; // this means we read up to EOF (see cIndex) + else if (Length > Max) { + esyslog(LOG_ERR, "ERROR: frame larger than buffer (%d > %d)", Length, Max); + Length = Max; + } + int r = read(f, b, Length); + if (r < 0) + LOG_ERROR; + return r; +} + // --- 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; + FILE *dolbyDev; int replayFile; - eReplayMode mode; - int lastIndex, stillIndex; - int brakeCounter; - eReplayCmd command; - bool active; + bool eof; + int blockInput, blockOutput; + bool paused, fastForward, fastRewind; + int lastIndex, stillIndex, playIndex; + bool canToggleAudioTrack; + uchar audioTrack; 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); + void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00); + void DisplayFrame(uchar *b, int Length); + int Resume(void); + bool Save(void); protected: - virtual void Action(void); + virtual void Input(void); + virtual void Output(void); public: - cReplayBuffer(int *OutFile, const char *FileName); + cReplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, const char *FileName); virtual ~cReplayBuffer(); - virtual int Read(int Max = -1); - virtual int Write(int Max = -1); - void SetMode(eReplayMode Mode); - int Resume(void); - bool Save(void); - void Pause(void) { SetCmd(rcPause); } - void Play(void) { SetCmd(rcPlay); } - void Forward(void) { SetCmd(rcForward); } - void Backward(void) { SetCmd(rcBackward); } + void Pause(void); + void Play(void); + void Forward(void); + void Backward(void); int SkipFrames(int Frames); void SkipSeconds(int Seconds); void Goto(int Position, bool Still = false); void GetIndex(int &Current, int &Total, bool SnapToIFrame = false); + bool CanToggleAudioTrack(void) { return canToggleAudioTrack; } + void ToggleAudioTrack(void); }; -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; + dolbyDev = NULL; + if (cDvbApi::AudioCommand()) { + dolbyDev = popen(cDvbApi::AudioCommand(), "w"); + if (!dolbyDev) + esyslog(LOG_ERR, "ERROR: can't open pipe to audio command '%s'", cDvbApi::AudioCommand()); + } replayFile = fileName.Open(); - mode = rmPlay; - brakeCounter = 0; - command = rcNone; - lastIndex = stillIndex = -1; - active = false; + eof = false; + blockInput = blockOutput = false; + paused = fastForward = fastRewind = false; + lastIndex = stillIndex = playIndex = -1; + canToggleAudioTrack = false; + audioTrack = 0xC0; 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); + if (dolbyDev) + pclose(dolbyDev); + 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; + playIndex = -1; + r = ReadFrame(replayFile, b, Length, sizeof(b)); + StripAudioPackets(b, r); + } + else if (index) { + lastIndex = -1; + playIndex = (playIndex >= 0) ? playIndex + 1 : index->Get(fileName.Number(), fileOffset); + uchar FileNumber; + int FileOffset, Length; + if (!(index->Get(playIndex, &FileNumber, &FileOffset, NULL, &Length) && NextFile(FileNumber, FileOffset))) + break; + r = ReadFrame(replayFile, b, Length, sizeof(b)); + StripAudioPackets(b, r, audioTrack); + } + else // allows replay even if the index file is missing + 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; + else + usleep(1); // this keeps the CPU load low + 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) { + p += w; + r -= w; + fileOffset += w; + } + else if (w < 0 && errno != EAGAIN) { + LOG_ERROR; + Stop(); + return; + } + } + } + else + usleep(1); // this keeps the CPU load low + if (blockOutput > 1) + blockOutput = 1; + } + + dsyslog(LOG_INFO, "output thread ended (pid=%d)", getpid()); +} + +void cReplayBuffer::StripAudioPackets(uchar *b, int Length, uchar Except) +{ + for (int i = 0; i < Length - 6; i++) { + if (b[i] == 0x00 && b[i + 1] == 0x00 && b[i + 2] == 0x01) { + uchar c = b[i + 3]; + int l = b[i + 4] * 256 + b[i + 5] + 6; + switch (c) { + case 0xBD: // dolby + if (Except && dolbyDev) { + int written = b[i + 8] + 9; // skips the PES header + int n = l - written; + while (n > 0) { + int w = fwrite(&b[i + written], 1, n, dolbyDev); + if (w < 0) { + LOG_ERROR; + break; + } + n -= w; + written += w; + } + } + // continue with deleting the data - otherwise it disturbs DVB replay + case 0xC0 ... 0xC1: // audio + if (c == 0xC1) + canToggleAudioTrack = true; + if (!Except || c != Except) { + int n = l; + for (int j = i; j < Length && n--; j++) + b[j] = 0x00; + } + break; + case 0xE0 ... 0xEF: // video + break; + default: + //esyslog(LOG_ERR, "ERROR: unexpected packet id %02X", c); + l = 0; + } + if (l) + i += l - 1; // the loop increments, too! + } + /*XXX + else + esyslog(LOG_ERR, "ERROR: broken packet header"); + XXX*/ + } +} + +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(); + playIndex = -1; + 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 +949,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 +983,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 +994,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 +1011,41 @@ 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 (paused) + CHECK(ioctl(videoDev, VIDEO_CONTINUE)); + 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; + playIndex = -1; + uchar b[MAXFRAMESIZE]; + int r = ReadFrame(replayFile, b, Length, sizeof(b)); + if (r > 0) + DisplayFrame(b, r); + fileOffset += Length; + paused = true; + } + else + stillIndex = playIndex = -1; + Clear(false); + } } void cReplayBuffer::GetIndex(int &Current, int &Total, bool SnapToIFrame) { if (index) { - - LOCK_THREAD; - if (stillIndex >= 0) Current = stillIndex; else { @@ -1099,178 +1065,145 @@ 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) +void cReplayBuffer::ToggleAudioTrack(void) { - 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 + if (CanToggleAudioTrack()) { + audioTrack = (audioTrack == 0xC0) ? 0xC1 : 0xC0; + Clear(); } - return Written; } // --- cTransferBuffer ------------------------------------------------------- -class cTransferBuffer : public cThread { +class cTransferBuffer : public cRingBuffer { private: - bool active; + cDvbApi *dvbApi; int fromDevice, toDevice; + bool gotBufferReserve; + 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, int VPid, int APid); virtual ~cTransferBuffer(); + void SetAudioPid(int APid); }; -cTransferBuffer::cTransferBuffer(int FromDevice, int ToDevice) +cTransferBuffer::cTransferBuffer(cDvbApi *DvbApi, int ToDevice, int VPid, int APid) +:cRingBuffer(VIDEOBUFSIZE, true) +,remux(VPid, APid, 0, 0, 0) { - fromDevice = FromDevice; + dvbApi = DvbApi; + fromDevice = dvbApi->SetModeRecord(); toDevice = ToDevice; - active = false; + gotBufferReserve = 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::SetAudioPid(int APid) { - dsyslog(LOG_INFO, "data transfer thread started (pid=%d)", getpid()); + Clear(); + //XXX we may need to have access to the audio device, too, in order to clear it + CHECK(ioctl(toDevice, VIDEO_CLEAR_BUFFER)); + gotBufferReserve = false; + remux.SetAudioPid(APid); +} + +void cTransferBuffer::Input(void) +{ + 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()); + + uchar b[MINVIDEODATA]; + while (Busy()) { + if (!gotBufferReserve) { + if (Available() < MAXFRAMESIZE) { + usleep(100000); // allow the buffer to collect some reserve + continue; + } + 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) { + p += w; + r -= w; + } + else if (w < 0 && errno != EAGAIN) { + LOG_ERROR; + Stop(); + return; + } + } + } + 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 +1218,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 +1261,24 @@ 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) { + Length = ReadFrame(fromFile, buffer, Length, sizeof(buffer)); + if (Length < 0) + break; + } else break; } @@ -1362,7 +1296,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 +1358,32 @@ 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; +char *cDvbApi::audioCommand = NULL; -cDvbApi::cDvbApi(const char *VideoFileName, const char *VbiFileName) +cDvbApi::cDvbApi(int n) { + vPid = aPid1 = aPid2 = dPid1 = dPid2 = 0; siProcessor = NULL; recordBuffer = NULL; replayBuffer = NULL; @@ -1438,21 +1391,49 @@ 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_demuxa1 = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true); + fd_demuxa2 = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true); + fd_demuxd1 = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true); + fd_demuxd2 = 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; + + // Video format: + + SetVideoFormat(Setup.VideoFormat ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3); + + // 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_demuxa1 >= 0 && fd_demuxa2 >= 0 && fd_demuxd1 >= 0 && fd_demuxd2 >= 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 +1461,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 +1534,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 +1590,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 +1687,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 +1730,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 +1778,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 +1797,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 +1820,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; @@ -1867,30 +1853,11 @@ 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; - dc.cmd = cmd; - dc.color = color; - dc.x0 = x0; - dc.y0 = y0; - dc.x1 = x1; - dc.y1 = y1; - dc.data = (void *)data; - ioctl(videoDev, VIDIOCSOSDCOMMAND, &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. - // TODO Check if this is still necessary with driver versions after 0.6. - } -} #endif void cDvbApi::Open(int w, int h) { - int d = (h < 0) ? MenuLines + h : 0; + int d = (h < 0) ? Setup.OSDheight + h : 0; h = abs(h); cols = w; rows = h; @@ -1899,16 +1866,7 @@ void cDvbApi::Open(int w, int h) syncok(window, true); #define B2C(b) (((b) * 1000) / 255) #define SETCOLOR(n, r, g, b, o) init_color(n, B2C(r), B2C(g), B2C(b)) -#else - w *= charWidth; - h *= lineHeight; - 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); - #define SETCOLOR(n, r, g, b, o) Cmd(OSD_SetColor, n, r, g, b, o) - SETCOLOR(clrTransparent, 0x00, 0x00, 0x00, 0); -#endif + //XXX SETCOLOR(clrBackground, 0x00, 0x00, 0x00, 127); // background 50% gray SETCOLOR(clrBlack, 0x00, 0x00, 0x00, 255); SETCOLOR(clrRed, 0xFC, 0x14, 0x14, 255); @@ -1918,6 +1876,35 @@ void cDvbApi::Open(int w, int h) SETCOLOR(clrCyan, 0x00, 0xFC, 0xFC, 255); SETCOLOR(clrMagenta, 0xB0, 0x00, 0xFC, 255); SETCOLOR(clrWhite, 0xFC, 0xFC, 0xFC, 255); +#else + w *= charWidth; + h *= lineHeight; + d *= lineHeight; + int x = (720 - (Setup.OSDwidth - 1) * charWidth) / 2; //TODO PAL vs. NTSC??? + int y = (576 - Setup.OSDheight * lineHeight) / 2 + d; + //XXX + osd = new cDvbOsd(fd_osd, x, y); + //XXX TODO this should be transferred to the places where the individual windows are requested (there's too much detailed knowledge here!) + if (h / lineHeight == 5) { //XXX channel display + osd->Create(0, 0, w, h, 4); + } + else if (h / lineHeight == 1) { //XXX info display + osd->Create(0, 0, w, h, 4); + } + else if (d == 0) { //XXX full menu + osd->Create(0, 0, w, lineHeight, 2); + osd->Create(0, lineHeight, w, (Setup.OSDheight - 3) * lineHeight, 2, true, clrBackground, clrCyan, clrWhite, clrBlack); + osd->Create(0, (Setup.OSDheight - 2) * lineHeight, w, 2 * lineHeight, 4); + } + else { //XXX progress display + /*XXX + osd->Create(0, 0, w, lineHeight, 1); + osd->Create(0, lineHeight, w, lineHeight, 2, false); + osd->Create(0, 2 * lineHeight, w, lineHeight, 1); + XXX*///XXX some pixels are not drawn correctly with lower bpp values + osd->Create(0, 0, w, 3*lineHeight, 4); + } +#endif } void cDvbApi::Close(void) @@ -2037,60 +2024,270 @@ void cDvbApi::Flush(void) #endif } -bool cDvbApi::SetChannel(int ChannelNumber, int FrequencyMHz, char Polarization, int Diseqc, int Srate, int Vpid, int Apid, int Tpid, int Ca, int Pnr) +int cDvbApi::SetModeRecord(void) { - 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); - unsigned int freq = FrequencyMHz; - if (front.type == FRONT_DVBS) { - front.ttk = (freq < 11700UL) ? 0 : 1; - if (freq < 11700UL) { + // 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); + } + } +} + +void cDvbApi::SetVideoFormat(videoFormat_t Format) +{ + if (fd_video) + CHECK(ioctl(fd_video, VIDEO_SET_FORMAT, Format)); +} + +bool cDvbApi::SetPid(int fd, dmxPesType_t PesType, int Pid, dmxOutput_t Output) +{ + if (Pid) { + CHECK(ioctl(fd, DMX_STOP)); + dmxPesFilterParams pesFilterParams; + 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) { + if (Pid != 0x1FFF) + LOG_ERROR; + return false; + } + } + return true; +} + +bool cDvbApi::SetPids(bool ForRecording) +{ + return SetVpid(vPid, ForRecording ? DMX_OUT_TS_TAP : DMX_OUT_DECODER) && + SetApid1(aPid1, ForRecording ? DMX_OUT_TS_TAP : DMX_OUT_DECODER) && + SetApid2(ForRecording ? aPid2 : 0, DMX_OUT_TS_TAP) && + SetDpid1(ForRecording ? dPid1 : 0, DMX_OUT_TS_TAP) && + SetDpid2(ForRecording ? dPid2 : 0, DMX_OUT_TS_TAP); +} + +bool cDvbApi::SetChannel(int ChannelNumber, int FrequencyMHz, char Polarization, int Diseqc, int Srate, int Vpid, int Apid1, int Apid2, int Dpid1, int Dpid2, int Tpid, int Ca, int Pnr) +{ + // Make sure the siProcessor won't access the device while switching + cThreadLock ThreadLock(siProcessor); + + StopTransfer(); + StopReplay(); + + // Must set this anyway to avoid getting stuck when switching through + // channels with 'Up' and 'Down' keys: + currentChannel = ChannelNumber; + vPid = Vpid; + aPid1 = Apid1; + aPid2 = Apid2; + dPid1 = Dpid1; + dPid2 = Dpid2; + + // 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)); + } + + // If this card can't receive this channel, we must not actually switch + // the channel here, because that would irritate the driver when we + // start replaying in Transfer Mode immediately after switching the channel: + bool NeedsTransferMode = (this == PrimaryDvbApi && Ca && Ca != Index() + 1); + + if (!NeedsTransferMode) { + + // Turn off current PIDs: + + SetVpid( 0x1FFF, DMX_OUT_DECODER); + SetApid1(0x1FFF, DMX_OUT_DECODER); + SetApid2(0x1FFF, DMX_OUT_DECODER); + SetDpid1(0x1FFF, DMX_OUT_DECODER); + SetDpid2(0x1FFF, DMX_OUT_DECODER); + SetTpid( 0x1FFF, DMX_OUT_DECODER); + + bool ChannelSynced = false; + + if (fd_qpskfe >= 0 && fd_sec >= 0) { // DVB-S + + // Frequency offsets: + + unsigned int freq = FrequencyMHz; + int tone = SEC_TONE_OFF; + + if (freq < (unsigned int)Setup.LnbSLOF) { freq -= Setup.LnbFrequLo; - front.ttk = 0; + tone = SEC_TONE_OFF; } else { freq -= Setup.LnbFrequHi; - front.ttk = 1; + 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 = Setup.DiSEqC ? 1 : 0; + scmds.commands = &scmd; + + CHECK(ioctl(fd_sec, SEC_SEND_SEQUENCE, &scmds)); + + // Tuning: + + CHECK(ioctl(fd_qpskfe, QPSK_TUNE, &qpsk)); + + // Wait for channel sync: + + if (cFile::FileReady(fd_qpskfe, 5000)) { + qpskEvent event; + int res = ioctl(fd_qpskfe, QPSK_GET_EVENT, &event); + if (res >= 0) + ChannelSynced = event.type == FE_COMPLETION_EV; + else + esyslog(LOG_ERR, "ERROR %d in qpsk get event", res); } + else + esyslog(LOG_ERR, "ERROR: timeout while tuning\n"); } - 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 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: + + if (cFile::FileReady(fd_qamfe, 5000)) { + qamEvent event; + int res = ioctl(fd_qamfe, QAM_GET_EVENT, &event); + if (res >= 0) + ChannelSynced = event.type == FE_COMPLETION_EV; + else + esyslog(LOG_ERR, "ERROR %d in qam get event", res); + } + else + esyslog(LOG_ERR, "ERROR: timeout while tuning\n"); + } + 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); + if (this == PrimaryDvbApi) + cThread::RaisePanic(); + return false; + } + + // PID settings: + + 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); + + // 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 (NeedsTransferMode) { + cDvbApi *CaDvbApi = GetDvbApi(Ca, 0); + if (CaDvbApi) { + if (!CaDvbApi->Recording()) { + if (CaDvbApi->SetChannel(ChannelNumber, FrequencyMHz, Polarization, Diseqc, Srate, Vpid, Apid1, Apid2, Dpid1, Dpid2, 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 +2298,7 @@ bool cDvbApi::Transferring(void) cDvbApi *cDvbApi::StartTransfer(int TransferToVideoDev) { StopTransfer(); - transferBuffer = new cTransferBuffer(videoDev, TransferToVideoDev); + transferBuffer = new cTransferBuffer(this, TransferToVideoDev, vPid, aPid1); return this; } @@ -2110,7 +2307,6 @@ void cDvbApi::StopTransfer(void) if (transferBuffer) { delete transferBuffer; transferBuffer = NULL; - SetPlayMode(videoDev, VID_PLAY_RESET); } if (transferringFromDvbApi) { transferringFromDvbApi->StopTransfer(); @@ -2143,37 +2339,36 @@ bool cDvbApi::StartRecord(const char *FileName, int Ca, int Priority) esyslog(LOG_ERR, "ERROR: StartRecord() called while recording - ignored!"); return false; } - if (videoDev >= 0) { - StopTransfer(); + StopTransfer(); - StopReplay(); // TODO: remove this if the driver is able to do record and replay at the same time + StopReplay(); // TODO: remove this if the driver is able to do record and replay at the same time - // Check FileName: + // Check FileName: - if (!FileName) { - esyslog(LOG_ERR, "ERROR: StartRecord: file name is (null)"); - return false; - } - isyslog(LOG_INFO, "record %s", FileName); + if (!FileName) { + esyslog(LOG_ERR, "ERROR: StartRecord: file name is (null)"); + return false; + } + isyslog(LOG_INFO, "record %s", FileName); - // Create directories if necessary: + // Create directories if necessary: - if (!MakeDirs(FileName, true)) - return false; + if (!MakeDirs(FileName, true)) + return false; - // Create recording buffer: + // Create recording buffer: - recordBuffer = new cRecordBuffer(&videoDev, FileName); + recordBuffer = new cRecordBuffer(this, FileName, vPid, aPid1, aPid2, dPid1, dPid2); - if (recordBuffer) { - ca = Ca; - priority = Priority; - return true; - } - else - esyslog(LOG_ERR, "ERROR: can't allocate recording buffer"); + if (recordBuffer) { + ca = Ca; + priority = Priority; + return true; } + else + esyslog(LOG_ERR, "ERROR: can't allocate recording buffer"); + return false; } @@ -2195,7 +2390,7 @@ bool cDvbApi::StartReplay(const char *FileName) } StopTransfer(); StopReplay(); - if (videoDev >= 0) { + if (fd_video >= 0 && fd_audio >= 0) { // Check FileName: @@ -2207,7 +2402,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 @@ -2221,6 +2416,12 @@ void cDvbApi::StopReplay(void) if (replayBuffer) { delete replayBuffer; replayBuffer = NULL; + if (this == PrimaryDvbApi) { + // let's explicitly switch the channel back in case it was in Transfer Mode: + cChannel *Channel = Channels.GetByNumber(currentChannel); + if (Channel) + Channel->Switch(this, false); + } } } @@ -2276,6 +2477,38 @@ void cDvbApi::Goto(int Position, bool Still) replayBuffer->Goto(Position, Still); } +bool cDvbApi::CanToggleAudioTrack(void) +{ + return replayBuffer ? replayBuffer->CanToggleAudioTrack() : (aPid1 && aPid2 && aPid1 != aPid2); +} + +bool cDvbApi::ToggleAudioTrack(void) +{ + if (replayBuffer) { + replayBuffer->ToggleAudioTrack(); + return true; + } + else { + int a = aPid2; + aPid2 = aPid1; + aPid1 = a; + if (transferringFromDvbApi) + return transferringFromDvbApi->ToggleAudioTrack(); + else { + if (transferBuffer) + transferBuffer->SetAudioPid(aPid1); + return SetPids(transferBuffer != NULL); + } + } + return false; +} + +void cDvbApi::SetAudioCommand(const char *Command) +{ + delete audioCommand; + audioCommand = strdup(Command); +} + // --- cEITScanner ----------------------------------------------------------- cEITScanner::cEITScanner(void) @@ -2318,7 +2551,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, MAXPRIORITY); 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 de64612..2203d79 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.42 2001/07/27 11:40:38 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,9 +36,6 @@ typedef struct CRect { signed short x, y, width, height; }; -#define MenuLines 15 -#define MenuColumns 40 - const char *IndexToHMSF(int Index, bool WithFrame = false); // Converts the given index to a string, optionally containing the frame number. int HMSFToIndex(const char *HMSF); @@ -55,9 +58,22 @@ 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_demuxa1, fd_demuxa2, fd_demuxd1, fd_demuxd2, fd_demuxv, fd_demuxt; + int vPid, aPid1, aPid2, dPid1, dPid2; + bool SetPid(int fd, dmxPesType_t PesType, int Pid, dmxOutput_t Output); + bool SetVpid(int Vpid, dmxOutput_t Output) { return SetPid(fd_demuxv, DMX_PES_VIDEO, Vpid, Output); } + bool SetApid1(int Apid, dmxOutput_t Output) { return SetPid(fd_demuxa1, DMX_PES_AUDIO, Apid, Output); } + bool SetApid2(int Apid, dmxOutput_t Output) { return SetPid(fd_demuxa2, DMX_PES_OTHER, Apid, Output); } + bool SetDpid1(int Dpid, dmxOutput_t Output) { return SetPid(fd_demuxd1, DMX_PES_OTHER, Dpid, Output); } + bool SetDpid2(int Dpid, dmxOutput_t Output) { return SetPid(fd_demuxd2, DMX_PES_OTHER, Dpid, 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. @@ -138,7 +156,6 @@ private: cDvbOsd *osd; #endif int cols, rows; - void Cmd(OSD_Command cmd, int color = 0, int x0 = 0, int y0 = 0, int x1 = 0, int y1 = 0, const void *data = NULL); public: void Open(int w, int h); void Close(void); @@ -154,12 +171,16 @@ public: void Text(int x, int y, const char *s, eDvbColor colorFg = clrWhite, eDvbColor colorBg = clrBackground); void Flush(void); + // Video format facilities: + + void SetVideoFormat(videoFormat_t Format); + // Channel facilities private: int currentChannel; public: - bool SetChannel(int ChannelNumber, int FrequencyMHz, char Polarization, int Diseqc, int Srate, int Vpid, int Apid, int Tpid, int Ca, int Pnr); + bool SetChannel(int ChannelNumber, int FrequencyMHz, char Polarization, int Diseqc, int Srate, int Vpid, int Apid1, int Apid2, int Dpid1, int Dpid2, int Tpid, int Ca, int Pnr); static int CurrentChannel(void) { return PrimaryDvbApi ? PrimaryDvbApi->currentChannel : 0; } int Channel(void) { return currentChannel; } @@ -184,12 +205,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 +262,24 @@ 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. + + // Audio track facilities + +public: + bool CanToggleAudioTrack(void); + // Returns true if we are currently replaying and this recording has two + // audio tracks, or if the current channel has two audio PIDs. + bool ToggleAudioTrack(void); + // Toggles the audio track if possible. + + // Dolby Digital audio facilities + +private: + static char *audioCommand; +public: + static void SetAudioCommand(const char *Command); + static const char *AudioCommand(void) { return audioCommand; } }; class cEITScanner { diff --git a/dvbosd.c b/dvbosd.c index 395ad32..90ac067 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.10 2001/07/24 16:25:34 kls Exp $ */ #include "dvbosd.h" @@ -14,12 +14,83 @@ #include #include "tools.h" +// --- cPalette -------------------------------------------------------------- + +cPalette::cPalette(int Bpp) +{ + maxColors = 1 << Bpp; + numColors = 0; + full = false; +} + +int cPalette::Index(eDvbColor Color) +{ + for (int i = 0; i < numColors; i++) { + if (color[i] == Color) { + used[i] = true; + return i; + } + } + if (!full) { + if (numColors < maxColors) { + color[numColors++] = Color; + used[numColors - 1] = true; + fetched[numColors - 1] = false; + return numColors - 1; + } + for (int i = maxColors; --i >= 0; ) { + if (!used[i]) { + color[i] = Color; + used[i] = true; + fetched[i] = false; + return i; + } + } + esyslog(LOG_ERR, "ERROR: too many different colors used in palette"); + full = true; + } + return 0; +} + +void cPalette::Reset(void) +{ + for (int i = 0; i < numColors; i++) + used[i] = false; + full = false; +} + +const eDvbColor *cPalette::Colors(int &FirstColor, int &LastColor) +{ + for (FirstColor = 0; FirstColor < numColors; FirstColor++) { + if (!fetched[FirstColor]) { + for (LastColor = FirstColor; LastColor < numColors && !fetched[LastColor]; LastColor++) + fetched[LastColor] = true; + LastColor--; // the loop ended one past the last one! + return &color[FirstColor]; + } + } + return NULL; +} + +void cPalette::Take(const cPalette &Palette, tIndexes *Indexes) +{ + for (int i = 0; i < Palette.numColors; i++) { + if (Palette.used[i]) { + int n = Index(Palette.color[i]); + if (Indexes) + (*Indexes)[i] = n; + } + } +} + // --- cBitmap --------------------------------------------------------------- -cBitmap::cBitmap(int Width, int Height) +cBitmap::cBitmap(int Width, int Height, int Bpp, bool ClearWithBackground) +:cPalette(Bpp) { width = Width; height = Height; + clearWithBackground = ClearWithBackground; bitmap = NULL; fontType = fontOsd; font = NULL; @@ -27,7 +98,7 @@ cBitmap::cBitmap(int Width, int Height) bitmap = new char[width * height]; if (bitmap) { Clean(); - memset(bitmap, clrTransparent, width * height); + memset(bitmap, 0x00, width * height); SetFont(fontOsd); } else @@ -54,9 +125,38 @@ eDvbFont cBitmap::SetFont(eDvbFont Font) return oldFont; } -bool cBitmap::Dirty(void) +bool cBitmap::Dirty(int &x1, int &y1, int &x2, int &y2) { - return dirtyX2 >= 0; + if (dirtyX2 >= 0) { + //XXX Workaround: apparently the bitmap sent to the driver always has to be a multiple + //XXX of 8 bits wide, and (dx * dy) also has to be a multiple of 8. + //TODO Fix driver (should be able to handle any size bitmaps!) + while ((dirtyX1 > 0 || dirtyX2 < width - 1) && ((dirtyX2 - dirtyX1) & 7) != 7) { + if (dirtyX2 < width - 1) + dirtyX2++; + else if (dirtyX1 > 0) + dirtyX1--; + } + //XXX "... / 2" <==> Bpp??? + while ((dirtyY1 > 0 || dirtyY2 < height - 1) && (((dirtyX2 - dirtyX1 + 1) * (dirtyY2 - dirtyY1 + 1) / 2) & 7) != 0) { + if (dirtyY2 < height - 1) + dirtyY2++; + else if (dirtyY1 > 0) + dirtyY1--; + } + while ((dirtyX1 > 0 || dirtyX2 < width - 1) && (((dirtyX2 - dirtyX1 + 1) * (dirtyY2 - dirtyY1 + 1) / 2) & 7) != 0) { + if (dirtyX2 < width - 1) + dirtyX2++; + else if (dirtyX1 > 0) + dirtyX1--; + } + x1 = dirtyX1; + y1 = dirtyY1; + x2 = dirtyX2; + y2 = dirtyY2; + return true; + } + return false; } void cBitmap::Clean(void) @@ -67,12 +167,12 @@ void cBitmap::Clean(void) dirtyY2 = -1; } -void cBitmap::SetPixel(int x, int y, eDvbColor Color) +void cBitmap::SetIndex(int x, int y, char Index) { if (bitmap) { if (0 <= x && x < width && 0 <= y && y < height) { - if (bitmap[width * y + x] != Color) { - bitmap[width * y + x] = Color; + if (bitmap[width * y + x] != Index) { + bitmap[width * y + x] = Index; if (dirtyX1 > x) dirtyX1 = x; if (dirtyY1 > y) dirtyY1 = y; if (dirtyX2 < x) dirtyX2 = x; @@ -82,12 +182,19 @@ void cBitmap::SetPixel(int x, int y, eDvbColor Color) } } +void cBitmap::SetPixel(int x, int y, eDvbColor Color) +{ + SetIndex(x, y, Index(Color)); +} + void cBitmap::SetBitmap(int x, int y, const cBitmap &Bitmap) { if (bitmap && Bitmap.bitmap) { + tIndexes Indexes; + Take(Bitmap, &Indexes); for (int ix = 0; ix < Bitmap.width; ix++) { for (int iy = 0; iy < Bitmap.height; iy++) - SetPixel(x + ix, y + iy, eDvbColor(Bitmap.bitmap[Bitmap.width * iy + ix])); + SetIndex(x + ix, y + iy, Indexes[Bitmap.bitmap[Bitmap.width * iy + ix]]); } } } @@ -105,6 +212,8 @@ int cBitmap::Width(const char *s) void cBitmap::Text(int x, int y, const char *s, eDvbColor ColorFg, eDvbColor ColorBg) { if (bitmap) { + char fg = Index(ColorFg); + char bg = Index(ColorBg); int h = font->Height(s); while (s && *s) { const cFont::tCharData *CharData = font->CharData(*s++); @@ -113,7 +222,7 @@ void cBitmap::Text(int x, int y, const char *s, eDvbColor ColorFg, eDvbColor Col for (int row = 0; row < h; row++) { cFont::tPixelData PixelData = CharData->lines[row]; for (int col = CharData->width; col-- > 0; ) { - SetPixel(x + col, y + row, (PixelData & 1) ? ColorFg : ColorBg); + SetIndex(x + col, y + row, (PixelData & 1) ? fg : bg); PixelData >>= 1; } } @@ -125,39 +234,109 @@ void cBitmap::Text(int x, int y, const char *s, eDvbColor ColorFg, eDvbColor Col void cBitmap::Fill(int x1, int y1, int x2, int y2, eDvbColor Color) { if (bitmap) { + char c = Index(Color); for (int y = y1; y <= y2; y++) for (int x = x1; x <= x2; x++) - SetPixel(x, y, Color); + SetIndex(x, y, c); } } void cBitmap::Clear(void) { - Fill(0, 0, width - 1, height - 1, clrBackground); + Reset(); + if (clearWithBackground) + Fill(0, 0, width - 1, height - 1, clrBackground); +} + +const char *cBitmap::Data(int x, int y) +{ + return &bitmap[y * width + x]; +} + +// --- cWindow --------------------------------------------------------------- + +class cWindow : public cBitmap { +private: + int x0, y0; + bool shown; +public: + cWindow(int x, int y, int w, int h, int Bpp, bool ClearWithBackground = true); + int X0(void) { return x0; } + int Y0(void) { return y0; } + bool Shown(void) { bool s = shown; shown = true; return s; } + bool Contains(int x, int y); + void Fill(int x1, int y1, int x2, int y2, eDvbColor Color); + void SetBitmap(int x, int y, const cBitmap &Bitmap); + void Text(int x, int y, const char *s, eDvbColor ColorFg = clrWhite, eDvbColor ColorBg = clrBackground); + const char *Data(int x, int y); + }; + +cWindow::cWindow(int x, int y, int w, int h, int Bpp, bool ClearWithBackground) +:cBitmap(w, h, Bpp, ClearWithBackground) +{ + x0 = x; + y0 = y; + shown = false; +} + +bool cWindow::Contains(int x, int y) +{ + x -= x0; + y -= y0; + return x >= 0 && y >= 0 && x < width && y < height; +} + +void cWindow::Fill(int x1, int y1, int x2, int y2, eDvbColor Color) +{ + cBitmap::Fill(x1 - x0, y1 - y0, x2 - x0, y2 - y0, Color); +} + +void cWindow::SetBitmap(int x, int y, const cBitmap &Bitmap) +{ + cBitmap::SetBitmap(x - x0, y - y0, Bitmap); +} + +void cWindow::Text(int x, int y, const char *s, eDvbColor ColorFg, eDvbColor ColorBg) +{ + cBitmap::Text(x - x0, y - y0, s, ColorFg, ColorBg); +} + +const char *cWindow::Data(int x, int y) +{ + return cBitmap::Data(x, y); } // --- cDvbOsd --------------------------------------------------------------- -cDvbOsd::cDvbOsd(int VideoDev, int x1, int y1, int x2, int y2, int Bpp) -:cBitmap(x2 - x1 + 1, y2 - y1 + 1) +cDvbOsd::cDvbOsd(int VideoDev, int x, int y, int w, int h, int Bpp) { videoDev = VideoDev; - if (videoDev >= 0) - Cmd(OSD_Open, Bpp, x1, y1, x2, y2); + numWindows = 0; + x0 = x; + y0 = y; + if (videoDev >= 0) { + if (w > 0 && h > 0) + Create(0, 0, w, h, Bpp); + } else esyslog(LOG_ERR, "ERROR: illegal video device handle (%d)!", videoDev); } cDvbOsd::~cDvbOsd() { - if (videoDev >= 0) - Cmd(OSD_Close); + if (videoDev >= 0) { + while (numWindows > 0) { + Cmd(OSD_SetWindow, 0, numWindows--); + Cmd(OSD_Close); + delete window[numWindows]; + } + } } 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,41 +348,120 @@ 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); - 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. - // TODO Check if this is still necessary with driver versions after 0.7. + ioctl(videoDev, OSD_SEND_CMD, &dc); + usleep(5000); // 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. + // TODO Check if this is still necessary with driver versions after 0.7. sigprocmask(SIG_SETMASK, &oldset, NULL); } } -void cDvbOsd::Flush(void) +bool cDvbOsd::Create(int x, int y, int w, int h, int Bpp, bool ClearWithBackground, eDvbColor Color0, eDvbColor Color1, eDvbColor Color2, eDvbColor Color3) { - if (Dirty()) { - //XXX Workaround: apparently the bitmap sent to the driver always has to be a multiple - //XXX of 8 bits wide, and (dx * dy) also has to be a multiple of 8. - //TODO Fix driver (should be able to handle any size bitmaps!) - while ((dirtyX1 > 0 || dirtyX2 < width - 1) && ((dirtyX2 - dirtyX1) & 7) != 7) { - if (dirtyX2 < width - 1) - dirtyX2++; - else if (dirtyX1 > 0) - dirtyX1--; - } - while ((dirtyY1 > 0 || dirtyY2 < height - 1) && (((dirtyX2 - dirtyX1 + 1) * (dirtyY2 - dirtyY1 + 1) / 2) & 7) != 0) { - if (dirtyY2 < height - 1) - dirtyY2++; - else if (dirtyY1 > 0) - dirtyY1--; + /* TODO XXX + - check that no two windows overlap + */ + if (numWindows < MAXNUMWINDOWS) { + if (x >= 0 && y >= 0 && w > 0 && h > 0 && (Bpp == 1 || Bpp == 2 || Bpp == 4 || Bpp == 8)) { + if ((w & 0x03) != 0) { + w += 4 - (w & 0x03); + esyslog(LOG_ERR, "ERROR: OSD window width must be a multiple of 4 - increasing to %d", w); } - while ((dirtyX1 > 0 || dirtyX2 < width - 1) && (((dirtyX2 - dirtyX1 + 1) * (dirtyY2 - dirtyY1 + 1) / 2) & 7) != 0) { - if (dirtyX2 < width - 1) - dirtyX2++; - else if (dirtyX1 > 0) - dirtyX1--; + cWindow *win = new cWindow(x, y, w, h, Bpp, ClearWithBackground); + if (Color0 != clrTransparent) { + win->Index(Color0); + win->Index(Color1); + win->Index(Color2); + win->Index(Color3); + win->Reset(); } - Cmd(OSD_SetBlock, width, dirtyX1, dirtyY1, dirtyX2, dirtyY2, &bitmap[dirtyY1 * width + dirtyX1]); - Clean(); + window[numWindows++] = win; + Cmd(OSD_SetWindow, 0, numWindows); + Cmd(OSD_Open, Bpp, x0 + x, y0 + y, x0 + x + w - 1, y0 + y + h - 1, (void *)1); // initially hidden! + } + else + esyslog(LOG_ERR, "ERROR: illegal OSD parameters"); } + else + esyslog(LOG_ERR, "ERROR: too many OSD windows"); + return false; +} + +cWindow *cDvbOsd::GetWindow(int x, int y) +{ + for (int i = 0; i < numWindows; i++) { + if (window[i]->Contains(x, y)) + return window[i]; + } + return NULL; +} + +void cDvbOsd::Flush(void) +{ + for (int i = 0; i < numWindows; i++) { + int x1 = 0, y1 = 0, x2 = 0, y2 = 0; + if (window[i]->Dirty(x1, y1, x2, y2)) { + Cmd(OSD_SetWindow, 0, i + 1); + int FirstColor = 0, LastColor = 0; + const eDvbColor *pal; + while ((pal = window[i]->Colors(FirstColor, LastColor)) != NULL) + Cmd(OSD_SetPalette, FirstColor, LastColor, 0, 0, 0, pal); + Cmd(OSD_SetBlock, window[i]->Width(), x1, y1, x2, y2, window[i]->Data(x1, y1)); + window[i]->Clean(); + } + } + // Showing the windows in a separate loop to avoid seeing them come up one after another + for (int i = 0; i < numWindows; i++) { + if (!window[i]->Shown()) { + Cmd(OSD_SetWindow, 0, i + 1); + Cmd(OSD_MoveWindow, 0, x0 + window[i]->X0(), y0 + window[i]->Y0()); + } + } +} + +void cDvbOsd::Clear(void) +{ + for (int i = 0; i < numWindows; i++) + window[i]->Clear(); +} + +void cDvbOsd::Fill(int x1, int y1, int x2, int y2, eDvbColor Color) +{ + cWindow *w = GetWindow(x1, y1); + if (w) + w->Fill(x1, y1, x2, y2, Color); +} + +void cDvbOsd::SetBitmap(int x, int y, const cBitmap &Bitmap) +{ + cWindow *w = GetWindow(x, y); + if (w) + w->SetBitmap(x, y, Bitmap); +} + +int cDvbOsd::Width(unsigned char c) +{ + return numWindows ? window[0]->Width(c) : 0; +} + +int cDvbOsd::Width(const char *s) +{ + return numWindows ? window[0]->Width(s) : 0; +} + +eDvbFont cDvbOsd::SetFont(eDvbFont Font) +{ + eDvbFont oldFont = Font; + for (int i = 0; i < numWindows; i++) + oldFont = window[i]->SetFont(Font); + return oldFont; +} + +void cDvbOsd::Text(int x, int y, const char *s, eDvbColor ColorFg = clrWhite, eDvbColor ColorBg = clrBackground) +{ + cWindow *w = GetWindow(x, y); + if (w) + w->Text(x, y, s, ColorFg, ColorBg); } diff --git a/dvbosd.h b/dvbosd.h index eefdb62..5039a91 100644 --- a/dvbosd.h +++ b/dvbosd.h @@ -4,35 +4,26 @@ * 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.8 2001/07/28 16:22:32 kls Exp $ */ #ifndef __DVBOSD_H #define __DVBOSD_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 #include -#include #include "font.h" -enum eDvbColor { -#ifndef DEBUG_OSD - clrTransparent, -#endif - clrBackground, +#define MAXNUMCOLORS 16 + +enum eDvbColor { #ifdef DEBUG_OSD + clrBackground, clrTransparent = clrBackground, clrBlack = clrBackground, -#else - clrBlack, -#endif clrRed, clrGreen, clrYellow, @@ -40,39 +31,88 @@ enum eDvbColor { clrMagenta, clrCyan, clrWhite, +#else + clrTransparent = 0x00000000, + clrBackground = 0x7F000000, // 50% gray + clrBlack = 0xFF000000, + clrRed = 0xFF1414FC, + clrGreen = 0xFF24FC24, + clrYellow = 0xFF24C0FC, + clrMagenta = 0xFFFC00B0, + clrBlue = 0xFFFC0000, + clrCyan = 0xFFFCFC00, + clrWhite = 0xFFFCFCFC, +#endif + }; + +class cPalette { +private: + eDvbColor color[MAXNUMCOLORS]; + int maxColors, numColors; + bool used[MAXNUMCOLORS]; + bool fetched[MAXNUMCOLORS]; + bool full; +protected: + typedef unsigned char tIndexes[MAXNUMCOLORS]; +public: + cPalette(int Bpp); + int Index(eDvbColor Color); + void Reset(void); + const eDvbColor *Colors(int &FirstColor, int &LastColor); + void Take(const cPalette &Palette, tIndexes *Indexes = NULL); }; -class cBitmap { +class cBitmap : public cPalette { private: cFont *font; eDvbFont fontType; + void SetIndex(int x, int y, char Index); + char *bitmap; + bool clearWithBackground; protected: int width, height; - char *bitmap; int dirtyX1, dirtyY1, dirtyX2, dirtyY2; - void Clean(void); public: - cBitmap(int Width, int Height); + cBitmap(int Width, int Height, int Bpp, bool ClearWithBackground = true); virtual ~cBitmap(); eDvbFont SetFont(eDvbFont Font); - bool Dirty(void); + bool Dirty(int &x1, int &y1, int &x2, int &y2); void SetPixel(int x, int y, eDvbColor Color); void SetBitmap(int x, int y, const cBitmap &Bitmap); + int Width(void) { return width; } int Width(unsigned char c); int Width(const char *s); void Text(int x, int y, const char *s, eDvbColor ColorFg = clrWhite, eDvbColor ColorBg = clrBackground); void Fill(int x1, int y1, int x2, int y2, eDvbColor Color); + void Clean(void); void Clear(void); + const char *Data(int x, int y); }; -class cDvbOsd : public cBitmap { +#define MAXNUMWINDOWS 7 // OSD windows are counted 1...7 + +class cWindow; + +class cDvbOsd { private: int videoDev; + int numWindows; + int x0, y0; + cWindow *window[MAXNUMWINDOWS]; void Cmd(OSD_Command cmd, int color = 0, int x0 = 0, int y0 = 0, int x1 = 0, int y1 = 0, const void *data = NULL); + cWindow *GetWindow(int x, int y); public: - cDvbOsd(int VideoDev, int x1, int y1, int x2, int y2, int Bpp); + cDvbOsd(int VideoDev, int x, int y, int w = -1, int h = -1, int Bpp = -1); ~cDvbOsd(); + bool Create(int x, int y, int w, int h, int Bpp, bool ClearWithBackground = true, eDvbColor Color0 = clrTransparent, eDvbColor Color1 = clrTransparent, eDvbColor Color2 = clrTransparent, eDvbColor Color3 = clrTransparent); void Flush(void); + void Clear(void); + void Fill(int x1, int y1, int x2, int y2, eDvbColor Color); + void SetBitmap(int x, int y, const cBitmap &Bitmap); + int Width(unsigned char c); + int Width(const char *s); + eDvbFont SetFont(eDvbFont Font); + void Text(int x, int y, const char *s, eDvbColor ColorFg = clrWhite, eDvbColor ColorBg = clrBackground); }; #endif //__DVBOSD_H diff --git a/eit.c b/eit.c index 6cbe504..3de6102 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); + if (filters[a].inuse) + { + pfd[NumUsedFilters].fd = filters[a].handle; + pfd[NumUsedFilters].events = POLLIN; + NumUsedFilters++; + } + } - switch (pid) + // wait until data becomes ready from the bitfilter + if (poll(pfd, NumUsedFilters, 1000) != 0) + { + for (int a = 0; a < NumUsedFilters ; a++) { - case 0x14: - if (buf[0] == 0x70) + if (pfd[a].revents & POLLIN) + { + /* read section */ + unsigned char buf[4096+1]; // max. allowed size for any EIT section (+1 for safety ;-) + if (read(filters[a].handle, buf, 3) == 3) { - if (useTStime) + int seclen = ((buf[1] & 0x0F) << 8) | (buf[2] & 0xFF); + int pid = filters[a].pid; + int n = read(filters[a].handle, buf + 3, seclen); + if (n == seclen) { - cTDT ctdt((tdt_t *)buf); - ctdt.SetSystemTime(); + seclen += 3; + //dsyslog(LOG_INFO, "Received pid 0x%02x with table ID 0x%02x and length of %04d\n", pid, buf[0], seclen); + switch (pid) + { + case 0x14: + if (buf[0] == 0x70) + { + if (useTStime) + { + cTDT ctdt((tdt_t *)buf); + ctdt.SetSystemTime(); + } + } + /*XXX this comes pretty often: + else + dsyslog(LOG_INFO, "Time packet was not 0x70 but 0x%02x\n", (int)buf[0]); + XXX*/ + break; + + case 0x12: + if (buf[0] != 0x72) + { + LOCK_THREAD; + + schedulesMutex.Lock(); + cEIT ceit(buf, seclen, schedules); + ceit.ProcessEIT(); + schedulesMutex.Unlock(); + } + else + dsyslog(LOG_INFO, "Received stuffing section in EIT\n"); + break; + + default: + break; + } } + else + dsyslog(LOG_INFO, "read incomplete section - seclen = %d, n = %d", seclen, n); } - /*XXX this comes pretty often: - else - dsyslog(LOG_INFO, "Time packet was not 0x70 but 0x%02x\n", (int)buf[0]); - XXX*/ - break; - - case 0x12: - if (buf[0] != 0x72) - { - LOCK_THREAD; - - schedulesMutex.Lock(); - cEIT ceit(buf, seclen, schedules); - ceit.ProcessEIT(); - schedulesMutex.Unlock(); - } - else - dsyslog(LOG_INFO, "Received stuffing section in EIT\n"); - break; - - default: - break; + } } } - else - { - LOCK_THREAD; - - //XXX this comes pretty often - //isyslog(LOG_INFO, "Received timeout from poll, refreshing filters\n"); - RefreshFilters(); - } -// WakeUp(); } + + dsyslog(LOG_INFO, "EIT processing thread ended (pid=%d)%s", getpid(), masterSIProcessor ? " - master" : ""); } /** Add a filter with packet identifier pid and table identifer tid */ bool cSIProcessor::AddFilter(u_char pid, u_char tid) { - if (fsvbi < 0) - return false; - - int section = ((int)tid << 8) | 0x00ff; - - struct bitfilter filt = { - pid, - { section, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, - SECTION_CONTINUOS, 0, - FILTER_MEM, - {}, - }; - - if (ioctl(fsvbi, VIDIOCSBITFILTER, &filt) < 0) - return false; + dmxSctFilterParams sctFilterParams; + sctFilterParams.pid = pid; + memset(&sctFilterParams.filter.filter, 0, DMX_FILTER_SIZE); + memset(&sctFilterParams.filter.mask, 0, DMX_FILTER_SIZE); + sctFilterParams.timeout = 0; + sctFilterParams.flags = DMX_IMMEDIATE_START; + sctFilterParams.filter.filter[0] = tid; + sctFilterParams.filter.mask[0] = 0xFF; for (int a = 0; a < MAX_FILTERS; a++) { - if (filters[a].inuse == false) + if (!filters[a].inuse) { filters[a].pid = pid; filters[a].tid = tid; - filters[a].handle = filt.handle; - filters[a].inuse = true; - // dsyslog(LOG_INFO, " Registered filter handle %04x, pid = %02d, tid = %02d", filters[a].handle, filters[a].pid, filters[a].tid); + if ((filters[a].handle = open(fileName, O_RDWR | O_NONBLOCK)) >= 0) + { + if (ioctl(filters[a].handle, DMX_SET_FILTER, &sctFilterParams) >= 0) + filters[a].inuse = true; + else + { + esyslog(LOG_ERR, "ERROR: can't set filter"); + close(filters[a].handle); + return false; + } + // dsyslog(LOG_INFO, " Registered filter handle %04x, pid = %02d, tid = %02d", filters[a].handle, filters[a].pid, filters[a].tid); + } + else + { + esyslog(LOG_ERR, "ERROR: can't open filter handle"); + return false; + } return true; } } + esyslog(LOG_ERR, "ERROR: too many filters"); return false; } @@ -1294,27 +1319,19 @@ bool cSIProcessor::SetUseTSTime(bool use) } /** */ -bool cSIProcessor::ShutDownFilters() +bool cSIProcessor::ShutDownFilters(void) { - if (fsvbi < 0) - return false; - - bool ret = true; - for (int a = 0; a < MAX_FILTERS; a++) { - if (filters[a].inuse == true) + if (filters[a].inuse) { - if (ioctl(fsvbi, VIDIOCSSHUTDOWNFILTER, &filters[a].handle) < 0) - ret = false; - + close(filters[a].handle); // dsyslog(LOG_INFO, "Deregistered filter handle %04x, pid = %02d, tid = %02d", filters[a].handle, filters[a].pid, filters[a].tid); - filters[a].inuse = false; } } - return ret; + return true; // there's no real 'boolean' to return here... } /** */ @@ -1323,25 +1340,3 @@ bool cSIProcessor::SetCurrentServiceID(unsigned short servid) LOCK_THREAD; return schedules ? schedules->SetCurrentServiceID(servid) : false; } - -/** */ -bool cSIProcessor::RefreshFilters() -{ - if (fsvbi < 0) - return false; - - bool ret = true; - - ret = ShutDownFilters(); - - for (int a = 0; a < MAX_FILTERS; a++) - { - if (filters[a].inuse == false && filters[a].pid != 0 && filters[a].tid != 0) - { - if (!AddFilter(filters[a].pid, filters[a].tid)) - ret = false; - } - } - - return ret; -} diff --git a/eit.h b/eit.h index 3d49193..e6bbd13 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 b2c321a..571eea9 100644 --- a/i18n.c +++ b/i18n.c @@ -4,11 +4,14 @@ * 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.26 2001/07/27 13:32:43 kls Exp $ * * Slovenian translations provided by Miha Setina * Italian translations provided by Alberto Carraro * Dutch translations provided by Arnold Niessen + * Portugese translations provided by Paulo Manuel Martins Lopes + * French translations provided by Jean-Claude Repetto + * Norwegian translations provided by Jørgen Tvedt * */ @@ -49,7 +52,7 @@ #include "config.h" #include "tools.h" -const int NumLanguages = 5; +const int NumLanguages = 8; typedef const char *tPhrase[NumLanguages]; @@ -60,6 +63,9 @@ const tPhrase Phrases[] = { "Slovenski", "Italiano", "Nederlands", + "Portugues", + "Français", + "Norsk", }, // Menu titles: { "Main", @@ -67,84 +73,126 @@ const tPhrase Phrases[] = { "Glavni meni", "Principale", "Hoofdmenu", + "Principal", + "Menu", + "Hovedmeny", }, { "Schedule", "Programm", "Urnik", "Programmi", "Gids", + "Programa", + "Programmes", + "Programmer", }, { "Channels", "Kanäle", "Kanali", "Canali", "Kanalen", + "Canal", + "Chaînes", + "Kanaler", }, { "Timers", "Timer", "Termini", "Timer", "Timers", + "Alarmes", + "Programmation", + "Timer", }, { "Recordings", "Aufzeichnungen", "Posnetki", "Registrazioni", "Opnames", + "Gravacoes", + "Enregistrements", + "Opptak", }, { "Setup", "Einstellungen", "Nastavitve", "Opzioni", "Instellingen", + "Configurar", + "Configuration", + "Konfigurasjon", }, { "Commands", "Befehle", "Ukazi", "Comandi", "Commando's", + "Comandos", + "Commandes", + "Kommandoer", }, { "Edit Channel", "Kanal Editieren", "Uredi kanal", "Modifica canale", "Kanaal aanpassen", + "Modificar Canal", + "Modifier une chaîne", + "Editer Kanal", }, { "Edit Timer", "Timer Editieren", "Uredi termin", "Modifica Timer", "Timer veranderen", + "Modificar Alarme", + "Changer la programmation", + "Editer Timer", }, { "Event", "Sendung", "Oddaja", "Eventi", "Uitzending", + "Evento", + "Evènement", + "Hendelse" }, { "Summary", "Inhalt", "Vsebina", "Sommario", "Inhoud", + "Resumo", + "Résumé", + "Sammendrag", }, { "Schedule - %s", "Programm - %s", "Urnik - %s", "Programma - %s", "Programma - %s", + "Programa - %s", + "Programmes - %s", + "Program Guide - %s", }, { "What's on now?", "Was läuft jetzt?", "Kaj je na sporedu?", "In programmazione", "Wat is er nu?", + "O que ver agora?", + "Programmes en cours", + "Hvilket program sendes nå?", }, { "What's on next?", "Was läuft als nächstes?", "Kaj sledi?", "Prossimi programmi", "Wat komt er hierna?", + "O que ver depois?", + "Prochains programmes", + "Hvilket program er neste?", }, // Button texts (must not be more than 10 characters!): { "Edit", @@ -152,78 +200,126 @@ const tPhrase Phrases[] = { "Uredi", "Modifica", "Verander", + "Modificar", + "Modifier", + "Editer", }, { "New", "Neu", "Novo", "Nuovo", "Nieuw", + "Novo", + "Nouveau", + "Ny", }, { "Delete", "Löschen", "Odstrani", "Cancella", "Verwijder", + "Apagar", + "Supprimer", + "Slett", }, { "Mark", "Markieren", "Oznaci", "Marca", "Verplaats", + "Marcar", + "Marquer", + "Marker", }, { "Record", "Aufnehmen", "Posnemi", "Registra", "Opnemen", + "Gravar", + "Enregistre", + "Ta opp", }, { "Play", "Wiedergabe", "Predavajaj", "Riproduci", "Afspelen", + "Play", + "Lire", + "Spill av", }, { "Rewind", "Anfang", "Zacetek", "Da inizio", "Spoel terug", + "Rebobinar", + "Retour", + "Spol tilbake", }, { "Resume", "Weiter", "Nadaljuj", "Riprendi", "Verder", + "Continuar", + "Reprendre", + "Fortsett", }, { "Summary", "Inhalt", "Vsebina", "Sommario", "Inhoud", + "Resumo", + "Résumé", + "Sammendrag", }, { "Switch", "Umschalten", "Preklopi", "Cambia", "Selecteer", + "Seleccionar", + "Regarder", + "Skift til", }, { "Now", "Jetzt", "Sedaj", "Adesso", "Nu", + "Agora", + "Maintenant", + "Nå", }, { "Next", "Nächste", "Naslednji", "Prossimo", "Hierna", + "Proximo", + "Après", + "Neste", }, { "Schedule", "Programm", "Urnik", "Programma", "Programma", + "Programa", + "Programme", + "Programmer", + }, + { "Language", + "Sprache", + "Jezik", + "Linguaggio", + "Taal", + "", // TODO + "Langue", + "Språk", }, // Confirmations: { "Delete channel?", @@ -231,30 +327,45 @@ const tPhrase Phrases[] = { "Odstrani kanal?", "Cancello il canale?", "Kanaal verwijderen?", + "Apagar o Canal?", + "Supprimer la chaîne?", + "Slette kanal?", }, { "Delete timer?", "Timer löschen?", "Odstani termin?", "Cancello il timer?", "Timer verwijderen?", + "Apagar o Alarme?", + "Supprimer la programmation?", + "Slette timer?", }, { "Delete recording?", "Aufzeichnung löschen?", "Odstrani posnetek?", "Cancello la registrazione?", "Opname verwijderen?", + "Apagar Gravacão?", + "Supprimer l'enregistrement?", + "Slette opptak?", }, { "Stop recording?", "Aufzeichnung beenden?", "Koncaj snemanje?", "Fermo la registrazione?", "Opname stoppen?", + "Parar Gravacão?", + "Arrêter l'enregistrement?", + "Stoppe opptak?", }, { "Cancel editing?", "Schneiden abbrechen?", "Zelite prekiniti urejanje?", "Annullo la modifica?", "Bewerken afbreken?", + "Cancelar Modificar?", + "Annuler les modifications?", + "Avbryte redigering", }, // Channel parameters: { "Name", @@ -262,60 +373,117 @@ const tPhrase Phrases[] = { "Naziv", "Nome", "Naam", + "Nome", + "Nom", + "Navn", }, { "Frequency", "Frequenz", "Frekvenca", "Frequenza", "Frequentie", + "Frequencia", + "Fréquence", + "Frekvens", }, { "Polarization", "Polarisation", "Polarizacija", "Polarizzazione", "Polarisatie", + "Polarizacao", + "Polarisation", + "Polaritet", }, { "Diseqc", "Diseqc", "Diseqc", "Diseqc", "Diseqc", + "Diseqc", + "Diseqc", + "Diseqc", }, { "Srate", "Srate", "Srate", "Srate", "Srate", + "Srate", + "Srate", + "Symbolrate", }, { "Vpid", "Vpid", "Vpid", "Vpid", "Vpid", - }, - { "Apid", - "Apid", - "Apid", - "Apid", - "Apid", + "Vpid", + "Vpid", + "Video pid", + }, + { "Apid1", + "Apid1", + "Apid1", + "Apid1", + "Apid1", + "Apid1", + "Apid1", + "Audio pid1", + }, + { "Apid2", + "Apid2", + "Apid2", + "Apid2", + "Apid2", + "Apid2", + "Apid2", + "Audio pid2", + }, + { "Dpid1", + "Dpid1", + "Dpid1", + "Dpid1", + "Dpid1", + "Dpid1", + "Dpid1", + "AC3 pid1", + }, + { "Dpid2", + "Dpid2", + "Dpid2", + "Dpid2", + "Dpid2", + "Dpid2", + "Dpid2", + "AC3 pid2", }, { "Tpid", "Tpid", "Tpid", "Tpid", "Tpid", + "Tpid", + "Tpid", + "Teletext pid", }, { "CA", "CA", "CA", "CA", "CA", + "CA", + "CA", + "Kortleser", }, { "Pnr", "Pnr", "Pnr", "Pnr", "Pnr", + "Pnr", + "Pnr", + "Program Id", }, // Timer parameters: { "Active", @@ -323,48 +491,72 @@ const tPhrase Phrases[] = { "Aktivno", "Attivo", "Actief", + "Activo", + "Actif", + "Aktiv", }, { "Channel", "Kanal", "Kanal", "Canale", "Kanaal", + "Canal", + "Chaîne", + "Kanal", }, { "Day", "Tag", "Dan", "Giorno", "Dag", + "Dia", + "Jour", + "Dag", }, { "Start", "Anfang", "Zacetek", "Inizio", "Begin", + "Inicio", + "Début", + "Start", }, { "Stop", "Ende", "Konec", "Fine", "Einde", + "Fim", + "Fin", + "Slutt", }, { "Priority", "Priorität", "Prioriteta", "Priorita", "Prioriteit", + "Prioridade", + "Priorité", + "Prioritet", }, { "Lifetime", "Lebensdauer", "Veljavnost", "Durata", "Bewaarduur", + "Duracao", + "Durée de vie", + "Levetid", }, { "File", "Datei", "Datoteka", "Nome", "Filenaam", + "Ficheiro", + "Fichier", + "Filnavn", }, // Error messages: { "Channel is being used by a timer!", @@ -372,54 +564,81 @@ const tPhrase Phrases[] = { "Urnik zaseda kanal!", "Canale occupato da un timer!", "Kanaal wordt gebruikt door een timer!", + "Canal a ser utilizador por um alarme!", + "Cette chaîne est en cours d'utilisation!" + "Kanalen er i bruk av en timer!", }, { "Can't switch channel!", "Kanal kann nicht umgeschaltet werden!", "Ne morem preklopiti kanala!", "Impossibile cambiare canale!", "Kan geen kanaal wisselen!", + "Nao pode mudar de canal!", + "Impossible de changer de chaîne!", + "Ikke mulig å skifte kanal!", }, { "Timer is recording!", "Timer zeichnet gerade auf!", "Snemanje po urniku!", "Registrazione di un timer in corso!", "Timer is aan het opnemen!", + "Alarme a gravar!", + "Enregistrement en cours!", + "Timer gjør opptak!", }, { "Error while deleting recording!", "Fehler beim Löschen der Aufzeichnung!", "Napaka pri odstranjevanju posnetka!", "Errore durante la canc del filmato!", "Fout bij verwijderen opname!", + "Erro enquanto apagava uma gravacao!", + "Erreur de suppression de l'enregistrement!", + "Feil under sletting av opptak!", }, { "*** Invalid Channel ***", "*** Ungültiger Kanal ***", "*** Neznan kanal ***", "*** CANALE INVALIDO ***", "*** Ongeldig kanaal ***", + "*** Canal Invalido! ***", + "*** Chaîne invalide! ***", + "*** Ugyldig Kanal! ***", }, { "No free DVB device to record!", "Keine freie DVB-Karte zum Aufnehmen!", "Ni proste DVB naprave za snemanje!", "Nessuna card DVB disp per registrare!", "Geen vrije DVB kaart om op te nemen!", + "Nenhuma placa DVB disponivel para gravar!", + "Pas de carte DVB disponible pour l'enregistrement!", + "Ingen ledige DVB enheter for opptak!" }, { "Channel locked (recording)!", "Kanal blockiert (zeichnet auf)!", "Zaklenjen kanal (snemanje)!", "Canale bloccato (in registrazione)!", "Kanaal geblokkeerd (neemt op)!", + "Canal bloqueado (a gravar)!", + "Chaîne verrouillée (enregistrement en cours)!", + "Kanalen er låst (opptak)!", }, { "Can't start editing process!", "Schnitt kann nicht gestartet werden!", "Ne morem zaceti urejanja!", "Imposs iniziare processo di modifica", "Kan niet beginnen met bewerken!", + "Nao pode iniciar a modificacao!", + "Impossible de commencer le montage!", + "Kan ikke starte redigeringsprosessen!", }, { "Editing process already active!", "Schnitt bereits aktiv!", "Urejanje je ze aktivno!", "Processo di modifica gia` attivo", "Bewerken is al actief!", + "Processo de modificacao ja activo!", + "Montage déjà en cours!", + "Redigeringsprosessen er allerede aktiv!", }, // Setup parameters: { "OSD-Language", @@ -427,78 +646,186 @@ const tPhrase Phrases[] = { "OSD-jezik", "Linguaggio OSD", "OSD-taal", + "Linguagem OSD", + "Langue OSD", + "OSD Språk", }, { "PrimaryDVB", "Primäres Interface", "Primarna naprava", "Scheda DVB primaria", "Eerste DVB kaart", + "DVB primario", + "Première carte DVB", + "Hoved DVB-enhet", }, { "ShowInfoOnChSwitch", "Info zeigen", "Pokazi naziv kanala", "Vis info nel cambio canale", "Kanaal info tonen", + "Mostrar info ao mudar de Canal", + "Affichage progr. en cours", + "Info ved kanalskifte", }, { "MenuScrollPage", "Seitenweise scrollen", "Drsni meni", "Scrolla pagina nel menu", "Scrollen per pagina", + "Scroll da pagina no menu", + "Affichage progr. suivant", + "Rask rulling i menyer", }, { "MarkInstantRecord", "Direktaufz. markieren", "Oznaci direktno snemanje", "Marca la registrazione", "Direkte opnamen markeren", + "Marca de gravacao", + "Enregistrement immédiat", + "Markere direkteopptak", + }, + { "LnbSLOF", + "LnbSLOF", + "LnbSLOF", + "LnbSLOF", + "LnbSLOF", + "LnbSLOF", + "LnbSLOF", + "LO-grensefrekvens", }, { "LnbFrequLo", "Untere LNB-Frequenz", "Spodnja LNB-frek.", "Freq LO LNB", "Laagste LNB frequentie", + "Freq LO LNB", + "Fréquence basse LNB", + "LO-frekvens i lavbåndet", }, { "LnbFrequHi", "Obere LNB-Frequenz", "Zgornja LNB-frek.", "Freq HI LNB", "Hoogste LNB frequentie", + "Freq HI LNB", + "Fréquence haute LNB", + "LO-frekvens i høybåndet", + }, + { "DiSEqC", + "DiSEqC", + "DiSEqC", + "DiSEqC", + "DiSEqC", + "DiSEqC", + "DiSEqC", + "DiSEqC", }, { "SetSystemTime", "Systemzeit stellen", "Sistemski cas", "Setta orario auto", "Systeem klok instellen", + "Ajustar relogio do sistema", + "Ajuster l'heure du système", + "Juster system-klokken", }, { "MarginStart", "Zeitpuffer bei Anfang", "Premor pred zacetkom", "Min margine inizio", "Tijd marge (begin)", + "Margem de inicio", + "Marge antérieure", + "Opptaks margin (start)", }, { "MarginStop", "Zeitpuffer bei Ende", "Premor za koncem", "Min margine fine", "Tijd marge (eind)", + "Margem de fim", + "Marge postérieure", + "Opptaks margin (slutt)", }, { "EPGScanTimeout", "Zeit bis EPG Scan", "Cas do EPG pregleda", "Timeout EPG", "EPG-scan Timeout", + "Timeout EPG", + "Temps maxi EPG", + "Ledig tid før EPG-søk", }, { "SVDRPTimeout", "SVDRP Timeout", "", // TODO "Timeout SVDRP", "SVDRP Timeout", + "Timeout SVDRP", + "Temps maxi SVDRP", + "Ubrukt SVDRP-levetid", }, { "PrimaryLimit", "Primär-Limit", "", // TODO "", // TODO "", // TODO + "Limite Primario", + "Première limite", + "Prioritets grense HovedDVB", + }, + { "DefaultPriority", + "Default Priorität", + "", // TODO + "", // TODO + "", // TODO + "Prioridade por defeito", + "Priorité par défaut", + "Normal prioritet (Timer)", + }, + { "DefaultLifetime", + "Default Lebensdauer", + "", // TODO + "", // TODO + "", // TODO + "Validade por defeito", + "Durée de vie par défaut", + "Normal levetid (Timer)", + }, + { "VideoFormat", + "Video Format", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "Format vidéo", + "TV Format", + }, + { "ChannelInfoPos", + "Kanal Info Position", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { "OSDwidth", + "OSD Breite", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { "OSDheight", + "OSD Höhe", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO }, // The days of the week: { "MTWTFSS", @@ -506,6 +833,9 @@ const tPhrase Phrases[] = { "PTSCPSN", "DLMMGVS", "MDWDVZZ", + "STQQSSD", + "LMMJVSD", + "MTOTFLS", }, // Learning keys: { "Learning Remote Control Keys", @@ -513,78 +843,117 @@ const tPhrase Phrases[] = { "Ucim se kod upravljalca", "Apprendimento tasti unita` remota", "Leren toetsen afstandsbediening", + "Aprender as teclas do telecomando", + "Apprentissage des codes de télécommande", + "Lære fjernkontrolltaster", }, { "Phase 1: Detecting RC code type", "Phase 1: FB Code feststellen", "Faza 1: Sprejemanje IR kode", "Fase 1: tipo ricevitore RC", "Fase 1: detecteren type afstandsbediening", + "Fase 1: detectar tipo de receptor", + "Phase 1: Détection du type de code", + "Fase 1: Finne fjernkontroll-kodetype", }, { "Press any key on the RC unit", "Eine Taste auf der FB drücken", "Pritisnite tipko na upravljalcu", "Premere un tasto nell'unita` RC", "Druk op een willekeurige knop", + "Pressione qualquer tecla do telecomando", + "Appuyer sur une touche de la télécommande", + "Trykk en av tastene på fjernkontrollen", }, { "RC code detected!", "FB Code erkannt!", "IR koda sprejeta!", "Codice RC rilevato!", "Afstandsbediening code herkend!", + "Codigo do telecomando detectado!", + "Code de la télécommande détecté!", + "Fjernkontroll-kodetype funnet!", }, { "Do not press any key...", "Keine Taste drücken...", "Ne pritiskajte tipk...", "Non premere alcun tasto...", "Druk niet op een knop...", + "Nao pressione nada...", + "Ne pas appuyer sur une touche ...", + "Ikke trykk på noen av tastene...", }, { "Phase 2: Learning specific key codes", "Phase 2: Einzelne Tastencodes lernen", "Faza 2: Ucenje posebnih kod", "Fase 2: Codici specifici dei tasti", "Fase 2: Leren specifieke toets-codes", + "Fase 2: A aprender codigos especificos", + "Phase 2: Apprentissage des codes des touches", + "Fase 2: Lære spesifikke tastekoder", }, { "Press key for '%s'", "Taste für '%s' drücken", "Pritisnite tipko za '%s'", "Premere il tasto per '%s'", "Druk knop voor '%s'", + "Pressione tecla para '%s'", + "Appuyer sur la touche '%s'", + "Trykk tasten for '%s'", }, { "Press 'Up' to confirm", "'Auf' drücken zum Bestätigen", "Pritisnite tipko 'Gor' za potrditev", "Premere 'Su' per confermare", "Druk 'Omhoog' om te bevestigen", + "Pressione 'Cima' para confirmar", + "Appuyer sur 'Haut' pour confirmer", + "Trykk 'Opp' for å bekrefte", }, { "Press 'Down' to continue", "'Ab' drücken zum Weitermachen", "Pritisnite tipko 'Dol' za nadaljevanje", "Premere 'Giu' per confermare", "Druk 'Omlaag' om verder te gaan", + "Pressione 'Baixo' para continuar", + "Appuyer sur 'Bas' pour continuer", + "Trykk Ned' for å fortsette", }, { "(press 'Up' to go back)", "('Auf' drücken um zurückzugehen)", "(pritisnite 'Gor' za nazaj)", "(premere 'Su' per tornare indietro)", "(druk 'Omhoog' om terug te gaan)", + "(Pressione 'Cima' para voltar)", + "(Appuyer sur 'Haut' pour revenir en arrière)", + "(trykk 'Opp' for å gå tilbake)", }, { "(press 'Down' to end key definition)", "('Ab' drücken zum Beenden)", "(pritisnite 'Dol' za konec)", "('Giu' per finire la definiz tasti)", "(Druk 'Omlaag' om te beeindigen)", + "(Pressione 'Baixo' para terminar a definicao)", + "(Appuyer sur 'Bas' pour terminer)", + "(trykk 'Ned' for å avslutte innlæring)", }, { "Phase 3: Saving key codes", "Phase 3: Codes abspeichern", "Faza 3: Shranjujem kodo", "Fase 3: Salvataggio key codes", "Fase 3: Opslaan toets codes", + "Fase 3: A Salvar os codigos das teclas", + "Phase 3: Sauvegarde des codes des touches", + "Fase 3: Lagre tastekoder", }, { "Press 'Up' to save, 'Down' to cancel", "'Auf' speichert, 'Ab' bricht ab", "'Gor' za potrditev, 'Dol' za prekinitev", "'Su' per salvare, 'Giu' per annullare", "'Omhoog' te bewaren, 'Omlaag' voor annuleren", + "'Cima' para Salvar, 'Baixo' para Cancelar", + "Appuyer sur 'Haut' pour sauvegarder, 'Bas' pour annuler", + "Trykk 'Opp' for å lagre, 'Ned' for å avbryte", }, // Key names: { "Up", @@ -592,66 +961,99 @@ const tPhrase Phrases[] = { "Gor", "Su", "Omhoog", + "Cima", + "Haut", + "Opp", }, { "Down", "Ab", "Dol", "Giu", "Omlaag", + "Baixo", + "Bas", + "Ned", }, { "Menu", "Menü", "Meni", "Menu", "Menu", + "Menu", + "Menu", + "Meny", }, { "Ok", "Ok", "Ok", "Ok", "Ok", + "Ok", + "Ok", + "Ok", }, { "Back", "Zurück", "Nazaj", "Indietro", "Terug", + "Voltar", + "Retour", + "Tilbake", }, { "Left", "Links", "Levo", "Sinistra", "Links", + "Esquerda", + "Gauche", + "Venstre", }, { "Right", "Rechts", "Desno", "Destra", "Rechts", + "Direita", + "Droite", + "Høyre", }, { "Red", "Rot", "Rdeca", "Rosso", "Rood", + "Vermelho", + "Rouge", + "Rød", }, { "Green", "Grün", "Zelena", "Verde", "Groen", + "Verde", + "Vert", + "Grønn", }, { "Yellow", "Gelb", "Rumena", "Giallo", "Geel", + "Amarelo", + "Jaune", + "Gul", }, { "Blue", "Blau", "Modra", "Blu", "Blauw", + "Azul", + "Bleu", + "Blå", }, // Miscellaneous: { "yes", @@ -659,48 +1061,88 @@ const tPhrase Phrases[] = { "da", "si", "ja", + "sim", + "oui", + "ja", }, { "no", "nein", "ne", "no", "nee", + "nao", + "non", + "nei", + }, + { "top", + "oben", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, + { "bottom", + "unten", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO }, { " Stop replaying", // note the leading blank! " Wiedergabe beenden", " Prekini ponavljanje", " Interrompi riproduzione", " Stop afspelen", + " Parar reproducao", + " Arrêter la lecture", + " Stopp avspilling", }, { " Stop recording ", // note the leading and trailing blanks! " Aufzeichnung beenden ", " Prekini shranjevanje ", " Interrompi registrazione ", " Stop opnemen ", + " Parar gravacao ", + " Arrêter l'enregistrement ", + " Stopp opptak fra ", }, { " Cancel editing", // note the leading blank! " Schneiden abbrechen", " Prekini urejanje", " Annulla modifiche", " Bewerken afbreken", + " Anular modificacao", + " Annuler le montage", + " Avbryt editering", }, { "Switching primary DVB...", "Primäres Interface wird umgeschaltet...", "Preklapljanje primarne naprave...", "Cambio su card DVB primaria...", "Eerste DVB-kaart wordt omgeschakeld...", + "A mudar placa DVB primaria...", + "Changement de carte DVB...", + "Bytter hoved DVB-enhet... ", }, { "Up/Dn for new location - OK to move", "Auf/Ab für neue Position - dann OK", "Gor/Dol za novo poz. - Ok za premik", "Su/Giu per nuova posizione - OK per muovere", "Gebruik Omhoog/Omlaag - daarna Ok", + "Cima/Baixo para nova localizacao - Ok para mudar", + "Haut/Bas -> nouvelle place - OK -> déplacer", + "Opp/Ned for ny plass - OK for å flytte", }, { "Editing process started", "Schnitt gestartet", "Urejanje se je zacelo", "Processo di modifica iniziato", "Bewerken is gestart", + "Processo de modificacao iniciado", + "Opération de montage lancée", + "Redigeringsprosess startet", }, { NULL } }; diff --git a/interface.c b/interface.c index 55cdb0b..03dca06 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.39 2001/07/28 14:57:52 kls Exp $ */ #include "interface.h" @@ -44,8 +44,13 @@ cInterface::~cInterface() void cInterface::Open(int NumCols, int NumLines) { - if (!open++) + if (!open++) { + if (NumCols == 0) + NumCols = Setup.OSDwidth; + if (NumLines == 0) + NumLines = Setup.OSDheight; cDvbApi::PrimaryDvbApi->Open(width = NumCols, height = NumLines); + } } void cInterface::Close(void) @@ -281,16 +286,17 @@ void cInterface::Title(const char *s) void cInterface::Status(const char *s, eDvbColor FgColor, eDvbColor BgColor) { - ClearEol(0, -3, s ? BgColor : clrBackground); + int Line = (abs(height) == 1) ? 0 : -2; + ClearEol(0, Line, s ? BgColor : clrBackground); if (s) - Write(0, -3, s, FgColor, BgColor); + Write(0, Line, s, FgColor, BgColor); } void cInterface::Info(const char *s) { - Open(); + Open(Setup.OSDwidth, -1); isyslog(LOG_INFO, "info: %s", s); - Status(s, clrWhite, clrGreen); + Status(s, clrBlack, clrGreen); Wait(); Status(NULL); Close(); @@ -298,7 +304,7 @@ void cInterface::Info(const char *s) void cInterface::Error(const char *s) { - Open(); + Open(Setup.OSDwidth, -1); esyslog(LOG_ERR, "ERROR: %s", s); Status(s, clrWhite, clrRed); Wait(); @@ -310,7 +316,7 @@ bool cInterface::Confirm(const char *s) { Open(); isyslog(LOG_INFO, "confirm: %s", s); - Status(s, clrBlack, clrGreen); + Status(s, clrBlack, clrYellow); bool result = Wait(10) == kOk; Status(NULL); Close(); diff --git a/interface.h b/interface.h index 50c3761..2b0e2f1 100644 --- a/interface.h +++ b/interface.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: interface.h 1.21 2000/12/09 10:48:41 kls Exp $ + * $Id: interface.h 1.22 2001/07/27 11:38:01 kls Exp $ */ #ifndef __INTERFACE_H @@ -32,7 +32,7 @@ private: public: cInterface(int SVDRPport = 0); ~cInterface(); - void Open(int NumCols = MenuColumns, int NumLines = MenuLines); + void Open(int NumCols = 0, int NumLines = 0); void Close(void); int Width(void) { return width; } int Height(void) { return height; } diff --git a/menu.c b/menu.c index 09c180c..30532a7 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.88 2001/07/28 16:17:28 kls Exp $ */ #include "menu.h" @@ -118,21 +118,24 @@ eOSState cMenuEditIntItem::ProcessKey(eKeys Key) class cMenuEditBoolItem : public cMenuEditIntItem { protected: + const char *falseString, *trueString; virtual void Set(void); public: - cMenuEditBoolItem(const char *Name, int *Value); + cMenuEditBoolItem(const char *Name, int *Value, const char *FalseString = NULL, const char *TrueString = NULL); }; -cMenuEditBoolItem::cMenuEditBoolItem(const char *Name, int *Value) +cMenuEditBoolItem::cMenuEditBoolItem(const char *Name, int *Value, const char *FalseString, const char *TrueString) :cMenuEditIntItem(Name, Value, 0, 1) { + falseString = FalseString ? FalseString : tr("no"); + trueString = TrueString ? TrueString : tr("yes"); Set(); } void cMenuEditBoolItem::Set(void) { char buf[16]; - snprintf(buf, sizeof(buf), "%s", *value ? tr("yes") : tr("no")); + snprintf(buf, sizeof(buf), "%s", *value ? trueString : falseString); SetValue(buf); } @@ -274,7 +277,12 @@ cMenuEditTimeItem::cMenuEditTimeItem(const char *Name, int *Value) void cMenuEditTimeItem::Set(void) { char buf[10]; - snprintf(buf, sizeof(buf), "%02d:%02d", hh, mm); + switch (pos) { + case 1: snprintf(buf, sizeof(buf), "%01d-:--", hh / 10); break; + case 2: snprintf(buf, sizeof(buf), "%02d:--", hh); break; + case 3: snprintf(buf, sizeof(buf), "%02d:%01d-", hh, mm / 10); break; + default: snprintf(buf, sizeof(buf), "%02d:%02d", hh, mm); + } SetValue(buf); } @@ -538,10 +546,13 @@ cMenuEditChannel::cMenuEditChannel(int Index) Add(new cMenuEditIntItem( tr("Frequency"), &data.frequency, 10000, 13000)); //TODO exact limits??? 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("Srate"), &data.srate, 22000, 30000)); //TODO exact limits - toggle??? + Add(new cMenuEditIntItem( tr("Vpid"), &data.vpid, 0, 0xFFFE)); + Add(new cMenuEditIntItem( tr("Apid1"), &data.apid1, 0, 0xFFFE)); + Add(new cMenuEditIntItem( tr("Apid2"), &data.apid2, 0, 0xFFFE)); + Add(new cMenuEditIntItem( tr("Dpid1"), &data.dpid1, 0, 0xFFFE)); + Add(new cMenuEditIntItem( tr("Dpid2"), &data.dpid2, 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)); } @@ -579,7 +590,7 @@ cMenuChannelItem::cMenuChannelItem(int Index, cChannel *Channel) index = Index; channel = Channel; if (channel->groupSep) - SetColor(clrWhite, clrBlue); + SetColor(clrWhite, clrCyan); Set(); } @@ -589,7 +600,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); } @@ -868,7 +879,7 @@ public: cMenuText::cMenuText(const char *Title, const char *Text, eDvbFont Font) :cOsdMenu(Title) { - Add(new cMenuTextItem(Text, 1, 2, MenuColumns - 2, MAXOSDITEMS, clrWhite, clrBackground, Font)); + Add(new cMenuTextItem(Text, 1, 2, Setup.OSDwidth - 2, MAXOSDITEMS, clrWhite, clrBackground, Font)); } eOSState cMenuText::ProcessKey(eKeys Key) @@ -904,13 +915,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 +974,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, @@ -1137,20 +1148,20 @@ cMenuEvent::cMenuEvent(const cEventInfo *EventInfo, bool CanSwitch) // like all the others? Well, at least like those who actually send the full range // of information (like, e.g., 'Sat.1'). Some stations (like 'RTL') don't even // bother sending anything but the 'Title'... - if (isempty(ExtendedDescription) && !isempty(Subtitle) && strlen(Subtitle) > 2 * MenuColumns) { + if (isempty(ExtendedDescription) && !isempty(Subtitle) && int(strlen(Subtitle)) > 2 * Setup.OSDwidth) { ExtendedDescription = Subtitle; Subtitle = NULL; } if (!isempty(Title)) { - Add(item = new cMenuTextItem(Title, 1, Line, MenuColumns - 2, -1, clrCyan)); + Add(item = new cMenuTextItem(Title, 1, Line, Setup.OSDwidth - 2, -1, clrCyan)); Line += item->Height() + 1; } if (!isempty(Subtitle)) { - Add(item = new cMenuTextItem(Subtitle, 1, Line, MenuColumns - 2, -1, clrYellow)); + Add(item = new cMenuTextItem(Subtitle, 1, Line, Setup.OSDwidth - 2, -1, clrYellow)); Line += item->Height() + 1; } if (!isempty(ExtendedDescription)) - Add(new cMenuTextItem(ExtendedDescription, 1, Line, MenuColumns - 2, Height() - Line - 2, clrCyan), true); + Add(new cMenuTextItem(ExtendedDescription, 1, Line, Setup.OSDwidth - 2, Height() - Line - 2, clrCyan), true); SetHelp(tr("Record"), NULL, NULL, CanSwitch ? tr("Switch") : NULL); } } @@ -1265,7 +1276,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 +1402,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,14 +1619,22 @@ 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("DiSEqC"), &data.DiSEqC)); Add(new cMenuEditBoolItem(tr("SetSystemTime"), &data.SetSystemTime)); Add(new cMenuEditIntItem( tr("MarginStart"), &data.MarginStart)); 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)); + Add(new cMenuEditBoolItem(tr("VideoFormat"), &data.VideoFormat, "4:3", "16:9")); + Add(new cMenuEditBoolItem(tr("ChannelInfoPos"), &data.ChannelInfoPos, tr("bottom"), tr("top"))); + Add(new cMenuEditIntItem( tr("OSDwidth"), &data.OSDwidth, MINOSDWIDTH, MAXOSDWIDTH)); + Add(new cMenuEditIntItem( tr("OSDheight"), &data.OSDheight, MINOSDHEIGHT, MAXOSDHEIGHT)); } eOSState cMenuSetup::ProcessKey(eKeys Key) @@ -1626,6 +1645,7 @@ eOSState cMenuSetup::ProcessKey(eKeys Key) switch (Key) { case kOk: state = (Setup.PrimaryDVB != data.PrimaryDVB) ? osSwitchDvb : osEnd; cDvbApi::PrimaryDvbApi->SetUseTSTime(data.SetSystemTime); + cDvbApi::PrimaryDvbApi->SetVideoFormat(data.VideoFormat ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3); Setup = data; Setup.Save(); break; @@ -1722,7 +1742,7 @@ cMenuMain::cMenuMain(bool Replaying) } if (cVideoCutter::Active()) Add(new cOsdItem(tr(" Cancel editing"), osCancelEdit)); - SetHelp(tr("Record"), NULL, NULL, cReplayControl::LastReplayed() ? tr("Resume") : NULL); + SetHelp(tr("Record"), cDvbApi::PrimaryDvbApi->CanToggleAudioTrack() ? tr("Language") : NULL, NULL, cReplayControl::LastReplayed() ? tr("Resume") : NULL); Display(); lastActivity = time(NULL); SetHasHotkeys(); @@ -1753,14 +1773,22 @@ eOSState cMenuMain::ProcessKey(eKeys Key) } break; default: switch (Key) { - case kMenu: state = osEnd; break; - case kRed: if (!HasSubMenu()) - state = osRecord; - break; - case kBlue: if (!HasSubMenu()) - state = osReplay; - break; - default: break; + case kMenu: state = osEnd; break; + case kRed: if (!HasSubMenu()) + state = osRecord; + break; + case kGreen: if (!HasSubMenu()) { + if (cDvbApi::PrimaryDvbApi->CanToggleAudioTrack()) { + Interface->Clear(); + cDvbApi::PrimaryDvbApi->ToggleAudioTrack(); + state = osEnd; + } + } + break; + case kBlue: if (!HasSubMenu()) + state = osReplay; + break; + default: break; } } if (Key != kNone) @@ -1772,7 +1800,7 @@ eOSState cMenuMain::ProcessKey(eKeys Key) // --- cDisplayChannel ------------------------------------------------------- -#define DIRECTCHANNELTIMEOUT 500 //ms +#define DIRECTCHANNELTIMEOUT 1000 //ms #define INFOTIMEOUT 5000 //ms cDisplayChannel::cDisplayChannel(int Number, bool Switched, bool Group) @@ -1783,7 +1811,7 @@ cDisplayChannel::cDisplayChannel(int Number, bool Switched, bool Group) lines = 0; oldNumber = number = 0; cChannel *channel = Group ? Channels.Get(Number) : Channels.GetByNumber(Number); - Interface->Open(MenuColumns, 5); + Interface->Open(Setup.OSDwidth, Setup.ChannelInfoPos ? 5 : -5); if (channel) { DisplayChannel(channel); DisplayInfo(); @@ -1797,7 +1825,7 @@ cDisplayChannel::cDisplayChannel(eKeys FirstKey) oldNumber = cDvbApi::CurrentChannel(); number = 0; lastTime = time_ms(); - Interface->Open(MenuColumns, 5); + Interface->Open(Setup.OSDwidth, Setup.ChannelInfoPos ? 5 : -5); ProcessKey(FirstKey); } @@ -1810,21 +1838,20 @@ cDisplayChannel::~cDisplayChannel() void cDisplayChannel::DisplayChannel(const cChannel *Channel) { - if (Channel && Channel->number) + if (Channel && Channel->number > 0) Interface->DisplayChannelNumber(Channel->number); int BufSize = Width() + 1; char buffer[BufSize]; - if (Channel && Channel->number) - snprintf(buffer, BufSize, "%d %s", Channel->number, Channel->name); + if (Channel && Channel->number > 0) + snprintf(buffer, BufSize, "%d%s %s", Channel->number, number ? "-" : "", Channel->name); else snprintf(buffer, BufSize, "%s", Channel ? Channel->name : tr("*** Invalid Channel ***")); - Interface->Fill(0, 0, MenuColumns, 1, clrBackground); + Interface->Fill(0, 0, Setup.OSDwidth, 1, clrBackground); Interface->Write(0, 0, buffer); time_t t = time(NULL); struct tm *now = localtime(&t); snprintf(buffer, BufSize, "%02d:%02d", now->tm_hour, now->tm_min); Interface->Write(-5, 0, buffer); - Interface->Flush(); } void cDisplayChannel::DisplayInfo(void) @@ -1857,7 +1884,7 @@ void cDisplayChannel::DisplayInfo(void) if (Lines > lines) { const int t = 6; int l = 1; - Interface->Fill(0, 1, MenuColumns, Lines, clrBackground); + Interface->Fill(0, 1, Setup.OSDwidth, Lines, clrBackground); if (!isempty(PresentTitle)) { Interface->Write(0, l, Present->GetTimeString(), clrYellow, clrBackground); Interface->Write(t, l, PresentTitle, clrCyan, clrBackground); @@ -1945,11 +1972,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 +2009,7 @@ bool cRecordControl::Process(void) { if (!timer || !timer->Matches()) return false; - AssertFreeDiskSpace(); + AssertFreeDiskSpace(timer->priority); return true; } @@ -1993,7 +2023,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++) { @@ -2078,7 +2108,7 @@ public: }; cProgressBar::cProgressBar(int Width, int Height, int Current, int Total, const cMarks &Marks) -:cBitmap(Width, Height) +:cBitmap(Width, Height, 2) { total = Total; if (total > 0) { @@ -2119,6 +2149,8 @@ cReplayControl::cReplayControl(void) { dvbApi = cDvbApi::PrimaryDvbApi; visible = shown = displayFrames = false; + lastCurrent = lastTotal = -1; + timeoutShow = 0; if (fileName) { marks.Load(fileName); dvbApi->StartReplay(fileName); @@ -2152,12 +2184,14 @@ void cReplayControl::ClearLastReplayed(const char *FileName) } } -void cReplayControl::Show(void) +void cReplayControl::Show(int Seconds) { if (!visible) { - Interface->Open(MenuColumns, -3); + Interface->Open(Setup.OSDwidth, -3); needsFastResponse = visible = true; shown = ShowProgress(true); + if (Seconds > 0) + timeoutShow = time(NULL) + Seconds; } } @@ -2178,21 +2212,27 @@ bool cReplayControl::ShowProgress(bool Initial) Interface->Clear(); if (title) Interface->Write(0, 0, title); - displayFrames = marks.Count() > 0; + lastCurrent = lastTotal = -1; + } + if (Total != lastTotal) { + Interface->Write(-7, 2, IndexToHMSF(Total)); + Interface->Flush(); + lastTotal = Total; } - Interface->Write(-7, 2, IndexToHMSF(Total)); - Interface->Flush(); + if (Current != lastCurrent) { #ifdef DEBUG_OSD - int p = Width() * Current / Total; - Interface->Fill(0, 1, p, 1, clrGreen); - Interface->Fill(p, 1, Width() - p, 1, clrWhite); + int p = Width() * Current / Total; + Interface->Fill(0, 1, p, 1, clrGreen); + Interface->Fill(p, 1, Width() - p, 1, clrWhite); #else - cProgressBar ProgressBar(Width() * dvbApi->CellWidth(), dvbApi->LineHeight(), Current, Total, marks); - Interface->SetBitmap(0, dvbApi->LineHeight(), ProgressBar); - Interface->Flush(); + cProgressBar ProgressBar(Width() * dvbApi->CellWidth(), dvbApi->LineHeight(), Current, Total, marks); + Interface->SetBitmap(0, dvbApi->LineHeight(), ProgressBar); + Interface->Flush(); #endif - Interface->Write(0, 2, IndexToHMSF(Current, displayFrames)); - Interface->Flush(); + Interface->Write(0, 2, IndexToHMSF(Current, displayFrames)); + Interface->Flush(); + lastCurrent = Current; + } return true; } return false; @@ -2205,22 +2245,24 @@ void cReplayControl::MarkToggle(void) cMark *m = marks.Get(Current); if (m) marks.Del(m); - else + else { marks.Add(Current); + Show(2); + } marks.Save(); } - displayFrames = marks.Count() > 0; - if (!displayFrames) - Interface->Fill(0, 2, Width() / 2, 1, clrBackground); } void cReplayControl::MarkJump(bool Forward) { - int Current, Total; - if (dvbApi->GetIndex(Current, Total)) { - cMark *m = Forward ? marks.GetNext(Current) : marks.GetPrev(Current); - if (m) - dvbApi->Goto(m->position, true); + if (marks.Count()) { + int Current, Total; + if (dvbApi->GetIndex(Current, Total)) { + cMark *m = Forward ? marks.GetNext(Current) : marks.GetPrev(Current); + if (m) + dvbApi->Goto(m->position, true); + } + displayFrames = true; } } @@ -2230,6 +2272,7 @@ void cReplayControl::MarkMove(bool Forward) if (dvbApi->GetIndex(Current, Total)) { cMark *m = marks.Get(Current); if (m) { + displayFrames = true; int p = dvbApi->SkipFrames(Forward ? 1 : -1); cMark *m2; if (Forward) { @@ -2281,8 +2324,16 @@ eOSState cReplayControl::ProcessKey(eKeys Key) { if (!dvbApi->Replaying()) return osEnd; - if (visible) - shown = ShowProgress(!shown) || shown; + if (visible) { + if (timeoutShow && time(NULL) > timeoutShow) { + Hide(); + timeoutShow = 0; + } + else + shown = ShowProgress(!shown) || shown; + } + bool DisplayedFrames = displayFrames; + displayFrames = false; switch (Key) { // Positioning: case kUp: dvbApi->Play(); break; @@ -2299,23 +2350,34 @@ eOSState cReplayControl::ProcessKey(eKeys Key) case kBlue: Hide(); dvbApi->StopReplay(); return osEnd; - // Editing: - //XXX should we do this only when the ProgressDisplay is on??? - case kMarkToggle: MarkToggle(); break; - case kMarkJumpBack: MarkJump(false); break; - case kMarkJumpForward: MarkJump(true); break; - case kMarkMoveBack|k_Repeat: - case kMarkMoveBack: MarkMove(false); break; - case kMarkMoveForward|k_Repeat: - case kMarkMoveForward: MarkMove(true); break; - case kEditCut: EditCut(); break; - case kEditTest: EditTest(); break; - // Menu control: - case kMenu: Hide(); return osMenu; // allow direct switching to menu - case kOk: visible ? Hide() : Show(); break; - case kBack: return osRecordings; - default: return osUnknown; + default: { + switch (Key) { + // Editing: + //XXX should we do this only when the ProgressDisplay is on??? + case kMarkToggle: MarkToggle(); break; + case kMarkJumpBack: MarkJump(false); break; + case kMarkJumpForward: MarkJump(true); break; + case kMarkMoveBack|k_Repeat: + case kMarkMoveBack: MarkMove(false); break; + case kMarkMoveForward|k_Repeat: + case kMarkMoveForward: MarkMove(true); break; + case kEditCut: EditCut(); break; + case kEditTest: EditTest(); break; + default: { + displayFrames = DisplayedFrames; + switch (Key) { + // Menu control: + case kMenu: Hide(); return osMenu; // allow direct switching to menu + case kOk: visible ? Hide() : Show(); break; + case kBack: return osRecordings; + default: return osUnknown; + } + } + } + } } + if (DisplayedFrames && !displayFrames) + Interface->Fill(0, 2, Width() / 2, 1, clrBackground); return osContinue; } diff --git a/menu.h b/menu.h index 356122e..adc10b7 100644 --- a/menu.h +++ b/menu.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.h 1.18 2001/02/11 10:30:35 kls Exp $ + * $Id: menu.h 1.20 2001/07/28 13:59:29 kls Exp $ */ #ifndef _MENU_H @@ -83,7 +83,9 @@ private: cDvbApi *dvbApi; cMarks marks; bool visible, shown, displayFrames; - void Show(void); + int lastCurrent, lastTotal; + time_t timeoutShow; + void Show(int Seconds = 0); void Hide(void); static char *fileName; static char *title; diff --git a/osd.h b/osd.h index c0fb1a1..a1977be 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.22 2001/07/27 11:33:30 kls Exp $ */ #ifndef __OSD_H @@ -14,7 +14,7 @@ #include "interface.h" #include "tools.h" -#define MAXOSDITEMS 9 +#define MAXOSDITEMS (Setup.OSDheight - 4) enum eOSState { osUnknown, osMenu, diff --git a/recording.c b/recording.c index fc0daa5..ddca1a4 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.32 2001/06/16 10:33:20 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,13 +94,16 @@ void AssertFreeDiskSpace(void) cRecording *r = Recordings.First(); cRecording *r0 = NULL; while (r) { - if ((time(NULL) - r->start) / SECSINDAY > r->lifetime) { - if (r0) { - if (r->priority < r0->priority) + if (r->lifetime < MAXLIFETIME) { // 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 || (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; } - else - r0 = r; } r = Recordings.Next(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); @@ -174,18 +177,31 @@ void cResumeFile::Delete(void) // --- cRecording ------------------------------------------------------------ +struct tCharExchange { char a; char b; }; +tCharExchange CharExchange[] = { + { ' ', '_' }, + { '\'', '\x01' }, + { '/', '\x02' }, +#ifdef VFAT + { ':', '\x03' }, +#endif + { 0, 0 } + }; + +char *ExchangeChars(char *s, bool ToFileSystem) +{ + for (struct tCharExchange *ce = CharExchange; ce->a && ce->b; ce++) + strreplace(s, ToFileSystem ? ce->a : ce->b, ToFileSystem ? ce->b : ce->a); + return s; +} + cRecording::cRecording(cTimer *Timer) { titleBuffer = NULL; fileName = NULL; name = strdup(Timer->file); // substitute characters that would cause problems in file names: - for (char *p = name; *p; p++) { - switch (*p) { - case '\n': *p = ' '; break; - case '/': *p = '-'; break; - } - } + strreplace(name, '\n', ' '); summary = Timer->summary ? strdup(Timer->summary) : NULL; if (summary) strreplace(summary, '|', '\n'); @@ -215,8 +231,7 @@ cRecording::cRecording(const char *FileName) name = new char[p - FileName + 1]; strncpy(name, FileName, p - FileName); name[p - FileName] = 0; - strreplace(name, '_', ' '); - strreplace(name, '\x01', '\''); + ExchangeChars(name, false); } // read an optional summary file: char *SummaryFileName = NULL; @@ -239,7 +254,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); @@ -266,11 +281,9 @@ const char *cRecording::FileName(void) { if (!fileName) { struct tm *t = localtime(&start); + ExchangeChars(name, true); asprintf(&fileName, NAMEFORMAT, VideoDirectory, name, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, priority, lifetime); - if (fileName) { - strreplace(fileName, ' ', '_'); - strreplace(fileName, '\'', '\x01'); - } + ExchangeChars(name, false); } return fileName; } diff --git a/recording.h b/recording.h index b561fa2..059133e 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/remote.c b/remote.c index 9402438..db8fa87 100644 --- a/remote.c +++ b/remote.c @@ -6,7 +6,7 @@ * * Ported to LIRC by Carsten Koch 2000-06-16. * - * $Id: remote.c 1.21 2001/02/04 19:17:59 kls Exp $ + * $Id: remote.c 1.23 2001/07/27 10:17:19 kls Exp $ */ #include "remote.h" @@ -439,7 +439,7 @@ void cRcIoLIRC::Action(void) if (cFile::FileReady(f, REPEATLIMIT) && read(f, buf, sizeof(buf)) > 21) { if (!receivedData) { // only accept new data the previous data has been fetched int count; - sscanf(buf, "%*x %x %7s", &count, LastKeyName); // '7' in '%7s' is LIRC_KEY_BUF-1! + sscanf(buf, "%*x %x %29s", &count, LastKeyName); // '29' in '%29s' is LIRC_KEY_BUF-1! int Now = time_ms(); if (count == 0) { strcpy(keyName, LastKeyName); @@ -466,9 +466,11 @@ void cRcIoLIRC::Action(void) } } else if (receivedRepeat) { // all data has already been fetched, but the last one was a repeat, so let's generate a release - receivedData = receivedRelease = true; - receivedRepeat = false; - WakeUp(); + if (time_ms() - LastTime > REPEATDELAY) { + receivedData = receivedRelease = true; + receivedRepeat = false; + WakeUp(); + } } } } diff --git a/remote.h b/remote.h index 659de1a..bbdf2fa 100644 --- a/remote.h +++ b/remote.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: remote.h 1.14 2001/02/02 14:49:10 kls Exp $ + * $Id: remote.h 1.15 2001/07/22 14:42:59 kls Exp $ */ #ifndef __REMOTE_H @@ -81,7 +81,7 @@ public: class cRcIoLIRC : public cRcIoBase, private cThread { private: - enum { LIRC_KEY_BUF = 8, LIRC_BUFFER_SIZE = 128 }; + enum { LIRC_KEY_BUF = 30, LIRC_BUFFER_SIZE = 128 }; int f; char keyName[LIRC_KEY_BUF]; bool receivedData, receivedRepeat, receivedRelease; diff --git a/remux.c b/remux.c index e48296d..3c7ec6e 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.5 2001/06/24 16:37:23 kls Exp $ */ /* The calling interface of the 'cRemux::Process()' function is defined @@ -62,17 +66,390 @@ */ #include "remux.h" +#include "thread.h" #include "tools.h" -#if defined(REMUX_NONE) +// --- cTS2PES --------------------------------------------------------------- + +#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 + +// Start codes: +#define SC_PICTURE 0x00 // "picture header" + +#define MAXNONUSEFULDATA (10*1024*1024) + +class cTS2PES { +private: + int size; + int found; + int count; + uint8_t *buf; + uint8_t cid; + uint8_t audioCid; + 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, uint8_t AudioCid = 0x00); + ~cTS2PES(); + void ts_to_pes(const uint8_t *Buf); // don't need count (=188) + void Clear(void); + }; + +uint8_t cTS2PES::headr[] = { 0x00, 0x00, 0x01 }; -cRemux::cRemux(void) +cTS2PES::cTS2PES(uint8_t *ResultBuffer, int *ResultCount, int Size, uint8_t AudioCid) { + resultBuffer = ResultBuffer; + resultCount = ResultCount; + size = Size; + audioCid = AudioCid; + + if (!(buf = new uint8_t[size])) + esyslog(LOG_ERR, "Not enough memory for ts_transform"); + + reset_ipack(); +} + +cTS2PES::~cTS2PES() +{ + delete buf; +} + +void cTS2PES::Clear(void) +{ + reset_ipack(); +} + +void cTS2PES::store(uint8_t *Data, int Count) +{ + if (*resultCount + Count > RESULTBUFFERSIZE) { + esyslog(LOG_ERR, "ERROR: result buffer overflow (%d + %d > %d)", *resultCount, Count, RESULTBUFFERSIZE); + Count = RESULTBUFFERSIZE - *resultCount; + } + 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] = (AUDIO_STREAM_S <= cid && cid <= AUDIO_STREAM_E && audioCid) ? audioCid : 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(int VPid, int APid1, int APid2, int DPid1, int DPid2, bool ExitOnFailure) +{ + vPid = VPid; + aPid1 = APid1; + aPid2 = APid2; + dPid1 = DPid1; + dPid2 = DPid2; + exitOnFailure = ExitOnFailure; synced = false; + skipped = 0; + resultCount = resultDelivered = 0; + vTS2PES = new cTS2PES(resultBuffer, &resultCount, IPACKS); + aTS2PES1 = new cTS2PES(resultBuffer, &resultCount, IPACKS, 0xC0); + aTS2PES2 = aPid2 ? new cTS2PES(resultBuffer, &resultCount, IPACKS, 0xC1) : NULL; + dTS2PES1 = dPid1 ? new cTS2PES(resultBuffer, &resultCount, IPACKS) : NULL; + //XXX don't yet know how to tell apart primary and secondary DD data... + dTS2PES2 = /*XXX dPid2 ? new cTS2PES(resultBuffer, &resultCount, IPACKS) : XXX*/ NULL; } cRemux::~cRemux() { + delete vTS2PES; + delete aTS2PES1; + delete aTS2PES2; + delete dTS2PES1; + delete dTS2PES2; +} + +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 +481,140 @@ 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) +void cRemux::SetAudioPid(int APid) { - int Skip = 0; + aPid1 = APid; + vTS2PES->Clear(); + aTS2PES1->Clear(); + resultCount = resultDelivered = 0; +} - PictureType = NO_PICTURE; +const uchar *cRemux::Process(const uchar *Data, int &Count, int &Result, uchar *PictureType) +{ + uchar dummyPictureType; + if (!PictureType) + PictureType = &dummyPictureType; - 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]) { - case SC_VIDEO: +/*XXX + // test recording the raw TS: + Result = Count; + *PictureType = I_FRAME; + return Data; +XXX*/ + + // 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; + int pid = GetPid(Data + i + 1); + if (Data[i + 3] & 0x10) { // got payload + if (pid == vPid) vTS2PES->ts_to_pes(Data + i); + else if (pid == aPid1) aTS2PES1->ts_to_pes(Data + i); + else if (pid == aPid2 && aTS2PES2) aTS2PES2->ts_to_pes(Data + i); + else if (pid == dPid1 && dTS2PES1) dTS2PES1->ts_to_pes(Data + i); + else if (pid == dPid2 && dTS2PES2) dTS2PES2->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 > MAXNONUSEFULDATA) { + 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 VIDEO_STREAM_S ... VIDEO_STREAM_E: { 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! + case PRIVATE_STREAM1: + case AUDIO_STREAM_S ... AUDIO_STREAM_E: + { + 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 bceb676..792469e 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.5 2001/06/23 14:06:59 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,31 +19,32 @@ #define P_FRAME 2 #define B_FRAME 3 -// Start codes: -#define SC_PICTURE 0x00 // "picture header" -#define SC_SEQU 0xB3 // "sequence header" -#define SC_PHEAD 0xBA // "pack header" -#define SC_SHEAD 0xBB // "system header" -#define SC_AUDIO 0xC0 -#define SC_VIDEO 0xE0 - // 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) + +#define RESULTBUFFERSIZE (MINVIDEODATA * 4) typedef unsigned char uchar; +class cTS2PES; class cRemux { private: -#if defined(REMUX_NONE) + bool exitOnFailure; bool synced; + int skipped; + int vPid, aPid1, aPid2, dPid1, dPid2; + cTS2PES *vTS2PES, *aTS2PES1, *aTS2PES2, *dTS2PES1, *dTS2PES2; + uchar resultBuffer[RESULTBUFFERSIZE]; + 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(int VPid, int APid1, int APid2, int DPid1, int DPid2, bool ExitOnFailure = false); ~cRemux(); - const uchar *Process(const uchar *Data, int &Count, int &Result, uchar &PictureType); + void SetAudioPid(int APid); + 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 4870713..8511a1c 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/10 17:11:34 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 1669a49..49be769 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/10 14:00:59 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 5ec3458..afd645f 100755 --- a/runvdr +++ b/runvdr @@ -1,18 +1,44 @@ #!/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.8 2001/07/27 07:35:19 kls Exp $ + DVBDIR="../DVB/driver" VDRPRG="./vdr" -VDRCMD="$VDRPRG -w 60" +VDRCMD="$VDRPRG -w 60 $*" +LSMOD="`/sbin/lsmod | grep -w '^dvb' | wc -l`" KILLPROC="/sbin/killproc -TERM" +# Load driver if it hasn't been loaded already: +if [ $LSMOD -eq 0 ] ; then + (cd $DVBDIR; make insmod) + fi + 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 rmmod; make insmod) + date done diff --git a/svdrp.c b/svdrp.c index df4ffac..a626fda 100644 --- a/svdrp.c +++ b/svdrp.c @@ -10,7 +10,7 @@ * and interact with the Video Disk Recorder - or write a full featured * graphical interface that sits on top of an SVDRP connection. * - * $Id: svdrp.c 1.18 2001/04/01 16:06:54 kls Exp $ + * $Id: svdrp.c 1.20 2001/07/22 13:58:48 kls Exp $ */ #define _GNU_SOURCE @@ -941,7 +941,7 @@ void cSVDRP::Process(void) if (numChars > 0) numChars--; } - else if (c <= 0x03 || c == 0x0D || 0xF0 <= c) { + else if (c <= 0x03 || c == 0x0D) { // ignore control characters } else if (numChars < sizeof(cmdLine) - 1) { @@ -955,8 +955,10 @@ void cSVDRP::Process(void) } lastActivity = time(NULL); } - else if (r < 0) + else if (r <= 0) { + isyslog(LOG_INFO, "lost connection to SVDRP client"); Close(); + } } else if (Setup.SVDRPTimeout && time(NULL) - lastActivity > Setup.SVDRPTimeout) { isyslog(LOG_INFO, "timeout on SVDRP connection"); diff --git a/thread.c b/thread.c index 363190d..e253ea0 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.9 2001/06/27 11:34:41 kls Exp $ */ #include "thread.h" @@ -14,12 +14,43 @@ #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. +time_t cThread::lastPanic = 0; +int cThread::panicLevel = 0; bool cThread::signalHandlerInstalled = false; +bool cThread::emergencyExitRequested = false; cThread::cThread(void) { @@ -110,6 +141,33 @@ void cThread::WakeUp(void) kill(parentPid, SIGIO); // makes any waiting 'select()' call return immediately } +#define MAXPANICLEVEL 10 + +void cThread::RaisePanic(void) +{ + if (lastPanic > 0) { + if (time(NULL) - lastPanic < 5) + panicLevel++; + else if (panicLevel > 0) + panicLevel--; + } + lastPanic = time(NULL); + if (panicLevel > MAXPANICLEVEL) { + esyslog(LOG_ERR, "ERROR: max. panic level exceeded"); + EmergencyExit(true); + } + else + dsyslog(LOG_INFO, "panic level: %d", panicLevel); +} + +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 6aaee0b..bf9804d 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.6 2001/06/27 11:22:04 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,9 @@ private: pid_t parentPid, threadPid, lockingPid; int locked; bool running; + static time_t lastPanic; + static int panicLevel; + static bool emergencyExitRequested; static bool signalHandlerInstalled; static void SignalHandler(int signum); static void *StartThread(cThread *Thread); @@ -45,6 +50,8 @@ public: virtual ~cThread(); bool Start(void); bool Active(void); + static void RaisePanic(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 c303a8a..de1a52c 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.32 2001/04/01 14:13:36 kls Exp $ + * $Id: tools.c 1.34 2001/05/20 08:30:54 kls Exp $ */ #define _GNU_SOURCE @@ -120,7 +120,7 @@ const char *AddDirectory(const char *DirName, const char *FileName) return buf; } -#define DFCMD "df -m '%s'" +#define DFCMD "df -m -P '%s'" uint FreeDiskSpaceMB(const char *Directory) { @@ -132,7 +132,7 @@ uint FreeDiskSpaceMB(const char *Directory) if (p) { char *s; while ((s = readline(p)) != NULL) { - if (*s == '/') { + if (strchr(s, '/')) { uint available; sscanf(s, "%*s %*d %*d %u", &available); Free = available; @@ -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 a9d6121..b4a0cb6 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 8ed209a..12b1a53 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.58 2001/06/23 12:29:41 kls Exp $ */ #include @@ -77,6 +77,7 @@ int main(int argc, char *argv[]) char *Terminal = NULL; static struct option long_options[] = { + { "audio", required_argument, NULL, 'a' }, { "config", required_argument, NULL, 'c' }, { "daemon", no_argument, NULL, 'd' }, { "device", required_argument, NULL, 'D' }, @@ -91,8 +92,10 @@ int main(int argc, char *argv[]) int c; int option_index = 0; - while ((c = getopt_long(argc, argv, "c:dD:hl:p:v:w:t:", long_options, &option_index)) != -1) { + while ((c = getopt_long(argc, argv, "a:c:dD:hl:p:v:w:t:", long_options, &option_index)) != -1) { switch (c) { + case 'a': cDvbApi::SetAudioCommand(optarg); + break; case 'c': ConfigDirectory = optarg; break; case 'd': DaemonMode = true; break; @@ -107,6 +110,7 @@ int main(int argc, char *argv[]) return 2; break; case 'h': printf("Usage: vdr [OPTION]\n\n" // for easier orientation, this is column 80| + " -a CMD, --audio=CMD send Dolby Digital audio to stdin of command CMD\n" " -c DIR, --config=DIR read config files from DIR (default is to read them\n" " from the video directory)\n" " -h, --help display this help and exit\n" @@ -267,6 +271,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 +397,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 +411,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 2824d04..6f35a3d 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; -- cgit v1.2.3