diff options
-rw-r--r-- | CONTRIBUTORS | 80 | ||||
-rw-r--r-- | HISTORY | 186 | ||||
-rw-r--r-- | INSTALL | 13 | ||||
-rw-r--r-- | MANUAL | 47 | ||||
-rw-r--r-- | Makefile | 11 | ||||
-rw-r--r-- | PLUGINS.html | 43 | ||||
-rw-r--r-- | PLUGINS/src/sky/channels.conf.sky | 2 | ||||
-rwxr-xr-x | PLUGINS/src/sky/getskyepg.pl | 4 | ||||
-rw-r--r-- | PLUGINS/src/status/HISTORY | 4 | ||||
-rw-r--r-- | PLUGINS/src/status/status.c | 16 | ||||
-rw-r--r-- | ca.conf | 17 | ||||
-rw-r--r-- | channels.c | 4 | ||||
-rw-r--r-- | channels.conf | 37 | ||||
-rw-r--r-- | channels.h | 10 | ||||
-rw-r--r-- | ci.c | 15 | ||||
-rw-r--r-- | ci.h | 31 | ||||
-rw-r--r-- | config.c | 38 | ||||
-rw-r--r-- | config.h | 27 | ||||
-rw-r--r-- | device.c | 71 | ||||
-rw-r--r-- | device.h | 39 | ||||
-rw-r--r-- | diseqc.c | 6 | ||||
-rw-r--r-- | dvbdevice.c | 264 | ||||
-rw-r--r-- | dvbdevice.h | 4 | ||||
-rw-r--r-- | dvbosd.c | 6 | ||||
-rw-r--r-- | dvbplayer.c | 4 | ||||
-rw-r--r-- | dvbspu.c | 9 | ||||
-rw-r--r-- | dvbspu.h | 4 | ||||
-rw-r--r-- | eit.c | 8 | ||||
-rw-r--r-- | eitscan.c | 4 | ||||
-rw-r--r-- | epg.c | 58 | ||||
-rw-r--r-- | epg.h | 4 | ||||
-rw-r--r-- | genfontfile.c | 4 | ||||
-rw-r--r-- | i18n.c | 568 | ||||
-rw-r--r-- | interface.c | 6 | ||||
-rw-r--r-- | keymacros.conf | 1 | ||||
-rw-r--r-- | keys.c | 3 | ||||
-rw-r--r-- | keys.h | 3 | ||||
-rw-r--r-- | menu.c | 460 | ||||
-rw-r--r-- | menu.h | 17 | ||||
-rw-r--r-- | menuitems.c | 65 | ||||
-rw-r--r-- | menuitems.h | 13 | ||||
-rw-r--r-- | osd.c | 4 | ||||
-rw-r--r-- | osd.h | 3 | ||||
-rw-r--r-- | osdbase.c | 21 | ||||
-rw-r--r-- | osdbase.h | 5 | ||||
-rw-r--r-- | player.c | 12 | ||||
-rw-r--r-- | player.h | 4 | ||||
-rw-r--r-- | plugin.c | 4 | ||||
-rw-r--r-- | rcu.c | 92 | ||||
-rw-r--r-- | rcu.h | 18 | ||||
-rw-r--r-- | recorder.c | 6 | ||||
-rw-r--r-- | recording.c | 122 | ||||
-rw-r--r-- | recording.h | 7 | ||||
-rw-r--r-- | remote.c | 99 | ||||
-rw-r--r-- | remote.h | 4 | ||||
-rw-r--r-- | remux.c | 105 | ||||
-rw-r--r-- | ringbuffer.c | 10 | ||||
-rw-r--r-- | ringbuffer.h | 4 | ||||
-rwxr-xr-x | runvdr | 8 | ||||
-rw-r--r-- | skinclassic.c | 9 | ||||
-rw-r--r-- | skins.c | 4 | ||||
-rw-r--r-- | skins.h | 4 | ||||
-rw-r--r-- | skinsttng.c | 9 | ||||
-rw-r--r-- | status.c | 10 | ||||
-rw-r--r-- | status.h | 24 | ||||
-rw-r--r-- | svdrp.c | 126 | ||||
-rw-r--r-- | svdrp.h | 4 | ||||
-rw-r--r-- | thread.c | 36 | ||||
-rw-r--r-- | thread.h | 10 | ||||
-rw-r--r-- | timers.c | 32 | ||||
-rw-r--r-- | timers.h | 20 | ||||
-rw-r--r-- | tools.c | 202 | ||||
-rw-r--r-- | tools.h | 38 | ||||
-rw-r--r-- | vdr.1 | 22 | ||||
-rw-r--r-- | vdr.5 | 37 | ||||
-rw-r--r-- | vdr.c | 214 | ||||
-rw-r--r-- | videodir.c | 8 |
77 files changed, 2408 insertions, 1135 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS index c4173a0..d524a30 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -16,6 +16,8 @@ Carsten Koch <Carsten.Koch@icem.de> for fixing converting summary.vdr files that would result in a very long 'short text' for his help in testing and debugging reading the list of recordings in a separate thread + for reporting some unnecessary disk access when checking if there are deleted + recordings that need to be removed Plamen Ganev <pganev@com-it.net> for fixing the frequency offset for Hotbird channels @@ -310,6 +312,7 @@ Andreas Share <a.share@t-online.de> for his support in keeping the Premiere World channels up to date in 'channels.conf' for pointing out that section filters should only be set if the device actually has a lock + for reporting a lockup with the RCU on NPTL systems Simon Bauschulte <SemiSchwabe@Brutzel.de> for his support in keeping the Premiere World channels up to date in 'channels.conf' @@ -480,6 +483,8 @@ Oliver Lorei <oliverlorei@cityweb.de> Andreas Böttger <fboettger@t-online.de> for reporting a bug in skipping forward in time shift mode near the end of the recording for fixing setting system time to avoid time jumps in case of faulty data + for reporting a bug in the "Day" field of the "Edit timer" menu when pressing + '0' to switch from "single shot" to "weekly", followed by the "Right" key Onno Kreuzinger <ok@solutas.net> for reporting leftover references to the file FORMATS in MANUAL and svdrp.c @@ -517,8 +522,9 @@ Christian Rienecker <C.Rienecker@gmx.net> Joerg Riechardt <J.Riechardt@gmx.de> for filling in some missing teletext PIDs -Holger Wächtler <holger@convergence.de> +Holger Wächtler <holger@qanu.de> for some valuable advice during adapting to the NEWSTRUCT driver + for suggesting to use FE_READ_STATUS to read the current frontend status Jürgen Zimmermann <jnzimmer@informatik.uni-kl.de> for adding some missing #includes to files in libdtv for gcc 3.2 @@ -531,6 +537,7 @@ Helmut Auer <vdr@helmutauer.de> for implementing a default cRemote::Initialize() for suggesting to increase the default value for 'Min. user inactivity' to 300 minutes for suggesting to add cChannel::LinkChannels() and cChannel::RefChannel() + for suggesting to give a message when an instant recording is started Jeremy Hall <jhall@UU.NET> for fixing an incomplete initialization of the filter parameters in eit.c @@ -821,6 +828,7 @@ Ludwig Nussel <ludwig.nussel@web.de> cThread::Start() for removing the LOCK_THREAD from the LIRC thread for making the Makefile patch friendlier + for a patch that was used for implementing setting the user id Thomas Koch <tom@harhar.net> for his support in keeping the Premiere World channels up to date in 'channels.conf' @@ -1007,6 +1015,11 @@ Reinhard Nissl <rnissl@gmx.de> for suggesting to always use stream id 0xE0 for the video stream, to avoid problems with post processing tools that choke on different ids for fixing cDvbPlayer::SkipFrames() to properly handle radio recordings + for improving TS/PES conversion to better handle lost TS packets + for suggesting to make the DVB devices retune (and, if applicable, resend the + DiSEqC data) if the lock is lost + for fixing handling TS packets in cTS2PES + for adding a mutex to synchronize cDevice::PlayPesPacket() and SetCurrentAudioTrack() Richard Robson <richard_robson@beeb.net> for reporting freezing replay if a timer starts while in Transfer Mode from the @@ -1252,6 +1265,8 @@ Marco Schlüßler <marco@lordzodiac.de> for fixing initializing the day index when editing the weekday parameter of a repeating timer for figuring out some obscure length bytes the the CA PMT Reply data of AlphaCrypt CAMs + for fixing handling OSD areas that have invalid sizes + for removing unused variables in skinclassic.c and skinsttng.c Jürgen Schmitz <j.schmitz@web.de> for reporting a bug in displaying the current channel when switching via the SVDRP @@ -1307,6 +1322,7 @@ Udo Richter <udo_richter@gmx.de> for reporting a bug in opening recording folders in case the last replayed recording no longer exists for reporting a missing check against MAXOSDAREAS in cOsd::CanHandleAreas() + for making the Makefile report a summary of failed plugins Sven Kreiensen <svenk@kammer.uni-hannover.de> for his help in keeping 'channels.conf.terr' up to date @@ -1318,6 +1334,7 @@ Lucian Muresan <lucianm@users.sourceforge.net> for updating the Romanian language texts and the iso8859-2 fonts for making VDR actually use the iso8859-15 fonts for suggesting to make the function ExchangeChars() + for reporting duplicate texts in i18n.c Mattias Grönlund <Mattias@Gronlund.net> for pointing out a missing cleanup at program exit in case there is a problem @@ -1346,6 +1363,8 @@ Andreas Brugger <brougs78@gmx.net> for reporting the missing Euro sign in iso8859-1 for reporting a problem with making changes to timers through SVDRP while they are being edited via the menu + for suggesting to change the API of the functions cStatus::Recording() and + cStatus::Replaying(), so that they can provide the full file name of the recording Dino Ravnic <dino.ravnic@fer.hr> for fixing some characters in the iso8859-2 font file @@ -1364,6 +1383,9 @@ Darren Salt <linux@youmustbejoking.demon.co.uk> for a patch that was used to add the command line options '--lirc', '--rcu' and '--no-kbd' for adding '__attribute__' to functions that use printf() like parameters + for suggesting to write grabbed images to the SVDRP connection encoded in base64 + for suggesting to open the file handle in the SVDRP GRAB command in a way that + it won't follow symbolic links, and to canonicalize the file name Sean Carlos <seanc@libero.it> for translating OSD texts to the Italian language @@ -1416,6 +1438,7 @@ Chris Warren <dvb@ixalon.net> Luca Olivetti <luca@ventoso.org> for making cDevice::AttachPlayer() keep the track language codes and descriptions in Transfer Mode + for suggesting to make the "Menu" key behave consistently Mikko Salo <mikko.salo@ppe.inet.fi> for suggesting to make the setup option "DVB/Video display format" available only @@ -1432,6 +1455,10 @@ Ville Skyttä <ville.skytta@iki.fi> for making LIRC command parsing more robust for fixing some typos in MANUAL for reporting that the default value for "Setup/EPG bugfix level" was wrong + for fixing initializing pthread_mutexattr_t and pthread_rwlockattr_t to avoid + warnings with g++ 4.1.0 + for reporting warnings with g++ 4.1.0 regarding incrementing the 'state' variables + in the repacker classes in remux.c Steffen Beyer <cpunk@reactor.de> for fixing setting the colored button help after deleting a recording in case the next @@ -1453,6 +1480,8 @@ Michael Reinelt <reinelt@eunet.at> Johannes Stezenbach <js@linuxtv.org> for pointing out that the byte swap for big endian systems in cDvbOsd::Flush() is wrong + for suggesting to use gettid() syscall to get a thread's pid, so that we get a + useful value on NPTL systems Paavo Hartikainen <pahartik@sci.fi> for verifying that the byte swap for big endian systems in cDvbOsd::Flush() was @@ -1538,6 +1567,10 @@ Nicolas Huillard <nhuillard@e-dition.fr> Patrick Fischer <patrick_fischer@gmx.de> for reporting an error in the cFilter example in PLUGINS.html + for making the static cControl functions thread safe + for suggesting that the cTimer constructor should take an optional cChannel + for suggesting that any cReceivers still attached to a cDevice when that device + switches to a different transponder shall be automatically detached Ralf Müller <ralf@bj-ig.de> for a patch that was used to implement cUnbufferedFile @@ -1547,3 +1580,48 @@ Maarten Wisse <Maarten.Wisse@urz.uni-hd.de> Holger Brunn <holger.brunn@stud.uni-karlsruhe.de> for adding a copy constructor to cString and fixing its assignment operator + +Christian Vogt <c.v@nexgo.de> + for suggesting to take deleted recordings into account when displaying the + amount of free disk space + +Christof Steininger <christof.steininger@t-online.de> + for fixing a possible crash when displaying the "Low disk space!" message from + a background thread + +Kendy Kutzner <kutzner@ira.uka.de> + for making the version number of EPG events be stored in the epg.data file + for suggesting to add cTimer::SetPriority() to set a timer's priority + +Bob Withers <bwit@pobox.com> + for publishing a Base64 encoder at http://www.ruffboy.com/download.htm + (http://www.ruffboy.com/code/Base64.zip), part of which was used when writing + the cBase64Encoder class + +Javier Fernández-Sanguino Peña <jfs@computer.org> + for reporting a security hole in the way the SVDRP command GRAB writes the + image file + +Jürgen Schneider <ivory7@gmx.de> + for a patch that was used as a base to fix handling multi byte key sequences + in cKbdRemote + +Christian Wieninger <cwieninger@gmx.de> + for suggesting to add cMenuEditStrItem::InEditMode() + +Thiemo Gehrke <tgehrke@reel-multimedia.com> + for suggesting to add a setup option to turn off the automatic timeout of the + channel display in case it was invoked by a press of the "Ok" key + +Gavin Hamill <gdh@acentral.co.uk> + for reporting a missing #include "thread.h" in dvbspu.c + +Petri Hintukainen <Petri.Hintukainen@hut.fi> + for suggesting to disable the use of "fadvise" in cUnbufferedFile because there + have been several reports that it causes more problems than it solves + +Marcel Schaeben <mts280@gmx.de> + for his "Easy Input" patch + +Ingo Schneider <mail@ingo-schneider.de> + for adding a SleepMs() in cRecorder::Action() to avoid a busy loop @@ -1491,9 +1491,9 @@ Video Disk Recorder Revision History --- Makefile 2002/06/10 16:24:06 1.4 +++ Makefile 2002/09/17 15:36:36 1.5 @@ -15,7 +15,12 @@ - + ### The directory environment: - + +ifdef NEWSTRUCT +DVBDIR = ../../../../DVB/include +DEFINES += -DNEWSTRUCT @@ -1504,12 +1504,12 @@ Video Disk Recorder Revision History VDRINC = $(VDRDIR)/include LIBDIR = ../../lib @@ -34,7 +39,7 @@ - + INCLUDES = -I$(VDRINC) -I$(DVBDIR) - + -DEFINES = -DPLUGIN_NAME_I18N='"$(PLUGIN)"' +DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' - + ### The object files (add further files here): ------------------------------------------------------- @@ -2705,7 +2705,7 @@ Video Disk Recorder Revision History - Timers can now be set to use the VPS information to control recording a programme. The new setup options "Recording/Use VPS" and "Recording/VPS margin", as well as the "VPS" option in the individual timers, can be used to control this feature - (see MANUAL for details). + (see MANUAL for details). Note that this feature will certainly need a lot of testing before it can be called "safe"! - The "Schedule" and "What's on now/next?" menus now have an additional column @@ -3270,7 +3270,7 @@ Video Disk Recorder Revision History botton left side. - The new setup option "DVB/Audio languages" can be used to control which audio language shall be selected in case a channel broadcasts in different languages - (see MANUAL for details). + (see MANUAL for details). - The "Left" and "Right" keys in the "Audio" menu can be used to switch between the left and right stereo channels in case there are different audio tracks in these channels (see MANUAL for details). @@ -3962,3 +3962,175 @@ Video Disk Recorder Revision History - The SVDRP command MESG uses the new message queueing facility, so MESG commands may now be executed at any time, and the message will be displayed (no more "pending message"). + +2006-01-08: Version 1.3.38 + +- Fixed handling second audio and Dolby Digital PIDs for encrypted channels + (was broken in version 1.3.37). +- Improved TS/PES conversion to better handle lost TS packets (thanks to + Reinhard Nissl). +- Limited the frequency of log messages from the cRepackers. +- Now using the gettid() syscall to get a thread's pid, so that we get a + useful value on NPTL systems (suggested by Johannes Stezenbach). +- Fixed the RCU remote control handling to avoid problems with NPTL (thanks + to Andreas Share for reporting a lockup with the RCU on NPTL systems). +- When displaying the amount of free disk space, the space consumed by + recordings that have been "deleted" but not yet actually "removed" is now + taken into account (suggested by Christian Vogt). +- Now avoiding unnecessary disk access when checking if there are deleted + recordings that need to be removed (reported by Carsten Koch). +- Fixed handling the DELETEDLIFETIME when removing deleted recordings. Now + a deleted recording is retained at least DELETEDLIFETIME seconds before + actually removing it. + The value of DELETEDLIFETIME has been changed to 300. So after (possibly + inadvertently) deleting a recording, there will be at least 5 minutes + in which it can be recovered (unless a new recording immediately requires + the disk space). The count starts again at 0 every time VDR is started. +- Fixed a possible crash when displaying the "Low disk space!" message from + a background thread (thanks to Christof Steininger). +- Fixed handling OSD areas that have invalid sizes (thanks to Marco Schlüßler). +- Added a mutex to AssertFreeDiskSpace() to make sure calls from foreground + and background threads won't interfere. +- The main menu now dynamically updates its contents in case an instant + recording or replay stops, etc. +- The version number of EPG events is now also stored in the epg.data file + (thanks to Kendy Kutzner). +- EPG events that are no longer in the currently broadcasted data stream are + now automatically deleted. +- Removed an invalid access to Event->schedule in cSchedule::DelEvent(). +- Modified cSchedule::Cleanup() (events are always sorted by time). +- Schedules are now cleaned up once every hour (not only at 05:00). +- The "Schedule" and "What's on now/next?" menus are now updated if a timer + is set or modified. +- cTimer no longer has its own 'schedule' member, it rather uses that of the + event it has been set to. +- The "Red" button in the "Schedule", "What's on now/next?" and "Event" menus + now immediately creates a timer for the selected event and marks it with 'T'. + If the event is already marked with 'T', the "Red" button opens the "Edit + timer" menu for that timer. +- Removing deleted recordings is now done in a separate thread. +- Dropped the unused "stop recording on primary interface" stuff. +- Converting a grabbed image to JPEG is now done with the new function + RgbToJpeg() (see tools.h). +- The SVDRP command GRAB now determines the image type (JPEG or PNM) from the + extension (".jpg", ".jpeg" or ".pnm") of the given file name. The explicit + 'jpeg' or 'pnm' parameter is still accepted for backward compatibility, but + has no meaning any more. +- The function cDevice::GrabImage() no longer writes the grabbed image to a + file, but rather returns a pointer to the image in memory. The wrapper + function cDevice::GrabImageFile() can be used to write the grabbed image + directly to a file. Plugins that used the old version of cDevice::GrabImage() + need to be adapted to the new interface. +- The new class cBase64Encoder (see tools.h) can be used to encode data in + base64 (thanks to Bob Withers for publishing his Base64 class). +- The SVDRP command GRAB now writes the image data to the SVDRP connection + (encoded in base64) if the given file name consists of only the file + extension (".jpg", ".jpeg" or ".pnm"), or if only "-" is given as file + name (based on a suggestion from Darren Salt). + A simple way of viewing a grabbed image on a remote host is: + + svdrpsend.pl -d <hostname> 'grab -' | sed -n -e 's/^216-//p' -e '1ibegin-base64 644 -' -e '$a====' | uudecode | display + +- The new command line option '-g' must be given if the SVDRP command GRAB + shall be allowed to write image files to disk. The parameter to this option + must be the full path name of an existing directory, without any "..", double + '/' or symlinks. By default, or if "-g- is given, grabbing to files is + not allowed any more because of potential security risks. +- Modified the way the SVDRP command GRAB writes the grabbed image to a file + to avoid a security hole (CAN-2005-0071, reported by Javier Fernández-Sanguino + Peña): + + The file handle is now opened in a way that it won't follow symbolic links + (suggested by Darren Salt). + + The given file name is now canonicalized, so that it won't contain any + ".." or symlinks (suggested by Darren Salt). + + Grabbing to files is limited to the directory given in the the command + line option '-g'. By default grabbing to files is not allowed any more. +- Updated the Greek OSD texts (thanks to Dimitrios Dimitrakos). +- Changed all "illegal" to "invalid" in error messages (there's nothing "illegal" + in VDR ;-). +- When started as user 'root' VDR now switches to a lesser privileged user id, + keeping the capability to set the system time (based on a patch from Ludwig + Nussel). By default the user id 'vdr' is used, which can be changed through + the new command line option '-u'. Note that for security reasons VDR will no + longer run as user 'root' (unless you explicitly start it with '-u root', + but this is not recommended!). The 'runvdr' script has been changed to + use the '-u' option. +- Changed the API of the functions cStatus::Recording() and cStatus::Replaying(), + so that they can provide the full file name of the recording. Plugins that use + these (or the related cStatus::Msg...() functions) need to be adapted + (suggested by Andreas Brugger). +- The DVB devices now retune (and, if applicable, resend the DiSEqC data) if + the lock is lost (based on a patch from Reinhard Nissl). +- Fixed handling multi byte key sequences in cKbdRemote (based on a patch from + Jürgen Schneider). +- Removed unused variables in skinclassic.c and skinsttng.c (thanks to Marco + Schlüßler). +- Made the static cControl functions thread safe (thanks to Patrick Fischer). +- Fixed initializing pthread_mutexattr_t and pthread_rwlockattr_t to avoid + warnings with g++ 4.1.0 (thanks to Ville Skyttä). +- Fixed incrementing the 'state' variables in the repacker classes in remux.c + to avoid warnings with g++ 4.1.0 (reported by Ville Skyttä). +- The Makefile now reports a summary of failed plugins (thanks to Udo Richter). +- The cTimer constructor can now take an optional cChannel (suggested by + Patrick Fischer). +- Fixed setting the main thread id if VDR is running as a daemon. +- Fixed handling TS packets in cTS2PES (thanks to Reinhard Nissl). +- Added cTimer::SetPriority() to set a timer's priority (suggested by Kendy Kutzner). +- Added cMenuEditStrItem::InEditMode() (suggested by Christian Wieninger). +- Now using FE_READ_STATUS to read the current frontend status (suggested by + Holger Wächtler). +- The "Menu" key now behaves consistently. If there is anything on the OSD, it + is closed when the "Menu" key is pressed, and if there is nothing on the OSD, + the "Menu" key opens the main menu (suggested by Luca Olivetti). +- The new option "Setup/OSD/Timeout requested channel info" can be used to turn + off the automatic timeout of the channel display in case it was invoked by + a press of the "Ok" key (suggested by Thiemo Gehrke). +- A message is now given when an instant recording is started (suggested by + Helmut Auer). Actually the code was already there, just commented out - don't + remember why it wasn't active... +- Removed an obsolete "Summary" text from i18n.c and preceded all key definition + texts with "Key$" to avoid duplicates (reported by Lucian Muresan). +- Preceded all button texts with "Button$". +- Removed obsolete "Eject", "Language" and "scanning recordings..." texts. +- Added missing #include "thread.h" to dvbspu.c (reported by Gavin Hamill). +- Disabled the use of "fadvise" in cUnbufferedFile because there have been + several reports that it causes more problems than it solves (suggested by + Petri Hintukainen). If you want to use "fadvise", you can activate the line + //#define USE_FADVISE + in tools.c. +- Removed unused 'offset' member from cOsdItem. +- In the "Channels" menu the numeric keys now position the cursor to the channel + with the given number (see MANUAL, section "Remote Control Keys", note (3) for + details). +- The "Mark/Move" function in the "Channels" menu now also works in the non-numeric + sort modes. +- The default cOsdObject::Show() now automatically calls cOsdMenu::Display() if + this is a menu. +- The new "Info" key brings up information on the currently viewed programme + or recording. For a live programme this is the same as "Schedule/Ok", i.e. the + description of the current EPG event. For a recording this is the same as shown + by the "Info" button in the "Recordings" menu. Plugins that implement players + can overwrite their cControl::GetInfo() function to show their own info (see + PLUGINS.html for details). Pressing the "Info" key again while the info is + displayed will close the OSD. In order to assign this new key to an existing + remote control setup, the remote.conf file needs to be deleted and VDR has + to be restarted to go through the process of learning the remote control keys. +- Any cReceivers still attached to a cDevice when that device switches to a + different transponder are now automatically detached (suggested by Patrick + Fischer). +- The "flags" of a timer are now handled as an unsigned integer value. In order + to do this, the interface of cMenuEditBitItem also had to be changed. +- In string entry fields (like, e.g., the file name of a recording) the characters + can now be entered by pressing the numeric keys, the same way as on a + telephone keypad (based on the "Easy Input" patch from Marcel Schaeben). +- Fixed the "Day" field of the "Edit timer" menu when pressing '0' to switch + from "single shot" to "weekly", followed by the "Right" key (reported by + Andreas Böttger). +- The file 'ca.conf' is obsolete and has been removed. +- Revised all descriptions regarding CICAM. +- Adapted c(Dvb)Device::ProvidesCa() to the dynamic CA handling. +- Added a mutex to synchronize cDevice::PlayPesPacket() and SetCurrentAudioTrack() + (thanks to Reinhard Nissl). +- Added a SleepMs() in cRecorder::Action() to avoid a busy loop (thanks to Ingo + Schneider). +- Cleaned up some trailing white space. @@ -132,6 +132,19 @@ 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! +Setting the system time: +------------------------ + +If you want VDR to set the system time according to the data received +from the transponder, you need to start VDR as user 'root'. VDR will +then only keep the capability to set the system time, and set its +user id to a lesser privileged one ('vdr' by default, can be set +to a different value with the '-u' option). +You also need to enable the "EPG/Set system time" option in VDR's +Setup menu, and select a transponder from which you want to receive +the time in "Use time from transponder". Make sure you select a transponder +that has a reliable clock - some transponders are quite off. + Automatic shutdown: ------------------- @@ -1,7 +1,7 @@ Video Disk Recorder User's Manual --------------------------------- -Version 1.2 +Version 1.3 ----------- * Remote Control Keys @@ -18,7 +18,7 @@ Version 1.2 Left Prev group - Page up Page up Decrement Page up Search back Sel. channel Right Next group - Page down Page down Increment Page down Search forward Sel. channel Ok Ch display Select Switch Edit Accept Play Progress disp. Switch & Close - Menu Menu on Menu off Menu off Menu off Menu off Menu off Menu on Menu on + Menu Menu on Menu off Menu off Menu off Menu off Menu off Menu off Menu off Back - Menu off VDR menu VDR menu Discard VDR menu Recordings menu Close Red - Record Edit Edit ABC/abc Play/Commands(2) Jump - Green - Audio New New Ins/Ovr Rewind Skip -60s - @@ -30,9 +30,14 @@ Version 1.2 are used to enter the data, and the Left key can be used to delete the last entered digit. + In a text input field (like, e.g., the file name of a recording) the characters + can be entered by pressing the numeric keys, the same way as on a telephone + keypad. + If your remote control provides additional keys, they can be used for the following functions: + Info display information on the currently viewed programme or recording Play resume normal replay Pause pause replay or live video Stop stop replay @@ -74,7 +79,12 @@ Version 1.2 to "mark" a timer for moving. (2) See "Processing Recordings" below. (3) In the "Channels" menu the '0' key switches the sort mode through "by number", - "by name" and "by provider". + "by name" and "by provider". Other numeric input positions the cursor to + the channel with the number entered so far. If there is no channel with that + number, nothing happens. While entering a channel number, the '0' key will + be treated as part of that number, not as a sort mode toggle. If no numeric + key has been pressed for more than one second, the number is reset and '0' + functions as sort mode toggle again. (4) In the "Timers" menu, when on the "Day" item, the '0' key toggles between a single shot and a repeating timer. If "Day" indicates a repeating timer, the keys '1'...'7' can be used to toggle the individual days ('1' is Monday). @@ -141,14 +151,17 @@ Version 1.2 "Schedule" menu of the current channel in the list. The "Red" button allows you to instantly program a timer to record the - selected programme. You will get into the "Edit Timer" menu in which - everything has already been filled in, and you can make any modifications + selected programme. After pressing this button, the current event will + be marked with 'T', and the function of the "Red" button will change from + "Record" to "Timer". Pressing "Red" on an event marked with 'T' will open + the "Edit timer" menu for this timer, where you can make any modifications you may want to apply. Note that the Start and Stop time are offset by the MarginStart and MarginStop parameters (see Setup) in order to make sure the entire programme is recorded in case it doesn't exactly adhere to its published start/stop times. Of course, no guarantee can be given that the default margin values will be sufficient, so in case this recording is - really important you may want to add an extra margin ;-) + really important you may want to add an extra margin ;-). VPS recordings + will use the exact Start (or VPS) and Stop times as given in the event. The "Blue" button can be pressed to switch to the channel with the selected programme. @@ -373,7 +386,7 @@ Version 1.2 * Programming the Timer Use the "Timer" menu to maintain your list of timer controlled recordings. - The parameters in the "Edit Timer" menu have the following meanings: + The parameters in the "Edit timer" menu have the following meanings: Active: Defines whether the timer will be processed (set it to 'no' to temporarily disable a timer). @@ -493,6 +506,10 @@ Version 1.2 always displayed when pressing the "Ok" button in normal viewing mode. + Timeout requested channel info = yes + Turns the automatic timeout of the channel display (when + invoked by a press of the "Ok" key) on or off. + Scroll pages = yes no = when pressing the "Down" ("Up") key while the cursor is on the last (first) line of a list page, the list is scrolled down (up) a single line and the cursor will @@ -639,17 +656,11 @@ Version 1.2 CICAM: - CICAM DVBn m Defines the "Conditional Access" capabilities of the DVB - card 'n'. Each DVB card can provide up to two CICAM - methods ('m' = [1, 2]). - - In the 'setup.conf' file the value consists of the card - number, followed by a list of decryption method values - (defined in 'ca.conf'). - For instance - CaCaps = 3 101 102 - would define that card number 3 is able to decrypt - "Premiere World" and the "ORF". + CICAM DVBn m Shows the CAMs that each device contains, where 'n' is + the number of the device, and 'm' is the number of the + Common Interface slot of that device. The "Red" key + can be pressed to enter the CAM menu, and the "Green" key + triggers a reset of the selected CAM. Recording: @@ -4,7 +4,7 @@ # See the main source file 'vdr.c' for copyright information and # how to reach the author. # -# $Id: Makefile 1.79 2005/09/02 14:23:38 kls Exp $ +# $Id: Makefile 1.81 2006/01/01 15:12:05 kls Exp $ .DELETE_ON_ERROR: @@ -27,7 +27,7 @@ endif LSIDIR = ./libsi MANDIR = /usr/local/man BINDIR = /usr/local/bin -LIBS = -ljpeg -lpthread -ldl +LIBS = -ljpeg -lpthread -ldl -lcap INCLUDES = PLUGINDIR= ./PLUGINS @@ -183,7 +183,12 @@ include-dir: # Plugins: plugins: include-dir - @for i in `ls $(PLUGINDIR)/src | grep -v '[^a-z0-9]'`; do $(MAKE) -C "$(PLUGINDIR)/src/$$i" all; done + @failed="";\ + for i in `ls $(PLUGINDIR)/src | grep -v '[^a-z0-9]'`; do\ + echo "Plugin $$i:";\ + $(MAKE) -C "$(PLUGINDIR)/src/$$i" all || failed="$$failed $$i";\ + done;\ + if [ -n "$$failed" ] ; then echo; echo "*** failed plugins:$$failed"; echo; fi plugins-clean: @for i in `ls $(PLUGINDIR)/src | grep -v '[^a-z0-9]'`; do $(MAKE) -C "$(PLUGINDIR)/src/$$i" clean; done diff --git a/PLUGINS.html b/PLUGINS.html index bdaf1ef..bec5127 100644 --- a/PLUGINS.html +++ b/PLUGINS.html @@ -9,23 +9,23 @@ <center><b>Version 1.3</b></center> <p> <center> -Copyright © 2005 Klaus Schmidinger<br> +Copyright © 2006 Klaus Schmidinger<br> <a href="mailto:kls@cadsoft.de">kls@cadsoft.de</a><br> <a href="http://www.cadsoft.de/vdr">www.cadsoft.de/vdr</a> </center> <p> -<!--X1.3.21--><table width=100%><tr><td bgcolor=#0000AA> </td><td width=100%> -Important modifications introduced in version 1.3.21 are marked like this. -<!--X1.3.21--></td></tr></table> -<!--X1.3.30--><table width=100%><tr><td bgcolor=#00AA00> </td><td width=100%> +<!--X1.3.30--><table width=100%><tr><td bgcolor=#0000AA> </td><td width=100%> Important modifications introduced in version 1.3.30 are marked like this. <!--X1.3.30--></td></tr></table> -<!--X1.3.31--><table width=100%><tr><td bgcolor=#AA0000> </td><td width=100%> +<!--X1.3.31--><table width=100%><tr><td bgcolor=#00AA00> </td><td width=100%> Important modifications introduced in version 1.3.31 are marked like this. <!--X1.3.31--></td></tr></table> -<!--X1.3.37--><table width=100%><tr><td bgcolor=#FF0000> </td><td width=100%> +<!--X1.3.37--><table width=100%><tr><td bgcolor=#AA0000> </td><td width=100%> Important modifications introduced in version 1.3.37 are marked like this. <!--X1.3.37--></td></tr></table> +<!--X1.3.38--><table width=100%><tr><td bgcolor=#FF0000> </td><td width=100%> +Important modifications introduced in version 1.3.38 are marked like this. +<!--X1.3.38--></td></tr></table> <p> VDR provides an easy to use plugin interface that allows additional functionality to be added to the program by implementing a dynamically loadable library file. @@ -66,10 +66,10 @@ structures and allows it to hook itself into specific areas to perform special a <li><a href="#The Setup menu">The Setup menu</a> <li><a href="#Configuration files">Configuration files</a> <li><a href="#Internationalization">Internationalization</a> -<!--X1.3.30--><table width=100%><tr><td bgcolor=#00AA00> </td><td width=100%> +<!--X1.3.30--><table width=100%><tr><td bgcolor=#0000AA> </td><td width=100%> <li><a href="#Custom services">Custom services</a> <!--X1.3.30--></td></tr></table> -<!--X1.3.31--><table width=100%><tr><td bgcolor=#AA0000> </td><td width=100%> +<!--X1.3.31--><table width=100%><tr><td bgcolor=#00AA00> </td><td width=100%> <li><a href="#SVDRP commands">SVDRP commands</a> <!--X1.3.31--></td></tr></table> <li><a href="#Loading plugins into VDR">Loading plugins into VDR</a> @@ -85,9 +85,7 @@ structures and allows it to hook itself into specific areas to perform special a <li><a href="#Skins">Skins</a> <li><a href="#Themes">Themes</a> <li><a href="#Devices">Devices</a> -<!--X1.3.21--><table width=100%><tr><td bgcolor=#0000AA> </td><td width=100%> <li><a href="#Audio">Audio</a> -<!--X1.3.21--></td></tr></table> <li><a href="#Remote Control">Remote Control</a> </ul> </ul> @@ -520,7 +518,7 @@ virtual void Stop(void); in which it shall stop them. <p> -The <tt>Stop()</tt> function will only be called if a previous call to the +The <tt>Stop()</tt> function will only be called if a previous call to the <a href="#Getting started"><tt>Start()</tt></a> function of that plugin has returned <i>true</i>. The <tt>Stop()</tt> functions are called in the reverse order as the <a href="#Getting started"><tt>Start()</tt></a> functions were called. @@ -866,7 +864,7 @@ Texts are first searched for in the <i>Phrases</i> registered for this plugin (i and then in the global VDR texts. So a plugin can make use of texts defined by the core VDR code. -<!--X1.3.30--><table width=100%><tr><td bgcolor=#00AA00> </td><td width=100%> +<!--X1.3.30--><table width=100%><tr><td bgcolor=#0000AA> </td><td width=100%> <a name="Custom services"><hr><h2>Custom services</h2> <center><i><b>What can I do for you?</b></i></center><p> @@ -937,7 +935,7 @@ any plugin handled the request, or <tt>false</tt> if no plugin handled the reque <!--X1.3.30--></td></tr></table> -<!--X1.3.31--><table width=100%><tr><td bgcolor=#AA0000> </td><td width=100%> +<!--X1.3.31--><table width=100%><tr><td bgcolor=#00AA00> </td><td width=100%> <a name="SVDRP commands"><hr><h2>SVDRP commands</h2> <center><i><b>Infinite Diversity in Infinite Combinations</b></i></center><p> @@ -1264,6 +1262,9 @@ public: cMyControl(void); virtual ~cMyControl(); virtual void Hide(void); +<!--X1.3.38--><table width=100%><tr><td bgcolor=#FF0000> </td><td width=100%> + virtual cOsdObject *GetInfo(void); +<!--X1.3.38--></td></tr></table> virtual eOSState ProcessKey(eKeys Key); }; </pre></td></tr></table><p> @@ -1292,8 +1293,14 @@ to make the main program loop shut down the player control. A derived <tt>cControl</tt> <b>must</b> implement the <tt>Hide()</tt> function, in which it has to hide itself from the OSD, in case it uses it. <tt>Hide()</tt> may be called at any time, and it may be called even if the <tt>cControl</tt> is not visible at the moment. -The reason for this is that the <tt>Menu</tt> button shall always bring up the main VDR -menu, so any active <tt>cControl</tt> needs to be hidden when that button is pressed. +<p> +<!--X1.3.38--><table width=100%><tr><td bgcolor=#FF0000> </td><td width=100%> +The <tt>GetInfo()</tt> function is called when the user presses the <tt>Info</tt> button, +and shall return a pointer to a <tt>cOsdObject</tt> that contains information +about the currently played programme. The caller takes ownership of the returned +pointer and will delete it when it is no longer used. If no information is available, +<tt>NULL</tt> shall be returned. +<!--X1.3.38--></td></tr></table> <p> Finally, to get things going, a plugin that implements a player (and the surrounding infrastructure like displaying a list of playable stuff etc) simply has to call the @@ -1515,7 +1522,7 @@ with the full required resolution. Only if this fails shall it use alternate areas. Drawing areas are always rectangular and may not overlap (but do not need to be adjacent). -<!--X1.3.37--><table width=100%><tr><td bgcolor=#FF0000> </td><td width=100%> +<!--X1.3.37--><table width=100%><tr><td bgcolor=#AA0000> </td><td width=100%> <p> Directly accessing the OSD is only allowed from the foreground thread, which restricts this to a <tt>cOsdObject</tt> returned from the plugin's <tt>MainMenuAction()</tt> @@ -1840,9 +1847,7 @@ private: virtual void Action(void); public: cMyAudio(void); -<!--X1.3.21--><table width=100%><tr><td bgcolor=#0000AA> </td><td width=100%> virtual void Play(const uchar *Data, int Length, uchar Id); -<!--X1.3.21--></td></tr></table> virtual void Mute(bool On); virtual void Clear(void); }; diff --git a/PLUGINS/src/sky/channels.conf.sky b/PLUGINS/src/sky/channels.conf.sky index 52d1a87..5f0b773 100644 --- a/PLUGINS/src/sky/channels.conf.sky +++ b/PLUGINS/src/sky/channels.conf.sky @@ -17,7 +17,7 @@ # page exists, 'x' is entered. # S28.2E-2-2027-4705:106:sky_one -S28.2E-2-2027-5104:107:sky_one_mix +S28.2E-2-2027-5104:107:sky_two S28.2E-2-2044-10070:118:itv2 S28.2E-2-2023-4905:130:scifi S28.2E-2-2025-5904:127:paramount diff --git a/PLUGINS/src/sky/getskyepg.pl b/PLUGINS/src/sky/getskyepg.pl index 77768ad..26dbc0a 100755 --- a/PLUGINS/src/sky/getskyepg.pl +++ b/PLUGINS/src/sky/getskyepg.pl @@ -8,7 +8,7 @@ # # See the README file for copyright information and how to reach the author. # -# $Id: getskyepg.pl 1.3 2004/02/15 13:35:52 kls Exp $ +# $Id: getskyepg.pl 1.4 2006/01/08 10:21:32 kls Exp $ use Getopt::Std; use Time::Local; @@ -151,7 +151,7 @@ sub GetEpgData $gmt[1] = $m; # minutes $gmt[2] = $h; # hours $time = timegm(@gmt) + ($day - 1) * $SecsInDay + ($h < 12 ? $dt : 0); - # comensate for DST: + # compensate for DST: $time += $DST if (localtime($time))[8]; # create EPG data: if ($Time) { diff --git a/PLUGINS/src/status/HISTORY b/PLUGINS/src/status/HISTORY index 1c399aa..998e23d 100644 --- a/PLUGINS/src/status/HISTORY +++ b/PLUGINS/src/status/HISTORY @@ -31,3 +31,7 @@ VDR Plugin 'status' Revision History 2002-12-13: Version 0.1.0 - Changed setting of CXX and CXXFLAGS variables in Makefile. + +2005-12-31: Version 0.2.0 + +- API change in cStatus. diff --git a/PLUGINS/src/status/status.c b/PLUGINS/src/status/status.c index a034659..7163927 100644 --- a/PLUGINS/src/status/status.c +++ b/PLUGINS/src/status/status.c @@ -3,13 +3,13 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: status.c 1.7 2002/12/13 15:01:53 kls Exp $ + * $Id: status.c 1.8 2005/12/31 15:19:45 kls Exp $ */ #include <vdr/plugin.h> #include <vdr/status.h> -static const char *VERSION = "0.1.0"; +static const char *VERSION = "0.2.0"; static const char *DESCRIPTION = "Status monitor test"; static const char *MAINMENUENTRY = NULL; @@ -18,8 +18,8 @@ static const char *MAINMENUENTRY = NULL; class cStatusTest : public cStatus { protected: virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber); - virtual void Recording(const cDevice *Device, const char *Name); - virtual void Replaying(const cControl *Control, const char *Name); + virtual void Recording(const cDevice *Device, const char *Name, const char *FileName, bool On); + virtual void Replaying(const cControl *Control, const char *Name, const char *FileName, bool On); virtual void SetVolume(int Volume, bool Absolute); virtual void OsdClear(void); virtual void OsdTitle(const char *Title); @@ -36,14 +36,14 @@ void cStatusTest::ChannelSwitch(const cDevice *Device, int ChannelNumber) dsyslog("status: cStatusTest::ChannelSwitch %d %d", Device->CardIndex(), ChannelNumber); } -void cStatusTest::Recording(const cDevice *Device, const char *Name) +void cStatusTest::Recording(const cDevice *Device, const char *Name, const char *FileName, bool On) { - dsyslog("status: cStatusTest::Recording %d %s", Device->CardIndex(), Name); + dsyslog("status: cStatusTest::Recording %d %s %s %d", Device->CardIndex(), Name, FileName, On); } -void cStatusTest::Replaying(const cControl *Control, const char *Name) +void cStatusTest::Replaying(const cControl *Control, const char *Name, const char *FileName, bool On) { - dsyslog("status: cStatusTest::Replaying %s", Name); + dsyslog("status: cStatusTest::Replaying %s %s %d", Name, FileName, On); } void cStatusTest::SetVolume(int Volume, bool Absolute) diff --git a/ca.conf b/ca.conf deleted file mode 100644 index e4bab66..0000000 --- a/ca.conf +++ /dev/null @@ -1,17 +0,0 @@ -# Conditional Access configuration for VDR -# -# Format: -# -# number description -# -# Please contact kls@cadsoft.de before assigning a new number -# to a description, in order to keep them unique. - -0 Free To Air - -# Special values to "hard code" a channel to a specific DVB card: - -1 DVB 1 -2 DVB 2 -3 DVB 3 -4 DVB 4 @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: channels.c 1.46 2005/09/11 14:22:24 kls Exp $ + * $Id: channels.c 1.47 2005/12/30 15:41:24 kls Exp $ */ #include "channels.h" @@ -586,7 +586,7 @@ static const char *ParseParameter(const char *s, int &Value, const tChannelParam return p; } } - esyslog("ERROR: illegal value for parameter '%c'", *(s - 1)); + esyslog("ERROR: invalid value for parameter '%c'", *(s - 1)); return NULL; } diff --git a/channels.conf b/channels.conf index e54bfcb..e4f0fa4 100644 --- a/channels.conf +++ b/channels.conf @@ -2,18 +2,18 @@ RTL Television,RTL;RTL World:12187:hC34:S19.2E:27500:163:104=deu:105:0:12003:1:1 SAT.1;ProSiebenSat.1:12480:vC34:S19.2E:27500:1791:1792=deu;1795=deu:34:0:46:133:33:0 ProSieben;ProSiebenSat.1:12480:vC34:S19.2E:27500:255:256=deu;257=deu:32:0:898:133:33:0 RTL2;RTL World:12187:hC34:S19.2E:27500:166:128=deu:68:0:12020:1:1089:0 -Das Erste;ARD:11836:hC34:S19.2E:27500:101:102=deu:104:0:28106:1:1101:0 +Das Erste;ARD:11836:hC34:S19.2E:27500:101:102=deu;106=deu:104:0:28106:1:1101:0 Bayerisches FS;ARD:11836:hC34:S19.2E:27500:201:202=deu:204:0:28107:1:1101:0 hr-fernsehen;ARD:11836:hC34:S19.2E:27500:301:302=deu:304:0:28108:1:1101:0 NDR FS MV;ARD:12109:hC34:S19.2E:27500:2401:2402=deu:2404:0:28224:1:1073:0 SR SÜDWEST Ferns.;ARD:12265:hC34:S19.2E:27500:1301:1302=deu:1304:0:28486:1:1093:0 WDR Köln;ARD:11836:hC34:S19.2E:27500:601:602=deu:604:0:28111:1:1101:0 -BR-alpha;ARD:11836:hC34:S19.2E:27500:701:702=deu;703:704:0:28112:1:1101:0 +BR-alpha;ARD:11836:hC34:S19.2E:27500:701:702=deu:704:0:28112:1:1101:0 SÜDWEST Ferns. BW;ARD:11836:hC34:S19.2E:27500:801:802=deu:804:0:28113:1:1101:0 Phoenix;ARD:11836:hC34:S19.2E:27500:901:902=deu:904:0:28114:1:1101:0 ZDF;ZDFvision:11953:hC34:S19.2E:27500:110:120=deu,121=2ch;125=dd:130:0:28006:1:1079:0 3sat;ZDFvision:11953:hC34:S19.2E:27500:210:220=deu,221=2ch;225=dd:230:0:28007:1:1079:0 -KiKa;ZDFvision:11953:hC34:S19.2E:27500:310:320:0:0:28008:1:1079:0 +KiKa;ZDFvision:11953:hC34:S19.2E:27500:310:320=deu:330:0:28008:1:1079:0 arte;ARD:11836:hC34:S19.2E:27500:401:402=deu,403=fra:404:0:28109:1:1101:0 ORF1;ORF:12692:hC56:S19.2E:22000:160:161=deu;163=deu:165:1762,D05,1702,1801:13001:1:1117:0 ORF2;ORF:12692:hC56:S19.2E:22000:500:501=deu;503=deu:505:1762,D05,1702,1801:13002:1:1117:0 @@ -28,14 +28,14 @@ DSF;BetaDigital:12480:vC34:S19.2E:27500:1023:1024=deu:39:0:900:133:33:0 HSE24,HSE24;BetaDigital:12480:vC34:S19.2E:27500:1279:1280=deu:37:0:40:133:33:0 Bloomberg TV Germany;Bloomberg:12551:vC56:S19.2E:22000:162:99=deu:0:0:12160:1:1108:0 EURONEWS;CSAT:11817:vC34:S19.2E:27500:163:92=fra,93=eng,94=ita,95=esl,91=rus,98=por,99=deu:0:0:8004:1:1070:0 -rbb Brandenburg;ARD:12109:hC34:S19.2E:27500:501:502=deu:504:0:28205:1:1073:0 +rbb Brandenburg;ARD:12109:hC34:S19.2E:27500:601:602=deu:604:0:28205:1:1073:0 Sky News:11597:vC56:S19.2E:22000:305+131:306=eng:0:0:28707:1:1026:0 Veronica/JETIX;CANALDIGITAAL:12574:hC56:S19.2E:22000:518+8190:92=dut:38:622,100:5020:53:1109:0 BVN;CANALDIGITAAL:12574:hC56:S19.2E:22000:515+8190:96=dut:36:0:5025:53:1109:0 n-tv;RTL World:12187:hC34:S19.2E:27500:169:73=deu:80:0:12090:1:1089:0 Al Jazeera;CANALSATELLITE:11567:vC56:S19.2E:22000:55:56=ara:0:0:9021:1:1024:0 TW1;ORF:12662:hC56:S19.2E:22000:1010:1011=deu:1013:0:13101:1:1115:0 -Eurosport;ZDFvision:11953:hC34:S19.2E:27500:410:420=deu:430:0:28009:1:1079:0 +Eurosport;SES Astra:12226:hC34:S19.2E:27500:101+8190:103=deu:102:0:31200:1:1091:0 EinsExtra;ARD:12109:hC34:S19.2E:27500:101:102=deu:0:0:28201:1:1073:0 EinsFestival;ARD:12109:hC34:S19.2E:27500:201:202=deu:0:0:28202:1:1073:0 EinsPlus;ARD:12109:hC34:S19.2E:27500:301:302=deu:0:0:28203:1:1073:0 @@ -44,11 +44,11 @@ ZDFdokukanal;ZDFvision:11953:hC34:S19.2E:27500:660:670=deu:130:0:28014:1:1079:0 MDR FERNSEHEN;ARD:12109:hC34:S19.2E:27500:401:402=deu:404:0:28204:1:1073:0 rbb Berlin;ARD:12109:hC34:S19.2E:27500:601:602=deu:604:0:28206:1:1073:0 :Premiere World -PREMIERE START,START;PREMIERE:11797:hC34:S19.2E:27500:255:256=deu:32:1:8:133:2:0 -PREMIERE 1,PREM 1;PREMIERE:11797:hC34:S19.2E:27500:511:512=deu,513=deu;515=deu:32:1702,1722,1801:10:133:2:0 -PREMIERE 2,PREM 2;PREMIERE:11797:hC34:S19.2E:27500:1791:1792=deu,1793=deu;1795=deu:32:1702,1801,1722:11:133:2:0 +PREMIERE START,START;PREMIERE:11797:hC34:S19.2E:27500:255:256=deu:32:1801,1722,1702:8:133:2:0 +PREMIERE 1,PREM 1;PREMIERE:11797:hC34:S19.2E:27500:511:512=deu,513=deu;515=deu:32:1801,1722,1702:10:133:2:0 +PREMIERE 2,PREM 2;PREMIERE:11797:hC34:S19.2E:27500:1791:1792=deu,1793=deu;1795=deu:32:1801,1722,1702:11:133:2:0 PREMIERE 3,PREM 3;PREMIERE:11797:hC34:S19.2E:27500:2303:2304=deu,2305=deu:32:1722,1702,1801:43:133:2:0 -PREMIERE 4,PREM 4;PREMIERE:11797:hC34:S19.2E:27500:767:768=deu:32:1801,1722,1702:9:133:2:0 +PREMIERE 4,PREM 4;PREMIERE:11797:hC34:S19.2E:27500:767:768=deu,769=deu:32:1801,1722,1702:9:133:2:0 PREMIERE 5,PREM 5;PREMIERE:11797:hC34:S19.2E:27500:1279:1280=deu,1281=deu:32:1722,1702,1801:29:133:2:0 PREMIERE 6,PREM 6;PREMIERE:11797:hC34:S19.2E:27500:1535:1536=deu:32:1702,1722,1801:41:133:2:0 PREMIERE 7,PREM 7;PREMIERE:11797:hC34:S19.2E:27500:1023:1024=deu:32:1801,1702,1722:20:133:2:0 @@ -57,10 +57,10 @@ DISNEY CHANNEL,DISNEY;PREMIERE:11758:hC34:S19.2E:27500:2559:2560=deu:32:1801,170 PREMIERE DIREKT,DIREKT;PREMIERE:12031:hC34:S19.2E:27500:2815:2816=deu,2817=deu;2819=deu:0:0:18:133:4:0 :PW Erotic BEATE-UHSE.TV,B-UHSE;PREMIERE:11758:hC34:S19.2E:27500:1791:1792=deu:32:1722,1702,1801:21:133:17:0 -EROTIK - AB 18!,AB 18!;PREMIERE:12031:hC34:S19.2E:27500:1279:1280=deu:0:1810,1801,1702,1722:513:133:4:0 +EROTIK - AB 18!,AB 18!;PREMIERE:12031:hC34:S19.2E:27500:1279:1280=deu:0:1722,1801,1702,1810:513:133:4:0 :Sportsworld -PREMIERE SPORT PORTAL,SPORT PORTAL;PREMIERE:11719:hC34:S19.2E:27500:255:256=deu,257=deu:32:1702,1722,1801:17:133:3:0 -PREMIERE WIN,WIN;PREMIERE:12031:hC34:S19.2E:27500:3839:3840=deu:32:0:27:133:4:0 +PREMIERE SPORT PORTAL,SPORT PORTAL;PREMIERE:11719:hC34:S19.2E:27500:255:256=deu,257=deu:32:1722,1801,1702:17:133:3:0 +PREMIERE WIN,WIN;PREMIERE:12031:hC34:S19.2E:27500:3839:3840=deu:33:0:27:133:4:0 :Beta Digital N24;ProSiebenSat.1:12480:vC34:S19.2E:27500:2047:2048=deu:36:0:47:133:33:0 LibertyTV FR;LibertyTV.com:12610:vC56:S19.2E:22000:941:943=deu:0:0:12199:1:1112:0 @@ -92,9 +92,9 @@ Sky One;BSkyB:12226:hC23:S28.2E:27500:515+8190:643=eng:579:960,961:4705:2:2027:0 Sky Two;BSkyB:12226:hC23:S28.2E:27500:514+8190:642=eng,662=NAR:578:960,961:5104:2:2027:0 ITV2;BSkyB:10758:vC56:S28.2E:22000:2314:2315=eng,2363=NAR:2317:0:10070:2:2044:0 Sci-Fi;BSkyB:12148:hC23:S28.2E:27500:512+8190:640=eng:576:960,961:4905:2:2023:0 -Paramount;BSkyB:12187:hC23:S28.2E:27500:518+8190:666=eng,686=NAR:582:960,961:5904:2:2025:0 +ParaComedy 1;BSkyB:12187:hC23:S28.2E:27500:518+8190:666=eng,686=NAR:582:960,961:5904:2:2025:0 Paramount;BSkyB:11526:vC23:S28.2E:27500:2317+2306:2318=eng:2319:960,961:50305:2:2404:0 -Paramount 2;BSkyB:11914:hC23:S28.2E:27500:514+8190:642=eng,662=NAR:578:960,961:4504:2:2011:0 +ParaComedy 2;BSkyB:11914:hC23:S28.2E:27500:514+8190:642=eng,662=NAR:578:960,961:4504:2:2011:0 Discovery;BSkyB:11875:hC23:S28.2E:27500:2308:2310=eng,2311=NAR:2309:960,961:6201:2:2009:0 Sky Movies 1;BSkyB:11836:hC23:S28.2E:27500:518+8190:646=eng,653=NAR;686=eng:582:960,961:4303:2:2007:0 Sky Movies 2;BSkyB:11836:hC23:S28.2E:27500:519+8190:647=eng,667=NAR;687=eng:583:960,961:4302:2:2007:0 @@ -115,8 +115,11 @@ S1T;BSkyB:12285:vC23:S28.2E:27500:513+8190:641=eng,661=NAR:577:960,961:4409:2:20 CNN;BSkyB:12051:vC23:S28.2E:27500:2313:2315=eng:2314:0:7140:2:2018:0 BBC PARL'MNT;BSkyB:10847:vC56:S28.2E:22000:2327:2328=eng:2331:0:6902:2:2050:0 IGLESIA MME;T-Systems/MTI:11200:vC56:S13.0E:27500:4097:4098:0:0:4733:318:13400:0 -Euro1080;EURO1080:12168:vC56:S19.2E:27500:308:256:0:FF:21100:1:1088:0 -Astra HD:12441:vC34:S19.2E:27500:133+80:134=eng:0:FF:29700:0:0:0 +Euro1080 HD-5;Euro1080:10758:vC78:S23.5E:22000:34:160=eng:0:0:1085:9999:3104:0 +Euro1080;EURO1080:12168:vC56:S19.2E:27500:308:256:0:0:21100:1:1088:0 +SMD HD;SES ASTRA:12699:vC56:S19.2E:22000:133+80:234=eng:0:0:29700:0:0:0 +Astra HD:12441:vC34:S19.2E:27500:133+80:134=eng:0:0:29700:0:0:0 eng-WRN-multi;WRN:12597:vC34:S13.0E:27500:0:2132:0:0:8230:318:9400:0 -TVS Teleport Bonn;DMV:11535:vC34:S1.0W:5632:308+8190:256=eng,257=eng:0:2:1:65535:1:0 +Challenger Tv;Telespazio:11304:hC34:S13.0E:27500:490:491:0:0:8409:318:500:0 +TVS Teleport Bonn;DMV:11535:vC34:S1.0W:5632:308+8190:256=eng,257=eng:0:3:1:65535:1:0 :@1000 New channels @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: channels.h 1.36 2005/09/17 09:59:14 kls Exp $ + * $Id: channels.h 1.37 2006/01/07 13:00:43 kls Exp $ */ #ifndef __CHANNELS_H @@ -39,6 +39,14 @@ #define MAXLANGCODE1 4 // a 3 letter language code, zero terminated #define MAXLANGCODE2 8 // up to two 3 letter language codes, separated by '+' and zero terminated +#define CA_FTA 0x0000 +#define CA_DVB_MIN 0x0001 +#define CA_DVB_MAX 0x000F +#define CA_USER_MIN 0x0010 +#define CA_USER_MAX 0x00FF +#define CA_ENCRYPTED_MIN 0x0100 +#define CA_ENCRYPTED_MAX 0xFFFF + struct tChannelParameterMap { int userValue; int driverValue; @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: ci.c 1.40 2005/11/26 13:36:51 kls Exp $ + * $Id: ci.c 1.42 2006/01/07 15:07:16 kls Exp $ */ #include "ci.h" @@ -185,7 +185,7 @@ cTPDU::cTPDU(uint8_t Slot, uint8_t Tcid, uint8_t Tag, int Length, const uint8_t size = 6; } else - esyslog("ERROR: illegal data length for TPDU tag 0x%02X: %d", Tag, Length); + esyslog("ERROR: invalid data length for TPDU tag 0x%02X: %d", Tag, Length); break; case T_DATA_LAST: case T_DATA_MORE: @@ -198,7 +198,7 @@ cTPDU::cTPDU(uint8_t Slot, uint8_t Tcid, uint8_t Tag, int Length, const uint8_t size = Length + (p - data); } else - esyslog("ERROR: illegal data length for TPDU tag 0x%02X: %d", Tag, Length); + esyslog("ERROR: invalid data length for TPDU tag 0x%02X: %d", Tag, Length); break; default: esyslog("ERROR: unknown TPDU tag: 0x%02X", Tag); @@ -1639,6 +1639,15 @@ int cCiHandler::CloseAllSessions(int Slot) return result; } +int cCiHandler::NumCams(void) +{ + int result = 0; + for (int i = 0; i < MAX_CI_SLOT; i++) + if (moduleReady[i]) + result++; + return result; +} + bool cCiHandler::Ready(void) { cMutexLock MutexLock(&mutex); @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: ci.h 1.19 2005/11/26 13:37:42 kls Exp $ + * $Id: ci.h 1.21 2006/01/07 15:03:05 kls Exp $ */ #ifndef __CI_H @@ -112,6 +112,7 @@ private: cList<cCiCaProgramData> caProgramList; int ResourceIdToInt(const uint8_t *Data); bool Send(uint8_t Tag, int SessionId, int ResourceId = 0, int Status = -1); + const unsigned short *GetCaSystemIds(int Slot); cCiSession *GetSessionBySessionId(int SessionId); cCiSession *GetSessionByResourceId(int ResourceId, int Slot); cCiSession *CreateSession(int ResourceId); @@ -123,18 +124,35 @@ private: public: ~cCiHandler(); static cCiHandler *CreateCiHandler(const char *FileName); + ///< Creates a new cCiHandler for the given CA device. int NumSlots(void) { return numSlots; } + ///< Returns the number of CAM slots provided by this CA device. + int NumCams(void); + ///< Returns the number of actual CAMs inserted into this CA device. bool Ready(void); + ///< Returns true if all CAMs in this CA device are ready. bool Process(int Slot = -1); ///< Processes the given Slot. If Slot is -1, all slots are processed. ///< Returns false in case of an error. bool HasUserIO(void) { return hasUserIO; } + ///< Returns true if there is a pending user interaction, which shall + ///< be retrieved via GetMenu() or GetEnquiry(). bool EnterMenu(int Slot); + ///< Requests the CAM in the given Slot to start its menu. cCiMenu *GetMenu(void); + ///< Gets a pending menu, or NULL if there is no menu. cCiEnquiry *GetEnquiry(void); + ///< Gets a pending enquiry, or NULL if there is no enquiry. const char *GetCamName(int Slot); - const unsigned short *GetCaSystemIds(int Slot); + ///< Returns the name of the CAM in the given Slot, or NULL if there + ///< is no CAM in that slot. bool ProvidesCa(const unsigned short *CaSystemIds); //XXX Slot??? + ///< Returns true if any of the CAMs can provide one of the given + ///< CaSystemIds. This doesn't necessarily mean that it will be + ///< possible to actually decrypt such a programme, since CAMs + ///< usually advertise several CA system ids, while the actual + ///< decryption is controlled by the smart card inserted into + ///< the CAM. void SetSource(int Source, int Transponder); ///< Sets the Source and Transponder of the device this cCiHandler is ///< currently tuned to. If Source or Transponder are different than @@ -145,11 +163,14 @@ public: ///< to SetPid() will (de)activate one of these entries. void SetPid(int Pid, bool Active); ///< Sets the given Pid (which has previously been added through a - ///< call to AddPid()) to Active. If Active is true, a later call to - ///< StartDecrypting() will send the full list of currently active CA_PMT - ///< entries to the CAM, including this one. + ///< call to AddPid()) to Active. A later call to StartDecrypting() will + ///< send the full list of currently active CA_PMT entries to the CAM. bool CanDecrypt(int ProgramNumber); ///< XXX + ///< Returns true if there is a CAM in this CA device that is able + ///< to decrypt the programme with the given ProgramNumber. The PIDs + ///< for this ProgramNumber must have been set through previous calls + ///< to SetPid(). void StartDecrypting(void); ///< Triggers sending all currently active CA_PMT entries to the CAM, ///< so that it will start decrypting. @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.c 1.138 2005/09/09 15:08:59 kls Exp $ + * $Id: config.c 1.140 2006/01/07 12:28:49 kls Exp $ */ #include "config.h" @@ -120,24 +120,6 @@ bool cSVDRPhost::Accepts(in_addr_t Address) return (Address & mask) == addr.s_addr; } -// -- cCaDefinition ---------------------------------------------------------- - -cCaDefinition::cCaDefinition(void) -{ - number = 0; - description = NULL; -} - -cCaDefinition::~cCaDefinition() -{ - free(description); -} - -bool cCaDefinition::Parse(const char *s) -{ - return 2 == sscanf(s, "%d %a[^\n]", &number, &description) && description && *description; -} - // -- cCommands -------------------------------------------------------------- cCommands Commands; @@ -158,21 +140,6 @@ bool cSVDRPhosts::Acceptable(in_addr_t Address) return false; } -// -- cCaDefinitions --------------------------------------------------------- - -cCaDefinitions CaDefinitions; - -const cCaDefinition *cCaDefinitions::Get(int Number) -{ - cCaDefinition *p = First(); - while (p) { - if (p->Number() == Number) - return p; - p = (cCaDefinition *)p->Next(); - } - return NULL; -} - // -- cSetupLine ------------------------------------------------------------- cSetupLine::cSetupLine(void) @@ -250,6 +217,7 @@ cSetup::cSetup(void) strcpy(OSDTheme, "default"); PrimaryDVB = 1; ShowInfoOnChSwitch = 1; + TimeoutRequChInfo = 1; MenuScrollPage = 1; MenuScrollWrap = 0; MarkInstantRecord = 1; @@ -408,6 +376,7 @@ bool cSetup::Parse(const char *Name, const char *Value) else if (!strcasecmp(Name, "OSDTheme")) strn0cpy(OSDTheme, Value, MaxThemeName); else if (!strcasecmp(Name, "PrimaryDVB")) PrimaryDVB = atoi(Value); else if (!strcasecmp(Name, "ShowInfoOnChSwitch")) ShowInfoOnChSwitch = atoi(Value); + else if (!strcasecmp(Name, "TimeoutRequChInfo")) TimeoutRequChInfo = atoi(Value); else if (!strcasecmp(Name, "MenuScrollPage")) MenuScrollPage = atoi(Value); else if (!strcasecmp(Name, "MenuScrollWrap")) MenuScrollWrap = atoi(Value); else if (!strcasecmp(Name, "MarkInstantRecord")) MarkInstantRecord = atoi(Value); @@ -473,6 +442,7 @@ bool cSetup::Save(void) Store("OSDTheme", OSDTheme); Store("PrimaryDVB", PrimaryDVB); Store("ShowInfoOnChSwitch", ShowInfoOnChSwitch); + Store("TimeoutRequChInfo", TimeoutRequChInfo); Store("MenuScrollPage", MenuScrollPage); Store("MenuScrollWrap", MenuScrollWrap); Store("MarkInstantRecord", MarkInstantRecord); @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.h 1.235 2005/11/11 13:22:02 kls Exp $ + * $Id: config.h 1.238 2006/01/07 12:57:42 kls Exp $ */ #ifndef __CONFIG_H @@ -19,8 +19,8 @@ #include "i18n.h" #include "tools.h" -#define VDRVERSION "1.3.37" -#define VDRVERSNUM 10337 // Version * 10000 + Major * 100 + Minor +#define VDRVERSION "1.3.38" +#define VDRVERSNUM 10338 // Version * 10000 + Major * 100 + Minor #define MAXPRIORITY 99 #define MAXLIFETIME 99 @@ -61,20 +61,6 @@ public: bool Accepts(in_addr_t Address); }; -#define CACONFBASE 100 - -class cCaDefinition : public cListObject { -private: - int number; - char *description; -public: - cCaDefinition(void); - ~cCaDefinition(); - bool Parse(const char *s); - int Number(void) const { return number; } - const char *Description(void) const { return description; } - }; - template<class T> class cConfig : public cList<T> { private: char *fileName; @@ -166,15 +152,9 @@ public: bool Acceptable(in_addr_t Address); }; -class cCaDefinitions : public cConfig<cCaDefinition> { -public: - const cCaDefinition *Get(int Number); - }; - extern cCommands Commands; extern cCommands RecordingCommands; extern cSVDRPhosts SVDRPhosts; -extern cCaDefinitions CaDefinitions; class cSetupLine : public cListObject { private: @@ -210,6 +190,7 @@ public: char OSDTheme[MaxThemeName]; int PrimaryDVB; int ShowInfoOnChSwitch; + int TimeoutRequChInfo; int MenuScrollPage; int MenuScrollWrap; int MarkInstantRecord; @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: device.c 1.112 2005/11/26 12:56:09 kls Exp $ + * $Id: device.c 1.121 2006/01/08 11:39:37 kls Exp $ */ #include "device.h" @@ -222,7 +222,7 @@ int cDevice::NextCardIndex(int n) esyslog("ERROR: nextCardIndex too big (%d)", nextCardIndex); } else if (n < 0) - esyslog("ERROR: illegal value in IncCardIndex(%d)", n); + esyslog("ERROR: invalid value in IncCardIndex(%d)", n); return nextCardIndex; } @@ -302,7 +302,7 @@ cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool *NeedsDe pri = 6; // receiving with same priority but fewer Ca's else pri = 7; // all others - if (pri < select) { + if (pri <= select) { select = pri; d = device[i]; if (NeedsDetachReceivers) @@ -322,9 +322,36 @@ void cDevice::Shutdown(void) } } -bool cDevice::GrabImage(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY) +uchar *cDevice::GrabImage(int &Size, bool Jpeg, int Quality, int SizeX, int SizeY) { - return false; + return NULL; +} + +bool cDevice::GrabImageFile(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY) +{ + int result = 0; + int fd = open(FileName, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC, DEFFILEMODE); + if (fd >= 0) { + int ImageSize; + uchar *Image = GrabImage(ImageSize, Jpeg, Quality, SizeX, SizeY); + if (Image) { + if (safe_write(fd, Image, ImageSize) == ImageSize) + isyslog("grabbed image to %s", FileName); + else { + LOG_ERROR_STR(FileName); + result |= 1; + } + free(Image); + } + else + result |= 1; + close(fd); + } + else { + LOG_ERROR_STR(FileName); + result |= 1; + } + return result == 0; } void cDevice::SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat) @@ -577,10 +604,14 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView) if (LiveView) StopReplay(); + // If this card is switched to an other transponder, any receivers still + // attached to it ineed to be automatically detached: + bool NeedsDetachReceivers = false; + // 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 = (LiveView && IsPrimaryDevice() && !ProvidesChannel(Channel, Setup.PrimaryLimit)); + bool NeedsTransferMode = (LiveView && IsPrimaryDevice() && !ProvidesChannel(Channel, Setup.PrimaryLimit, &NeedsDetachReceivers)); eSetChannelResult Result = scrOk; @@ -588,11 +619,14 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView) // use the card that actually can receive it and transfer data from there: if (NeedsTransferMode) { - cDevice *CaDevice = GetDevice(Channel, 0); + cDevice *CaDevice = GetDevice(Channel, 0, &NeedsDetachReceivers); if (CaDevice && CanReplay()) { cStatus::MsgChannelSwitch(this, 0); // only report status if we are actually going to switch the channel - if (CaDevice->SetChannel(Channel, false) == scrOk) // calling SetChannel() directly, not SwitchChannel()! + if (CaDevice->SetChannel(Channel, false) == scrOk) { // calling SetChannel() directly, not SwitchChannel()! + if (NeedsDetachReceivers) + CaDevice->DetachAllReceivers(); cControl::Launch(new cTransferControl(CaDevice, Channel->Vpid(), Channel->Apids(), Channel->Dpids(), Channel->Spids())); + } else Result = scrNoTransfer; } @@ -613,7 +647,7 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView) ciHandler->SetSource(Channel->Source(), Channel->Transponder()); // Men at work - please stand clear! ;-) #ifdef XXX_DO_MULTIPLE_CA_CHANNELS - if (Channel->Ca() > CACONFBASE) { + if (Channel->Ca() >= CA_ENCRYPTED_MIN) { #endif ciHandler->AddPid(Channel->Sid(), Channel->Vpid(), 2); for (const int *Apid = Channel->Apids(); *Apid; Apid++) @@ -626,13 +660,15 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView) } #endif } + if (NeedsDetachReceivers) + DetachAllReceivers(); if (SetChannelDevice(Channel, LiveView)) { // Start section handling: if (sectionHandler) { sectionHandler->SetChannel(Channel); sectionHandler->SetStatus(true); } - // Start decrypting any PIDs the might have been set in SetChannelDevice(): + // Start decrypting any PIDs that might have been set in SetChannelDevice(): if (ciHandler) ciHandler->StartDecrypting(); } @@ -794,6 +830,7 @@ int cDevice::NumAudioTracks(void) const bool cDevice::SetCurrentAudioTrack(eTrackType Type) { if (ttNone < Type && Type < ttDolbyLast) { + cMutexLock MutexLock(&mutexCurrentAudioTrack); if (IS_DOLBY_TRACK(Type)) SetDigitalAudioDevice(true); currentAudioTrack = Type; @@ -947,6 +984,7 @@ int cDevice::PlayAudio(const uchar *Data, int Length) int cDevice::PlayPesPacket(const uchar *Data, int Length, bool VideoOnly) { + cMutexLock MutexLock(&mutexCurrentAudioTrack); bool FirstLoop = true; uchar c = Data[3]; const uchar *Start = Data; @@ -972,7 +1010,7 @@ int cDevice::PlayPesPacket(const uchar *Data, int Length, bool VideoOnly) uchar SubStreamId = Data[PayloadOffset]; uchar SubStreamType = SubStreamId & 0xF0; uchar SubStreamIndex = SubStreamId & 0x1F; - + // Compatibility mode for old VDR recordings, where 0xBD was only AC3: pre_1_3_19_PrivateStreamDeteced: if (pre_1_3_19_PrivateStream) { @@ -1111,7 +1149,7 @@ int cDevice::ProvidesCa(const cChannel *Channel) const int Ca = Channel->Ca(); if (Ca == CardIndex() + 1) return 1; // exactly _this_ card was requested - if (Ca && Ca <= MAXDEVICES) + if (Ca && Ca <= CA_DVB_MAX) return 0; // a specific card was requested, but not _this_ one return !Ca; // by default every card can provide FTA } @@ -1242,6 +1280,15 @@ void cDevice::DetachAll(int Pid) } } +void cDevice::DetachAllReceivers(void) +{ + cMutexLock MutexLock(&mutexReceiver); + for (int i = 0; i < MAXRECEIVERS; i++) { + if (receiver[i]) + Detach(receiver[i]); + } +} + // --- cTSBuffer ------------------------------------------------------------- cTSBuffer::cTSBuffer(int File, int Size, int CardIndex) @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: device.h 1.66 2005/11/05 15:25:41 kls Exp $ + * $Id: device.h 1.70 2006/01/08 10:10:26 kls Exp $ */ #ifndef __DEVICE_H @@ -170,14 +170,16 @@ public: ///< Returns the card index of this device (0 ... MAXDEVICES - 1). int DeviceNumber(void) const; ///< Returns the number of this device (0 ... MAXDEVICES - 1). - virtual int ProvidesCa(const cChannel *Channel) const;//XXX PLUGINS.html!!! - //XXX describe changed functionality!!! - ///< Checks whether this device provides the given value in its - ///< caCaps. Returns 0 if the value is not provided, 1 if only this - ///< value is provided, and > 1 if this and other values are provided. - ///< If the given value is equal to the number of this device, - ///< 1 is returned. If it is 0 (FTA), 1 plus the number of other values - ///< in caCaps is returned. + virtual int ProvidesCa(const cChannel *Channel) const; + ///< Checks whether this device provides the conditional access + ///< facilities to decrypt the given Channel. + ///< Returns 0 if the Channel can't be decrypted, 1 if this is a + ///< Free To Air channel or only exactly this device can decrypt it, + ///< and > 1 if this device can decrypt the Channel. + ///< If the result is greater than 1 and the device has more than one + ///< CAM, the value will be increased by the number of CAMs, which + ///< allows to select the device with the smallest number of CAMs + ///< in order to preserve resources for other recordings. virtual bool HasDecoder(void) const; ///< Tells whether this device has an MPEG decoder. @@ -305,11 +307,9 @@ public: // Image Grab facilities public: - virtual bool GrabImage(const char *FileName, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1); - ///< Capture a single frame as an image. - ///< Grabs the currently visible screen image into the given file, with the - ///< given parameters. - ///< \param FileName The name of the file to write. Should include the proper extension. + virtual uchar *GrabImage(int &Size, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1); + ///< Grabs the currently visible screen image. + ///< \param Size The size of the returned data block. ///< \param Jpeg If true will write a JPEG file. Otherwise a PNM file will be written. ///< \param Quality The compression factor for JPEG. 1 will create a very blocky ///< and small image, 70..80 will yield reasonable quality images while keeping the @@ -317,7 +317,13 @@ public: ///< but very high quality image. ///< \param SizeX The number of horizontal pixels in the frame (default is the current screen width). ///< \param SizeY The number of vertical pixels in the frame (default is the current screen height). - ///< \return True if all went well. */ + ///< \return A pointer to the grabbed image data, or NULL in case of an error. + ///< The caller takes ownership of the returned memory and must free() it once it isn't needed any more. + bool GrabImageFile(const char *FileName, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1); + ///< Calls GrabImage() and stores the resulting image in a file with the given name. + ///< \return True if all went well. + ///< The caller is responsible for making sure that the given file name + ///< doesn't lead to overwriting any important other file. // Video format facilities @@ -338,6 +344,7 @@ public: private: tTrackId availableTracks[ttMaxTrackTypes]; eTrackType currentAudioTrack; + cMutex mutexCurrentAudioTrack; int currentAudioTrackMissingCount; bool pre_1_3_19_PrivateStream; protected: @@ -516,6 +523,8 @@ public: ///< Detaches the given receiver from this device. void DetachAll(int Pid); ///< Detaches all receivers from this device for this pid. + void DetachAllReceivers(void); + ///< Detaches all receivers from this device. }; /// Derived cDevice classes that can receive channels will have to provide @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: diseqc.c 1.4 2005/01/09 13:05:11 kls Exp $ + * $Id: diseqc.c 1.5 2005/12/30 15:41:48 kls Exp $ */ #include "diseqc.h" @@ -65,7 +65,7 @@ char *cDiseqc::Wait(char *s) cCondWait::SleepMs(n); return p; } - esyslog("ERROR: illegal value for wait time in '%s'", s - 1); + esyslog("ERROR: invalid value for wait time in '%s'", s - 1); return NULL; } @@ -85,7 +85,7 @@ char *cDiseqc::Codes(char *s) t = skipspace(p); } else { - esyslog("ERROR: illegal code at '%s'", t); + esyslog("ERROR: invalid code at '%s'", t); return NULL; } } diff --git a/dvbdevice.c b/dvbdevice.c index 30ce1e4..7fa39cc 100644 --- a/dvbdevice.c +++ b/dvbdevice.c @@ -4,18 +4,11 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbdevice.c 1.138 2005/11/26 13:23:11 kls Exp $ + * $Id: dvbdevice.c 1.149 2006/01/07 15:15:01 kls Exp $ */ #include "dvbdevice.h" #include <errno.h> -extern "C" { -#ifdef boolean -#define HAVE_BOOLEAN -#endif -#include <jpeglib.h> -#undef boolean -} #include <limits.h> #include <linux/videodev.h> #include <linux/dvb/audio.h> @@ -47,6 +40,13 @@ extern "C" { #define DEV_DVB_AUDIO "audio" #define DEV_DVB_CA "ca" +#define DVBS_TUNE_TIMEOUT 2000 //ms +#define DVBS_LOCK_TIMEOUT 2000 //ms +#define DVBC_TUNE_TIMEOUT 5000 //ms +#define DVBC_LOCK_TIMEOUT 2000 //ms +#define DVBT_TUNE_TIMEOUT 9000 //ms +#define DVBT_LOCK_TIMEOUT 2000 //ms + class cDvbName { private: char buffer[PATH_MAX]; @@ -73,6 +73,9 @@ private: enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked }; int fd_frontend; int cardIndex; + int tuneTimeout; + int lockTimeout; + time_t lastTimeoutReport; fe_type_t frontendType; cCiHandler *ciHandler; cChannel channel; @@ -81,7 +84,7 @@ private: cMutex mutex; cCondVar locked; cCondVar newSet; - bool GetFrontendEvent(dvb_frontend_event &Event, int TimeoutMs = 0); + bool GetFrontendStatus(fe_status_t &Status, int TimeoutMs = 0); bool SetFrontend(void); virtual void Action(void); public: @@ -98,6 +101,9 @@ cDvbTuner::cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType, cCi cardIndex = CardIndex; frontendType = FrontendType; ciHandler = CiHandler; + tuneTimeout = 0; + lockTimeout = 0; + lastTimeoutReport = 0; diseqcCommands = NULL; tunerStatus = tsIdle; if (frontendType == FE_QPSK) @@ -125,6 +131,7 @@ void cDvbTuner::Set(const cChannel *Channel, bool Tune) if (Tune) tunerStatus = tsSet; channel = *Channel; + lastTimeoutReport = 0; newSet.Broadcast(); } @@ -140,26 +147,18 @@ bool cDvbTuner::Locked(int TimeoutMs) return tunerStatus >= tsLocked; } -bool cDvbTuner::GetFrontendEvent(dvb_frontend_event &Event, int TimeoutMs) +bool cDvbTuner::GetFrontendStatus(fe_status_t &Status, int TimeoutMs) { if (TimeoutMs) { - struct pollfd pfd; - pfd.fd = fd_frontend; - pfd.events = POLLIN | POLLPRI; - do { - int stat = poll(&pfd, 1, TimeoutMs); - if (stat == 1) - break; - if (stat < 0) { - if (errno == EINTR) - continue; - esyslog("ERROR: frontend %d poll failed: %m", cardIndex); - } - return false; - } while (0); + cPoller Poller(fd_frontend); + if (Poller.Poll(TimeoutMs)) { + dvb_frontend_event Event; + while (ioctl(fd_frontend, FE_GET_EVENT, &Event) == 0) + ; // just to clear the event queue - we'll read the actual status below + } } do { - int stat = ioctl(fd_frontend, FE_GET_EVENT, &Event); + int stat = ioctl(fd_frontend, FE_READ_STATUS, &Status); if (stat == 0) return true; if (stat < 0) { @@ -245,6 +244,9 @@ bool cDvbTuner::SetFrontend(void) Frontend.inversion = fe_spectral_inversion_t(channel.Inversion()); Frontend.u.qpsk.symbol_rate = channel.Srate() * 1000UL; Frontend.u.qpsk.fec_inner = fe_code_rate_t(channel.CoderateH()); + + tuneTimeout = DVBS_TUNE_TIMEOUT; + lockTimeout = DVBS_LOCK_TIMEOUT; } break; case FE_QAM: { // DVB-C @@ -256,6 +258,9 @@ bool cDvbTuner::SetFrontend(void) Frontend.u.qam.symbol_rate = channel.Srate() * 1000UL; Frontend.u.qam.fec_inner = fe_code_rate_t(channel.CoderateH()); Frontend.u.qam.modulation = fe_modulation_t(channel.Modulation()); + + tuneTimeout = DVBC_TUNE_TIMEOUT; + lockTimeout = DVBC_LOCK_TIMEOUT; } break; case FE_OFDM: { // DVB-T @@ -271,6 +276,9 @@ bool cDvbTuner::SetFrontend(void) Frontend.u.ofdm.transmission_mode = fe_transmit_mode_t(channel.Transmission()); Frontend.u.ofdm.guard_interval = fe_guard_interval_t(channel.Guard()); Frontend.u.ofdm.hierarchy_information = fe_hierarchy_t(channel.Hierarchy()); + + tuneTimeout = DVBT_TUNE_TIMEOUT; + lockTimeout = DVBT_LOCK_TIMEOUT; } break; default: @@ -286,30 +294,54 @@ bool cDvbTuner::SetFrontend(void) void cDvbTuner::Action(void) { - dvb_frontend_event event; + cTimeMs Timer; + bool LostLock = false; + fe_status_t Status = (fe_status_t)0; while (Running()) { - bool hasEvent = GetFrontendEvent(event, 1); - + fe_status_t NewStatus; + if (GetFrontendStatus(NewStatus, 10)) + Status = NewStatus; cMutexLock MutexLock(&mutex); switch (tunerStatus) { case tsIdle: break; case tsSet: - if (hasEvent) - continue; tunerStatus = SetFrontend() ? tsTuned : tsIdle; + Timer.Set(tuneTimeout); continue; case tsTuned: - case tsLocked: - if (hasEvent) { - if (event.status & FE_REINIT) { - tunerStatus = tsSet; - esyslog("ERROR: frontend %d was reinitialized - re-tuning", cardIndex); + if (Timer.TimedOut()) { + tunerStatus = tsSet; + diseqcCommands = NULL; + if (time(NULL) - lastTimeoutReport > 60) { // let's not get too many of these + esyslog("ERROR: frontend %d timed out while tuning to channel %d, tp %d", cardIndex, channel.Number(), channel.Transponder()); + lastTimeoutReport = time(NULL); } - if (event.status & FE_HAS_LOCK) { - tunerStatus = tsLocked; - locked.Broadcast(); + continue; + } + case tsLocked: + if (Status & FE_REINIT) { + tunerStatus = tsSet; + diseqcCommands = NULL; + esyslog("ERROR: frontend %d was reinitialized", cardIndex); + lastTimeoutReport = 0; + continue; + } + else if (Status & FE_HAS_LOCK) { + if (LostLock) { + esyslog("frontend %d regained lock on channel %d, tp %d", cardIndex, channel.Number(), channel.Transponder()); + LostLock = false; } + tunerStatus = tsLocked; + locked.Broadcast(); + lastTimeoutReport = 0; + } + else if (tunerStatus == tsLocked) { + LostLock = true; + esyslog("ERROR: frontend %d lost lock on channel %d, tp %d", cardIndex, channel.Number(), channel.Transponder()); + tunerStatus = tsTuned; + Timer.Set(lockTimeout); + lastTimeoutReport = 0; continue; } } @@ -471,13 +503,21 @@ bool cDvbDevice::Ready(void) int cDvbDevice::ProvidesCa(const cChannel *Channel) const { - if (Channel->Ca() >= 0x0100 && ciHandler) { - unsigned short ids[MAXCAIDS + 1]; - for (int i = 0; i <= MAXCAIDS; i++) // '<=' copies the terminating 0! - ids[i] = Channel->Ca(i); - return ciHandler->ProvidesCa(ids); + int NumCams = 0; + if (ciHandler) { + NumCams = ciHandler->NumCams(); + if (Channel->Ca() >= CA_ENCRYPTED_MIN) { + unsigned short ids[MAXCAIDS + 1]; + for (int i = 0; i <= MAXCAIDS; i++) // '<=' copies the terminating 0! + ids[i] = Channel->Ca(i); + if (ciHandler->ProvidesCa(ids)) + return NumCams + 1; + } } - return cDevice::ProvidesCa(Channel); + int result = cDevice::ProvidesCa(Channel); + if (result > 0) + result += NumCams; + return result; } cSpuDecoder *cDvbDevice::GetSpuDecoder(void) @@ -487,103 +527,84 @@ cSpuDecoder *cDvbDevice::GetSpuDecoder(void) return spuDecoder; } -bool cDvbDevice::GrabImage(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY) +uchar *cDvbDevice::GrabImage(int &Size, bool Jpeg, int Quality, int SizeX, int SizeY) { if (devVideoIndex < 0) - return false; + return NULL; char buffer[PATH_MAX]; snprintf(buffer, sizeof(buffer), "%s%d", DEV_VIDEO, devVideoIndex); int videoDev = open(buffer, O_RDWR); - if (videoDev < 0) - LOG_ERROR_STR(buffer); if (videoDev >= 0) { - int result = 0; + uchar *result = NULL; struct video_mbuf mbuf; - result |= ioctl(videoDev, VIDIOCGMBUF, &mbuf); - if (result == 0) { + if (ioctl(videoDev, VIDIOCGMBUF, &mbuf) == 0) { int msize = mbuf.size; unsigned char *mem = (unsigned char *)mmap(0, msize, PROT_READ | PROT_WRITE, MAP_SHARED, videoDev, 0); if (mem && mem != (unsigned char *)-1) { // set up the size and RGB struct video_capability vc; - result |= ioctl(videoDev, VIDIOCGCAP, &vc); - struct video_mmap vm; - vm.frame = 0; - if ((SizeX > 0) && (SizeX <= vc.maxwidth) && - (SizeY > 0) && (SizeY <= vc.maxheight)) { - vm.width = SizeX; - vm.height = SizeY; - } - else { - vm.width = vc.maxwidth; - vm.height = vc.maxheight; - } - vm.format = VIDEO_PALETTE_RGB24; - result |= ioctl(videoDev, VIDIOCMCAPTURE, &vm); - result |= ioctl(videoDev, VIDIOCSYNC, &vm.frame); - // make RGB out of BGR: - int memsize = vm.width * vm.height; - unsigned char *mem1 = mem; - for (int i = 0; i < memsize; i++) { - unsigned char tmp = mem1[2]; - mem1[2] = mem1[0]; - mem1[0] = tmp; - mem1 += 3; - } - - if (Quality < 0) - Quality = 100; - - isyslog("grabbing to %s (%s %d %d %d)", FileName, Jpeg ? "JPEG" : "PNM", Quality, vm.width, vm.height); - FILE *f = fopen(FileName, "wb"); - if (f) { - if (Jpeg) { - // write JPEG file: - struct jpeg_compress_struct cinfo; - struct jpeg_error_mgr jerr; - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_compress(&cinfo); - jpeg_stdio_dest(&cinfo, f); - cinfo.image_width = vm.width; - cinfo.image_height = vm.height; - cinfo.input_components = 3; - cinfo.in_color_space = JCS_RGB; - - jpeg_set_defaults(&cinfo); - jpeg_set_quality(&cinfo, Quality, true); - jpeg_start_compress(&cinfo, true); - - int rs = vm.width * 3; - JSAMPROW rp[vm.height]; - for (int k = 0; k < vm.height; k++) - rp[k] = &mem[rs * k]; - jpeg_write_scanlines(&cinfo, rp, vm.height); - jpeg_finish_compress(&cinfo); - jpeg_destroy_compress(&cinfo); + if (ioctl(videoDev, VIDIOCGCAP, &vc) == 0) { + struct video_mmap vm; + vm.frame = 0; + if ((SizeX > 0) && (SizeX <= vc.maxwidth) && + (SizeY > 0) && (SizeY <= vc.maxheight)) { + vm.width = SizeX; + vm.height = SizeY; } else { - // write PNM file: - if (fprintf(f, "P6\n%d\n%d\n255\n", vm.width, vm.height) < 0 || - fwrite(mem, vm.width * vm.height * 3, 1, f) != 1) { - LOG_ERROR_STR(FileName); - result |= 1; + vm.width = vc.maxwidth; + vm.height = vc.maxheight; + } + vm.format = VIDEO_PALETTE_RGB24; + if (ioctl(videoDev, VIDIOCMCAPTURE, &vm) == 0 && ioctl(videoDev, VIDIOCSYNC, &vm.frame) == 0) { + // make RGB out of BGR: + int memsize = vm.width * vm.height; + unsigned char *mem1 = mem; + for (int i = 0; i < memsize; i++) { + unsigned char tmp = mem1[2]; + mem1[2] = mem1[0]; + mem1[0] = tmp; + mem1 += 3; + } + + if (Quality < 0) + Quality = 100; + + isyslog("grabbing to %s %d %d %d", Jpeg ? "JPEG" : "PNM", Quality, vm.width, vm.height); + if (Jpeg) { + // convert to JPEG: + result = RgbToJpeg(mem, vm.width, vm.height, Size, Quality); + if (!result) + esyslog("ERROR: failed to convert image to JPEG"); + } + else { + // convert to PNM: + char buf[32]; + snprintf(buf, sizeof(buf), "P6\n%d\n%d\n255\n", vm.width, vm.height); + int l = strlen(buf); + int bytes = memsize * 3; + Size = l + bytes; + result = MALLOC(uchar, Size); + if (result) { + memcpy(result, buf, l); + memcpy(result + l, mem, bytes); + } + else + esyslog("ERROR: failed to convert image to PNM"); } } - fclose(f); - } - else { - LOG_ERROR_STR(FileName); - result |= 1; } munmap(mem, msize); } else - result |= 1; + esyslog("ERROR: failed to memmap video device"); } close(videoDev); - return result == 0; + return result; } - return false; + else + LOG_ERROR_STR(buffer); + return NULL; } void cDvbDevice::SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat) @@ -754,7 +775,7 @@ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *Ne if (Channel->Vpid() && !HasPid(Channel->Vpid()) || Channel->Apid(0) && !HasPid(Channel->Apid(0))) { #ifdef DO_MULTIPLE_RECORDINGS #ifndef DO_MULTIPLE_CA_CHANNELS - if (Ca() > CACONFBASE || Channel->Ca() > CACONFBASE) + if (Ca() >= CA_ENCRYPTED_MIN || Channel->Ca() >= CA_ENCRYPTED_MIN) needsDetachReceivers = Ca() != Channel->Ca(); else #endif @@ -825,6 +846,11 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView) esyslog("ERROR: failed to set PIDs for channel %d on device %d", Channel->Number(), CardIndex() + 1); return false; } + //XXX quick workaround for additional live audio PIDs: + if (ciHandler) { + ciHandler->SetPid(Channel->Apid(1), true); + ciHandler->SetPid(Channel->Dpid(0), true); + } if (IsPrimaryDevice()) AddPid(Channel->Tpid(), ptTeletext); CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true)); // actually one would expect 'false' here, but according to Marco Schlüßler <marco@lordzodiac.de> this works diff --git a/dvbdevice.h b/dvbdevice.h index ed0cef8..cc9d126 100644 --- a/dvbdevice.h +++ b/dvbdevice.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbdevice.h 1.36 2005/11/11 14:51:38 kls Exp $ + * $Id: dvbdevice.h 1.37 2005/12/29 13:33:12 kls Exp $ */ #ifndef __DVBDEVICE_H @@ -85,7 +85,7 @@ private: static int devVideoOffset; int devVideoIndex; public: - virtual bool GrabImage(const char *FileName, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1); + virtual uchar *GrabImage(int &Size, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1); // Video format facilities @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbosd.c 1.27 2005/05/22 10:57:45 kls Exp $ + * $Id: dvbosd.c 1.29 2005/12/30 15:41:54 kls Exp $ */ #include "dvbosd.h" @@ -39,7 +39,7 @@ cDvbOsd::cDvbOsd(int Left, int Top, int OsdDev) osdDev = OsdDev; shown = false; if (osdDev < 0) - esyslog("ERROR: illegal OSD device handle (%d)!", osdDev); + esyslog("ERROR: invalid OSD device handle (%d)!", osdDev); else { osdMem = MAXOSDMEMORY; #ifdef OSD_CAP_MEMSIZE @@ -88,6 +88,8 @@ eOsdError cDvbOsd::CanHandleAreas(const tArea *Areas, int NumAreas) return oeBppNotSupported; if ((Areas[i].Width() & (8 / Areas[i].bpp - 1)) != 0) return oeWrongAlignment; + if (Areas[i].Width() < 1 || Areas[i].Height() < 1 || Areas[i].Width() > 720 || Areas[i].Height() > 576) + return oeWrongAreaSize; TotalMemory += Areas[i].Width() * Areas[i].Height() / (8 / Areas[i].bpp); } if (TotalMemory > osdMem) diff --git a/dvbplayer.c b/dvbplayer.c index fd640d3..efd767f 100644 --- a/dvbplayer.c +++ b/dvbplayer.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbplayer.c 1.41 2005/10/31 12:33:48 kls Exp $ + * $Id: dvbplayer.c 1.42 2006/01/08 11:39:41 kls Exp $ */ #include "dvbplayer.h" @@ -621,7 +621,7 @@ int cDvbPlayer::SkipFrames(int Frames) int Current, Total; GetIndex(Current, Total, true); int OldCurrent = Current; - // As GetNextIFrame() increments/decrements at least once, the + // As GetNextIFrame() increments/decrements at least once, the // destination frame (= Current + Frames) must be adjusted by // -1/+1 respectively. Current = index->GetNextIFrame(Current + Frames + (Frames > 0 ? -1 : 1), Frames > 0); @@ -8,16 +8,15 @@ * * parts of this file are derived from the OMS program. * - * $Id: dvbspu.c 1.17 2005/11/05 12:08:15 kls Exp $ + * $Id: dvbspu.c 1.19 2006/01/08 11:39:46 kls Exp $ */ +#include "dvbspu.h" #include <assert.h> #include <string.h> #include <inttypes.h> #include <math.h> - #include "device.h" -#include "dvbspu.h" /* * cDvbSpubitmap: @@ -28,7 +27,7 @@ * Inputs: * - a SPU rle encoded image on creation, which will be decoded into * the full screen indexed bitmap - * + * * Output: * - a minimal sized cDvbSpuBitmap a given palette, the indexed bitmap * will be scanned to get the smallest possible resulting bitmap considering @@ -436,7 +435,7 @@ int cDvbSpuDecoder::setTime(uint32_t pts) prev_DCSQ_offset = DCSQ_offset; DCSQ_offset = spuU32(i); - DEBUG("offs = %d, DCSQ = %d, prev_DCSQ = %d\n", + DEBUG("offs = %d, DCSQ = %d, prev_DCSQ = %d\n", i, DCSQ_offset, prev_DCSQ_offset); i += 2; @@ -8,16 +8,16 @@ * * parts of this file are derived from the OMS program. * - * $Id: dvbspu.h 1.10 2005/11/05 12:08:47 kls Exp $ + * $Id: dvbspu.h 1.11 2006/01/05 10:18:31 kls Exp $ */ #ifndef __DVBSPU_H #define __DVBSPU_H #include <inttypes.h> - #include "osd.h" #include "spu.h" +#include "thread.h" typedef struct sDvbSpuPalDescr { uint8_t index; @@ -8,7 +8,7 @@ * Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>. * Adapted to 'libsi' for VDR 1.3.0 by Marcel Wiesweg <marcel.wiesweg@gmx.de>. * - * $Id: eit.c 1.112 2005/11/04 14:19:16 kls Exp $ + * $Id: eit.c 1.113 2005/12/26 11:50:09 kls Exp $ */ #include "eit.h" @@ -43,6 +43,8 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data) bool Empty = true; bool Modified = false; + time_t SegmentStart = 0; + time_t SegmentEnd = 0; SI::EIT::Event SiEitEvent; for (SI::Loop::Iterator it; eventLoop.getNext(SiEitEvent, it); ) { @@ -50,6 +52,9 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data) if (SiEitEvent.getStartTime() == 0 || SiEitEvent.getStartTime() > 0 && SiEitEvent.getDuration() == 0) continue; Empty = false; + if (!SegmentStart) + SegmentStart = SiEitEvent.getStartTime(); + SegmentEnd = SiEitEvent.getStartTime() + SiEitEvent.getDuration(); cEvent *newEvent = NULL; cEvent *rEvent = NULL; cEvent *pEvent = (cEvent *)pSchedule->GetEvent(SiEitEvent.getEventId(), SiEitEvent.getStartTime()); @@ -242,6 +247,7 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data) pSchedule->SetPresentSeen(); if (Modified) { pSchedule->Sort(); + pSchedule->DropOutdated(SegmentStart, SegmentEnd, Tid, getVersionNumber()); Schedules->SetModified(pSchedule); } } @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: eitscan.c 1.29 2005/11/05 15:24:36 kls Exp $ + * $Id: eitscan.c 1.30 2006/01/07 14:10:17 kls Exp $ */ #include "eitscan.h" @@ -147,7 +147,7 @@ void cEITScanner::Process(void) for (cScanData *ScanData = scanList->First(); ScanData; ScanData = scanList->Next(ScanData)) { const cChannel *Channel = ScanData->GetChannel(); if (Channel) { - if (!Channel->Ca() || Channel->Ca() == Device->DeviceNumber() + 1 || Channel->Ca() >= 0x0100) { + if (!Channel->Ca() || Channel->Ca() == Device->DeviceNumber() + 1 || Channel->Ca() >= CA_ENCRYPTED_MIN) { if (Device->ProvidesTransponder(Channel)) { if (!Device->Receiving()) { bool MaySwitchTransponder = Device->MaySwitchTransponder(); @@ -7,7 +7,7 @@ * Original version (as used in VDR before 1.3.0) written by * Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>. * - * $Id: epg.c 1.40 2005/11/11 13:37:43 kls Exp $ + * $Id: epg.c 1.47 2005/12/30 15:41:59 kls Exp $ */ #include "epg.h" @@ -234,7 +234,7 @@ void cEvent::Dump(FILE *f, const char *Prefix, bool InfoOnly) const { if (InfoOnly || startTime + duration + Setup.EPGLinger * 60 >= time(NULL)) { if (!InfoOnly) - fprintf(f, "%sE %u %ld %d %X\n", Prefix, eventID, startTime, duration, tableID); + fprintf(f, "%sE %u %ld %d %X %X\n", Prefix, eventID, startTime, duration, tableID, version); if (!isempty(title)) fprintf(f, "%sT %s\n", Prefix, title); if (!isempty(shortText)) @@ -296,8 +296,9 @@ bool cEvent::Read(FILE *f, cSchedule *Schedule) time_t StartTime; int Duration; unsigned int TableID = 0; - int n = sscanf(t, "%u %ld %d %X", &EventID, &StartTime, &Duration, &TableID); - if (n == 3 || n == 4) { + unsigned int Version = 0xFF; + int n = sscanf(t, "%u %ld %d %X %X", &EventID, &StartTime, &Duration, &TableID, &Version); + if (n >= 3 && n <= 5) { Event = (cEvent *)Schedule->GetEvent(EventID, StartTime); cEvent *newEvent = NULL; if (Event) @@ -306,6 +307,7 @@ bool cEvent::Read(FILE *f, cSchedule *Schedule) Event = newEvent = new cEvent(EventID); if (Event) { Event->SetTableID(TableID); + Event->SetVersion(Version); Event->SetStartTime(StartTime); Event->SetDuration(Duration); if (newEvent) @@ -635,7 +637,6 @@ void cSchedule::DelEvent(cEvent *Event) if (Event->schedule == this) { UnhashEvent(Event); events.Del(Event); - Event->schedule = NULL; } } @@ -737,6 +738,31 @@ void cSchedule::Sort(void) events.Sort(); } +void cSchedule::DropOutdated(time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version) +{ + if (SegmentStart > 0 && SegmentEnd > 0) { + for (cEvent *p = events.First(); p; p = events.Next(p)) { + if (p->EndTime() > SegmentStart) { + if (p->StartTime() < SegmentEnd) { + // The event overlaps with the given time segment. + if (p->TableID() > TableID || p->TableID() == TableID && p->Version() != Version) { + // The segment overwrites all events from tables with higher ids, and + // within the same table id all events must have the same version. + // We can't delete the event right here because a timer might have + // a pointer to it, so let's set its id and start time to 0 to have it + // "phased out": + UnhashEvent(p); + p->eventID = 0; + p->startTime = 0; + } + } + else + break; + } + } + } +} + void cSchedule::Cleanup(void) { Cleanup(time(NULL)); @@ -745,15 +771,12 @@ void cSchedule::Cleanup(void) void cSchedule::Cleanup(time_t Time) { cEvent *Event; - for (int a = 0; true ; a++) { - Event = events.Get(a); - if (!Event) - break; - if (!Event->HasTimer() && Event->EndTime() + Setup.EPGLinger * 60 + 3600 < Time) { // adding one hour for safety - DelEvent(Event); - a--; - } - } + while ((Event = events.First()) != NULL) { + if (!Event->HasTimer() && Event->EndTime() + Setup.EPGLinger * 60 + 3600 < Time) // adding one hour for safety + DelEvent(Event); + else + break; + } } void cSchedule::Dump(FILE *f, const char *Prefix, eDumpMode DumpMode, time_t AtTime) const @@ -811,7 +834,7 @@ bool cSchedule::Read(FILE *f, cSchedules *Schedules) } } else { - esyslog("ERROR: illegal channel ID: %s", s); + esyslog("ERROR: invalid channel ID: %s", s); return false; } } @@ -871,7 +894,7 @@ void cSchedules::Cleanup(bool Force) time_t now = time(NULL); struct tm tm_r; struct tm *ptm = localtime_r(&now, &tm_r); - if (now - lastCleanup > 3600 && ptm->tm_hour == 5) { + if (now - lastCleanup > 3600) { isyslog("cleaning up schedules data"); cSchedulesLock SchedulesLock(true, 1000); cSchedules *s = (cSchedules *)Schedules(SchedulesLock); @@ -880,7 +903,8 @@ void cSchedules::Cleanup(bool Force) p->Cleanup(now); } lastCleanup = now; - ReportEpgBugFixStats(true); + if (ptm->tm_hour == 5) + ReportEpgBugFixStats(true); } if (epgDataFileName && now - lastDump > 600) { cSafeFile f(epgDataFileName); @@ -7,7 +7,7 @@ * Original version (as used in VDR before 1.3.0) written by * Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>. * - * $Id: epg.h 1.26 2005/09/11 12:54:30 kls Exp $ + * $Id: epg.h 1.28 2005/12/27 14:31:24 kls Exp $ */ #ifndef __EPG_H @@ -67,6 +67,7 @@ public: ~cEvent(); virtual int Compare(const cListObject &ListObject) const; tChannelID ChannelID(void) const; + const cSchedule *Schedule(void) const { return schedule; } u_int16_t EventID(void) const { return eventID; } uchar TableID(void) const { return tableID; } uchar Version(void) const { return version; } @@ -128,6 +129,7 @@ public: void ClrRunningStatus(cChannel *Channel = NULL); void ResetVersions(void); void Sort(void); + void DropOutdated(time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version); void Cleanup(time_t Time); void Cleanup(void); cEvent *AddEvent(cEvent *Event); diff --git a/genfontfile.c b/genfontfile.c index af45ad5..9189167 100644 --- a/genfontfile.c +++ b/genfontfile.c @@ -154,9 +154,9 @@ SuckGlyphsFromServer(Display * dpy, Font font) character.byte2 = (i + fontinfo->min_char_or_byte2) & 255; character.byte1 = (i + fontinfo->min_char_or_byte2) >> 8; - /* XXX we could use XDrawImageString16 which would also paint the backing + /* XXX we could use XDrawImageString16 which would also paint the backing - rectangle but X server bugs in some scalable font rasterizers makes it + rectangle but X server bugs in some scalable font rasterizers makes it more effective to do XFillRectangles to clear the pixmap and XDrawImage16 for the text. */ @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: i18n.c 1.220 2005/11/04 14:36:27 kls Exp $ + * $Id: i18n.c 1.230 2006/01/08 11:56:31 kls Exp $ * * Translations provided by: * @@ -221,7 +221,7 @@ const tI18nPhrase Phrases[] = { "Ajastimet", "Timery", "Timer", - "×ñïíïäéáêüðôçò", + "×ñïíïðñïãñáììáôéóìïß", "Timers", "Timer-e", "Felvétel beprogramozása", @@ -263,7 +263,7 @@ const tI18nPhrase Phrases[] = { "Tallenteen tiedot", "",// TODO "",// TODO - "ÅããñáöÞ", + "Ðëçñïöïñßåò EããñáöÞò", "Inspelning", "Detaliile înregistrãrii", "",// TODO @@ -347,7 +347,7 @@ const tI18nPhrase Phrases[] = { "Muokkaa kanavaa", "Ustawienie kanalu", "Modificar canal", - "ÐñïóáñìïãÝò Êáíáëéïý", + "Ôñïðïðïßçóç Êáíáëéïý", "Ändra kanal", "Modificare canal", "Csatornák beállítása", @@ -368,7 +368,7 @@ const tI18nPhrase Phrases[] = { "Muokkaa ajastinta", "Ustawienie timerow", "Modificar timer", - "ÐñïóáñìïãÞ ÷ñïíïäéáêüðôç", + "Ôñïðïðïßçóç ÷ñïíïðñïãñáììáôéóìïý", "Ändra timer", "Modificare timer", "Felvétel beprogramozásának megváltoztatása", @@ -399,27 +399,6 @@ const tI18nPhrase Phrases[] = { "Sündmus", "Udsendelse", }, - { "Summary", - "Inhalt", - "Vsebina", - "Sommario", - "Inhoud", - "Resumo", - "Résumé", - "Sammendrag", - "Yhteenveto", - "Zawartosc", - "Resúmen", - "Ðåñéå÷üìåíï", - "Sammanfattning", - "Detalii", - "Tartalom", - "Sinopsi", - "¾ßØáÐÝØÕ", - "Sadr¾aj", - "Kokkuvõte", - "Omtale", - }, { "Info", "Info", "Info", @@ -504,8 +483,29 @@ const tI18nPhrase Phrases[] = { "Järgmisena eetris", "Hvad vises som det næste?", }, + { "Summary", + "Inhalt", + "Vsebina", + "Sommario", + "Inhoud", + "Resumo", + "Résumé", + "Sammendrag", + "Yhteenveto", + "Zawartosc", + "Resumen", + "Ðåñéå÷üìåíï", + "Sammanfattning", + "Cuprins", + "Tartalom", + "Resum", + "¾ßØáÐÝØÕ", + "Sadr¾aj", + "Kokkuvõte", + "Omtale", + }, // Button texts (should not be more than 10 characters!): - { "Edit", + { "Button$Edit", "Editieren", "Uredi", "Modifica", @@ -526,7 +526,7 @@ const tI18nPhrase Phrases[] = { "Muuda", "Rediger", }, - { "New", + { "Button$New", "Neu", "Novo", "Nuovo", @@ -547,7 +547,7 @@ const tI18nPhrase Phrases[] = { "Uus", "Ny", }, - { "Delete", + { "Button$Delete", "Löschen", "Izbri¹i", "Cancella", @@ -568,7 +568,7 @@ const tI18nPhrase Phrases[] = { "Kustuta", "Slet", }, - { "Mark", + { "Button$Mark", "Markieren", "Oznaèi", "Marca", @@ -589,7 +589,7 @@ const tI18nPhrase Phrases[] = { "Siirda", "Markér", }, - { "On/Off", + { "Button$On/Off", "Ein/Aus", "Vklop/Izklop", "On/Off", @@ -610,7 +610,7 @@ const tI18nPhrase Phrases[] = { "Sees/Väljas", "Til/Fra", }, - { "Record", + { "Button$Record", "Aufnehmen", "Posnemi", "Registra", @@ -631,7 +631,7 @@ const tI18nPhrase Phrases[] = { "Salvesta", "Optag", }, - { "Play", + { "Button$Play", "Wiedergabe", "Predvajaj", "Riproduci", @@ -642,7 +642,7 @@ const tI18nPhrase Phrases[] = { "Toista", "Odtwarzac", "Play", - "ÁíáìåôÜäïóç", + "ÁíáðáñáãùãÞ", "Spela upp", "Redare", "Lejátszani", @@ -652,7 +652,28 @@ const tI18nPhrase Phrases[] = { "Start", "Afspil", }, - { "Rewind", + { "Button$Pause", + "Pause", + "Pavza", + "Pausa", + "Pauze", + "",// TODO + "Pause", + "",// TODO + "Tauko", + "Przerwa", + "Pausa", + "Ðáýóç", + "Pausa", + "Pauzã", + "Szünet", + "Pausa", + "¿Ðã×Ð", + "Pauza", + "Paus", + "Pause", + }, + { "Button$Rewind", "Anfang", "Na zaèetek", "Da inizio", @@ -663,7 +684,7 @@ const tI18nPhrase Phrases[] = { "Alkuun", "Poczatek", "Rebobinar", - "Áñ÷Þ", + "ÌåôáöïñÜ óôçí Áñ÷Þ", "Återspolning", "Înapoi", "Vissza az elejére", @@ -694,7 +715,7 @@ const tI18nPhrase Phrases[] = { "Stopp", "Stop", }, - { "Resume", + { "Button$Resume", "Weiter", "Nadaljuj", "Riprendi", @@ -705,7 +726,7 @@ const tI18nPhrase Phrases[] = { "Jatka", "Dalej", "Continuar", - "ÓõíÝ÷åéá", + "ÅðáíáöïñÜ", "Fortsätt", "Revenire", "Tovább", @@ -715,28 +736,7 @@ const tI18nPhrase Phrases[] = { "Jätka", "Fortsæt", }, - { "Summary", - "Inhalt", - "Vsebina", - "Sommario", - "Inhoud", - "Resumo", - "Résumé", - "Sammendrag", - "Yhteenveto", - "Zawartosc", - "Resumen", - "Ðåñéå÷üìåíï", - "Sammanfattning", - "Cuprins", - "Tartalom", - "Resum", - "¾ßØáÐÝØÕ", - "Sadr¾aj", - "Kokkuvõte", - "Omtale", - }, - { "Open", + { "Button$Open", "Öffnen", "Odpri", "Apri", @@ -747,7 +747,7 @@ const tI18nPhrase Phrases[] = { "Avaa", "Otworzyc", "Abrir", - "¢íïéãìá", + "Áíïéãìá", "Öppna", "Deschide", "Kinyitni", @@ -757,7 +757,7 @@ const tI18nPhrase Phrases[] = { "Ava", "Åbn", }, - { "Switch", + { "Button$Switch", "Umschalten", "Preklopi", "Cambia", @@ -778,7 +778,7 @@ const tI18nPhrase Phrases[] = { "Vali", "Skift", }, - { "Now", + { "Button$Now", "Jetzt", "Sedaj", "Adesso", @@ -799,7 +799,7 @@ const tI18nPhrase Phrases[] = { "Nüüd", "Nu", }, - { "Next", + { "Button$Next", "Nächste", "Sledi", "Prossimo", @@ -841,49 +841,7 @@ const tI18nPhrase Phrases[] = { "Kava", "Program", }, - { "Language", - "Sprache", - "Jezik", - "Linguaggio", - "Taal", - "Linguagem", - "Langue", - "Språk", - "Kieli", - "Jezyk", - "Idioma", - "Ãëþóóá", - "Språk", - "Limba", - "Nyelv", - "Idioma", - "Ï×ëÚ", - "Jezik", - "Keel", - "Sprog", - }, - { "Eject", - "Auswerfen", - "Izvr¾i", - "Eject", - "Eject", - "Ejectar", - "Ejection", - "Eject", - "Avaa", - "Wyrzucenie", - "Expulsar", - "ÅîáãùãÞ", - "Mata ut", - "Ejecteazã", - "Kidobni", - "Expulsar", - "¸×ÒÛÕçì", - "Izbaci", - "Ava", - "Skub ud", - }, - { "ABC/abc", + { "Button$ABC/abc", "ABC/abc", "ABC/abc", "ABC/abc", @@ -904,7 +862,7 @@ const tI18nPhrase Phrases[] = { "ABC/abc", "ABC/abc", }, - { "Insert", + { "Button$Insert", "Einfügen", "Vstavi", "Inserisci", @@ -925,7 +883,7 @@ const tI18nPhrase Phrases[] = { "Lisa (INS)", "Indsæt", }, - { "Overwrite", + { "Button$Overwrite", "Überschreiben", "Prepi¹i", "Sovrascrivi", @@ -946,7 +904,7 @@ const tI18nPhrase Phrases[] = { "Asenda (OVR)", "Overskriv", }, - { "Menu", + { "Button$Menu", "Menü", "Meni", "Menù", @@ -967,7 +925,7 @@ const tI18nPhrase Phrases[] = { "Menüü", "Menu", }, - { "Reset", + { "Button$Reset", "Reset", "Reset", "Reset", @@ -988,7 +946,7 @@ const tI18nPhrase Phrases[] = { "Nullimine", "Nulstille", }, - { "Scan", + { "Button$Scan", "Scan", "I¹èi", "Scansione", @@ -1009,6 +967,27 @@ const tI18nPhrase Phrases[] = { "Uuenda", "Skan", }, + { "Button$Audio", + "Audio", + "Zvok", + "",// TODO + "Audio", + "",// TODO + "Audio", + "",// TODO + "Ääni", + "",// TODO + "",// TODO + "¹÷ïò", + "Ljud", + "Sunet", + "",// TODO + "",// TODO + "Ï×ëÚ", + "",// TODO + "Audio", + "Audio", + }, // Confirmations: { "Delete channel?", "Kanal löschen?", @@ -1042,7 +1021,7 @@ const tI18nPhrase Phrases[] = { "Poistetaanko ajastin?", "Usunac timer?", "¿Eliminar timer?", - "ÄéáãñáöÞ ÷ñïíïäéáêüðôç?", + "ÄéáãñáöÞ ÷ñïíïðñïãñáììáôéóìïý;?", "Ta bort timern?", "ªterg timer-ul?", "Felvétel beprogramozásának törlése?", @@ -1063,7 +1042,7 @@ const tI18nPhrase Phrases[] = { "Poistetaanko tallenne?", "Usunac nagranie?", "¿Eliminar grabacion?", - "ÄéáãñáöÞ áñ÷åßïõ?", + "ÄéáãñáöÞ åããñáöÞò?", "Ta bort inspelningen?", "ªterg înregistrarea?", "Felvétel törlése?", @@ -1084,7 +1063,7 @@ const tI18nPhrase Phrases[] = { "Ajastettu tallennus käynnissä - poistetaanko silti?", "Nagrywanie w trakcie - napewno usunac?", "¿Timer activo - de verdad eliminarlo?", - "×ñïíïäéáêüðôçò óÝ åîÝëéîç - ÄéáãñáöÞ óßãïõñá?", + "×ñïíïðñïãñáììáôéóìüò óÝ åîÝëéîç - ÄéáãñáöÞ óßãïõñá?", "Timerstyrd inspelning pågår - Avbryta ändå?", "Timer-ul tocmai înregistreazã - ºterg, totuºi?", "Felvétel folyamatban van - mégis törölni?", @@ -1115,27 +1094,6 @@ const tI18nPhrase Phrases[] = { "Lõpetan salvestamise?", "Stop optagelse?", }, - { "on primary interface", - "auf dem primären Interface", - "na primarni napravi", - "su interfaccia primaria", - "op eerste interface", - "no interface primário", - "sur la carte primaire", - "på første enhet", - "ensisijaisella sovittimella", - "na pierwszym interfejsie", - "en interface primario", - "óôÞí êýñéá êÜñôá", - "från den första enheten?", - "pe prima interfaþã", - "az elsö kártyán", - "a la interfície primària", - "ÝÐ ÞáÝÞÒÝÞÜ ãáâàÞÙáâÒÕ", - "na primarnom ureðaju", - "peavastuvõtjal", - "på primær enhed", - }, { "Cancel editing?", "Schneiden abbrechen?", "®elite prekiniti urejanje?", @@ -1337,7 +1295,7 @@ const tI18nPhrase Phrases[] = { "Lähde", "Zrodlo", "",//TODO - "Äéåñåýíçóç", + "ÐçãÞ", "Källa", "Sursã", "Forrás", @@ -1536,6 +1494,48 @@ const tI18nPhrase Phrases[] = { "CA", "CA", }, + { "Free To Air", + "frei empfangbar", + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + }, + { "encrypted", + "verschlüsselt", + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + }, { "Sid", "Sid", "Sid", @@ -1568,7 +1568,7 @@ const tI18nPhrase Phrases[] = { "Inversio", "Inversion", "Inversion", - "Inversion", + "ÁíôéóôñïöÞ", "Inversion", "Inversiune", "Inversion", @@ -1589,7 +1589,7 @@ const tI18nPhrase Phrases[] = { "Kaistanleveys", "Szerokosc pasma", "Bandwidth", - "Bandwidth", + "Åýñïò Óõ÷íïôÞôùí", "Bandbredd", "Lãrgime de bandã", "Bandwidth", @@ -1610,7 +1610,7 @@ const tI18nPhrase Phrases[] = { "Suojaustaso (HP)", "CoderateH", "CoderateH", - "CoderateH", + "Ñõèìüò Êþäéêá H", "CoderateH", "CoderateH", "CoderateH", @@ -1631,7 +1631,7 @@ const tI18nPhrase Phrases[] = { "Suojaustaso (LP)", "CoderateL", "CoderateL", - "CoderateL", + "Ñõèìüò Êþäéêá L", "CoderateL", "CoderateL", "CoderateL", @@ -1652,7 +1652,7 @@ const tI18nPhrase Phrases[] = { "Modulaatio", "Modulacja", "Modulation", - "Modulation", + "Äéáìüñöùóç", "Modulation", "Modulaþie", "Modulation", @@ -1673,7 +1673,7 @@ const tI18nPhrase Phrases[] = { "Transmissio", "Transmisja", "Transmission", - "Transmission", + "ÌåôÜäïóç", "Transmission", "Transmisiune", "Transmission", @@ -1694,7 +1694,7 @@ const tI18nPhrase Phrases[] = { "Suojaväli", "Guard", "Guard", - "Guard", + "Ðñïóôáóßá", "Guard", "Guard", "Guard", @@ -1948,7 +1948,7 @@ const tI18nPhrase Phrases[] = { "Kanava on ajastimen käytössä!", "Kanal jest zajety przez timer nagran", "¡Canal está ocupado por un timer!", - "Ôï êáíÜëç ÷ñéóéìïðïéåßôáé áðü ÷ñïíïäéáêüðôç!", + "Ôï êáíÜëç ÷ñéóéìïðïéåßôáé áðü ÷ñïíïðñïãñáììáôéóìü!", "Kanalen används av en timer!", "Canalul este utilizat de un timer!", "Csatornát más használja!", @@ -2053,7 +2053,7 @@ const tI18nPhrase Phrases[] = { "*** Virheellinen kanavavalinta! ***", "*** Niewazny kanal ***", "*** Canal inválido ***", - "*** ¢êõñï êáíÜëç ***", + "*** Áêõñï êáíÜëç ***", "*** Felaktig kanal ***", "*** Canal invalid ***", "*** Érvénytelen csatorna ***", @@ -2347,7 +2347,7 @@ const tI18nPhrase Phrases[] = { "CA-moduulin palautus alkutilaan epäonnistui!", "Nieudany CAM-Reset!", "¡No puedo reiniciar la CAM!", - "Áäýíáôï íá ãßíåé åðáíáöïñÜ óôü CAM", + "Áäýíáôç ç åðáíáöïñÜ óôü CAM", "Kan inte återställa CAM!", "Nu pot reseta CAM", "A CAM-Reset nem sikerült", @@ -2368,7 +2368,7 @@ const tI18nPhrase Phrases[] = { "CA-moduuli palautettu alkutilaan", "CAM-Reset wykonany", "CAM reiniciada", - "Óôï CAM Ýãéíå åðáíáöïñÜ", + "¸ãéíå åðáíáöïñÜ óôï CAM", "CA modulen har återställts", "CAM-ul a fost resetat", "A CAM vissza lett állítva", @@ -2410,7 +2410,7 @@ const tI18nPhrase Phrases[] = { "Äänen kieli ei ole valittavissa!", "",//TODO "",//TODO - "ÁíåðÜñêåéá Þ÷ïõ", + "Ìç äéáèÝóéìïò Þ÷ïò", "Ljud saknas!" "Lipseºte sunetul!", "",//TODO @@ -2453,7 +2453,7 @@ const tI18nPhrase Phrases[] = { "Ohjelmaopas", "EPG", "Guía de Programación", - "Ïäçãüò ðñïãñÜììáôïò", + "Çëåêôñïíéêüò ïäçãüò ðñïãñÜììáôïò", "EPG", "EPG", "EPG", @@ -2558,7 +2558,7 @@ const tI18nPhrase Phrases[] = { "Toisto", "Odtwarzanie", "Opciones de Reproducción", - "ÁíáìåôÜäïóç", + "ÁíáðáñáãùãÞ", "Uppspelning", "Redare", "Lejátszás", @@ -2600,7 +2600,7 @@ const tI18nPhrase Phrases[] = { "Laajennokset", "Plugins", "Plugins", - "Âßóìáôá", + "ÅðåêôÜóåéò", "Moduler", "Plugin-uri", "Plugins", @@ -2621,7 +2621,7 @@ const tI18nPhrase Phrases[] = { "Laajennos", "Plugin", "Plugin", - "Âßóìá", + "ÅðÝêôáóç", "Modul", "Plugin (modul adiþional)", "Plugin", @@ -2968,6 +2968,27 @@ const tI18nPhrase Phrases[] = { "Kanaliteate näitamine", "Info ved kanalskift", }, + { "Setup.OSD$Timeout requested channel info", + "Angeforderte Kanalinfo schließen", + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + "", // TODO + }, { "Setup.OSD$Scroll pages", "Seitenweise scrollen", "Drsni meni", @@ -2979,7 +3000,7 @@ const tI18nPhrase Phrases[] = { "Valikoiden vieritys sivuttain", "Przesuwac stronami", "Desplazar página entera", - "Êßëéóç óåëßäáò", + "Êýëéóç óåëßäáò", "Bläddra sidor", "Deruleazã pagini", "Oldalanként léptetmi", @@ -3000,7 +3021,7 @@ const tI18nPhrase Phrases[] = { "Valikoiden vieritys ympäri", "",// TODO "",// TODO - "Êßëéóç ãýñù-ãýñù", + "Êýëéóç ãýñù-ãýñù", "Rulla texten", "Derulare circularã", "",// TODO @@ -3021,7 +3042,7 @@ const tI18nPhrase Phrases[] = { "Järjestä ajastimet", "Sortowanie timerow", "Ordenar timers", - "ÏñãÜíùóç ðñïãñáìáôéóìÝíùí", + "ÏñãÜíùóç ÷ñïíïðñïãñáììáôéóìþí", "Sortera timers", "Sortare timer-e", "A beprogramozott felvételek elrendezése", @@ -3063,7 +3084,7 @@ const tI18nPhrase Phrases[] = { "Ohjelmaoppaan taustapäivitys (h)", "Czas do skanu EPG (h)", "Tiempo hasta exploración EPG (h)", - "×ñüíïò ìÝ÷ñé åîÝôáóç EPG óå þñåò", + "×ñüíïò äéÜñêåéáò åîÝôáóçò EPG óå þñåò", "EPG sökning timeout", "Interval achiziþie EPG (h)", "Fennmaradt idö az EPG-g (h)", @@ -3084,7 +3105,7 @@ const tI18nPhrase Phrases[] = { "Ohjelmaoppaan korjaustaso", "Poziom bledow EPG", "Nivel para arreglar EPG", - "Âáèìüò äéüñèïóçò ïäçãïý EPG", + "Âáèìüò äéüñèùóçò ïäçãïý EPG", "Nivå för EPG bugfix", "Nivel corecþie EPG", "EPG hibaelhárítás", @@ -3105,7 +3126,7 @@ const tI18nPhrase Phrases[] = { "Vanha tieto näkyy (min)", "",// TODO "",// TODO - "¸íäåéêóç îåðåñáóìÝíïí ðëçñïöïñéþí (ëåðôÜ)", + "¸íäåéîç îåðåñáóìÝíùí ðëçñïöïñéþí (ëåðôÜ)", "Visa gammal information (min)", "Date EPG expirate cel mult (min)", "",// TODO @@ -3168,7 +3189,7 @@ const tI18nPhrase Phrases[] = { "Suosikkikielet", "",// TODO "",// TODO - "Ðñïôéíüìåíåò ãëþóåò", + "Ðñïôåéíüìåíåò ãëþóóåò", "Önskade språk", "Limbi preferate", "",// TODO @@ -3189,7 +3210,7 @@ const tI18nPhrase Phrases[] = { "Suosikkikieli", "",// TODO "",// TODO - "Ðñïôéíüìåíç ãëþóá", + "Ðñïôåéíüìåíç ãëþóóá", "Önskat språk", "Limba preferatã", "",// TODO @@ -3231,7 +3252,7 @@ const tI18nPhrase Phrases[] = { "Näyttömuoto", "",//TODO "",//TODO - "ÌïñöÞ Ýíäåéêóçò Âßíôåï", + "ÌïñöÞ áðåéêüíéóçò Âßíôåï", "Format för video display", "Formatul redãrii video", "",//TODO @@ -3336,7 +3357,7 @@ const tI18nPhrase Phrases[] = { "Käytä Dolby Digital -ääntä", "",//TODO "",//TODO - "×ñçóéìïðïßçóç Þ÷ïõ Dolby Digital", + "×ñÞóç Þ÷ïõ Dolby Digital", "Använd Dolby Digital", "Sunet Dolby Digital", "",//TODO @@ -3357,7 +3378,7 @@ const tI18nPhrase Phrases[] = { "Päivitä kanavat", "",// TODO "",// TODO - "áíáíÝïóç êáíáëéþí", + "ÅíçìÝñùóç êáíáëéþí", "Uppdatera kanaler", "Actualizare canale", "",// TODO @@ -3378,7 +3399,7 @@ const tI18nPhrase Phrases[] = { "vain nimet", "",// TODO "",// TODO - "ìüíï üíïìá", + "ìüíï ïíüìáôá", "bara namn", "doar numele", "",// TODO @@ -3399,7 +3420,7 @@ const tI18nPhrase Phrases[] = { "nimet ja PID:it", "",// TODO "",// TODO - "üíïìá êáß PID", + "Ïíüìáôá êáß PIDs", "namn och PID", "nume si PID-uri", "",// TODO @@ -3420,7 +3441,7 @@ const tI18nPhrase Phrases[] = { "uudet kanavat", "",// TODO "",// TODO - "ðñïóèÞêç íÝïí êáíáëéþí", + "ðñïóèÞêç íÝùí êáíáëéþí", "lägg till nya kanaler", "adãugare canale noi", "",// TODO @@ -3441,7 +3462,7 @@ const tI18nPhrase Phrases[] = { "uudet transponderit", "",// TODO "",// TODO - "ðñïóèÞêç íÝïí transponder", + "ðñïóèÞêç íÝïõ áíáìåôáäüôç", "lägg till nya transponders", "adãugare transpondere noi", "",// TODO @@ -3462,7 +3483,7 @@ const tI18nPhrase Phrases[] = { "Äänen kielet", "",//TODO "",//TODO - "Ãëþóåò Þ÷ïõ", + "Ãëþóóåò Þ÷ïõ", "Antal ljudspråk", "Limbi sunet", "",//TODO @@ -3483,7 +3504,7 @@ const tI18nPhrase Phrases[] = { "Äänen kieli", "",//TODO "",//TODO - "Ãëþóá Þ÷ïõ", + "Ãëþóóá Þ÷ïõ", "Ljudspråk", "Limba sunetului", "",//TODO @@ -3609,7 +3630,7 @@ const tI18nPhrase Phrases[] = { "Aloitusmarginaali (min)", "Poczatkowy czas buforowy (min)", "Comenzar grabación antes (min)", - "Ðñüóèåôïò ÷ñüíïò ðñßí áñ÷Þ (ëåðôÜ)", + "Ðñüóèåôïò ÷ñüíïò óôçí áñ÷Þ (ëåðôÜ)", "Marginal för start (min)", "Marjã la pornire (min)", "Idöeltolódás a kezdésnél (min)", @@ -3672,7 +3693,7 @@ const tI18nPhrase Phrases[] = { "Tallenteen oletusprioriteetti", "Priorytet pierwotny", "Prioridad predefinida", - "Ðñïôåñáéüôçôá", + "ÐñïêáèïñéóìÝíç ðñïôåñáéüôçôá", "Normal prioritet", "Prioritate implicitã", "Default priority", @@ -3798,7 +3819,7 @@ const tI18nPhrase Phrases[] = { "VPS-toiminnon aloitusmarginaali (s)", "",// TODO "",// TODO - "Ðåñéèüñéï VPS (ä)", + "Ðåñéèþñéï VPS (ä)", "VPS marginal (s)", "Marjã de timp la utilizare VPS (s)", "",// TODO @@ -4104,26 +4125,48 @@ const tI18nPhrase Phrases[] = { "ManTirOnsTorFreLørSøn", }, // The allowed characters in strings: - { " abcdefghijklmnopqrstuvwxyz0123456789-.#~", - " aäbcdefghijklmnoöpqrstuüvwxyz0123456789-.#~", - " abcèdefghijklmnopqrs¹tuvwxyz¾0123456789-.#~", - " aàbcdeéèfghiîjklmnopqrstuùvwxyz0123456789-.#~", - " abcdefghijklmnopqrstuvwxyz0123456789-.#~áäàïóöòúüù", - "",// TODO - " aàbcçdeéèêfghiîjklmnoôpqrstuùûvwxyz0123456789-.#~", - "",// TODO - " abcdefghijklmnopqrstuvwxyzåäö0123456789-.#~", - " abcdefghijklmnopqrstuvwxyz0123456789-.#~", - " aábcdeéfghiíjklmnñoópqrstuúvwxyz0123456789-.#~", - " áÜâãäåÝæçÞèéßêëìíîïüðñóòôõýö÷øùþ0123456789-.#~abcdefghijklmnopqrstuvwxyz", - " abcdefghijklmnopqrstuvwxyzåäö0123456789-.#~", - " aãâbcdefghiîjklmnopqrsºtþuvwxyz0123456789-.#~", - " aábcdeéfghiíjklmnoóöpqrstuúüvwxyz0123456789-.,#~", - " aàbcçdeéèfghiíjklmnoòpqrstuúvwxyz0123456789-.,#~_·", - " abcdefghijklmnopqrstuvwxyzÐÑÒÓÔÕñÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìîï0123456789-.#~", - " abcèædðefghijklmnopqrs¹tuvwxyz¾0123456789-.#~", // hrv - " abcdefghijklmnopqrsðzþtuvwõäöüxyå0123456789-.#~", - " abcdefghijklmnopqrstuvwxyzæøå0123456789-.#~", + { " abcdefghijklmnopqrstuvwxyz0123456789-.#~,/_@", + " aäbcdefghijklmnoöpqrstuüvwxyz0123456789-.#~,/_@", + " abcèdefghijklmnopqrs¹tuvwxyz¾0123456789-.#~,/_@", + " aàbcdeéèfghiîjklmnopqrstuùvwxyz0123456789-.#~,/_@", + " abcdefghijklmnopqrstuvwxyz0123456789-.#~,/_@áäàïóöòúüù", + "",// TODO + " aàbcçdeéèêfghiîjklmnoôpqrstuùûvwxyz0123456789-.#~,/_@", + "",// TODO + " abcdefghijklmnopqrstuvwxyzåäö0123456789-.#~,/_@", + " abcdefghijklmnopqrstuvwxyz0123456789-.#~,/_@", + " aábcdeéfghiíjklmnñoópqrstuúvwxyz0123456789-.#~,/_@", + " áÜâãäåÝæçÞèéßêëìíîïüðñóòôõýö÷øùþ0123456789-.#~,/_@abcdefghijklmnopqrstuvwxyz", + " abcdefghijklmnopqrstuvwxyzåäö0123456789-.#~,/_@", + " aãâbcdefghiîjklmnopqrsºtþuvwxyz0123456789-.#~,/_@", + " aábcdeéfghiíjklmnoóöpqrstuúüvwxyz0123456789-.,#~,/_@", + " aàbcçdeéèfghiíjklmnoòpqrstuúvwxyz0123456789-.,#~,/_@·", + " abcdefghijklmnopqrstuvwxyzÐÑÒÓÔÕñÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìîï0123456789-.#~,/_@", + " abcèædðefghijklmnopqrs¹tuvwxyz¾0123456789-.#~,/_@", // hrv + " abcdefghijklmnopqrsðzþtuvwõäöüxyå0123456789-.#~,/_@", + " abcdefghijklmnopqrstuvwxyzæøå0123456789-.#~,/_@", + }, + // The character maps for entering letters via the numeric keys: + { " 0\t-.#~,/_@1\tabc2\tdef3\tghi4\tjkl5\tmno6\tpqrs7\ttuv8\twxyz9", + " 0\t-.#~,/_@1\tabcä2\tdef3\tghi4\tjkl5\tmnoö6\tpqrs7\ttuvü8\twxyz9", + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO }, // Learning keys: { "Learning Remote Control Keys", @@ -4221,7 +4264,7 @@ const tI18nPhrase Phrases[] = { "Älä paina kaukosäätimen näppäimiä...", "Nie naciskac klawiszy...", "No pulse ninguna tecla...", - "ÌÞí ðéÝæåéò ðëÞêôñá...", + "ÌÞí ðáôÜò ðëÞêôñá...", "Tryck inte på någon knapp...", "Nu apãsaþi nici o tastã...", "Ne nyomjon meg gombot...", @@ -4368,7 +4411,7 @@ const tI18nPhrase Phrases[] = { "näppäimen puuttuessa paina 'Valikko'", "(Nacisnac 'Menu' by ominac klawisz)", "Pulse 'Menu' para saltarse esta tecla", - "ÐÜôá 'ìåíïý' ãéá ðñïóðÝñáóç áõôïý ôïý ðëÞêôñïõ", + "Ðßåóå 'ìåíïý' ãéá ðñïóðÝñáóç áõôïý ôïý ðëÞêôñïõ", "(Tryck 'Meny' för att hoppa över knappen.)", "Apãsaþi 'Meniu' pentru a sãri peste aceastã tastã", "A Menü gombot nyomni ennek a gombnak a kihagyásához", @@ -4389,7 +4432,7 @@ const tI18nPhrase Phrases[] = { "Vaihe 3: Näppäinkoodien tallentaminen", "Faza 3: Zapamietac Kod", "Fase 3: Guardar códigos de teclas", - "Öáóç 3: Áðïèßêåõóç êùäéêþí", + "Öáóç 3: ÁðïèÞêåõóç êùäéêþí", "Fas 3: Spara knappkoder", "Faza 3: Salvarea codurilor de taste", "Harmadik lépés: kód mentése", @@ -4410,7 +4453,7 @@ const tI18nPhrase Phrases[] = { "Paina 'Ylös' tallentaaksesi ja 'Alas' peruuttaaksesi", "'Gora' zapamietuje, 'Dol' przerywa", "Pulse 'Arriba' para guardar, 'Abajo' para anular", - "'ÐÜíù' áðïèßêåõóç, 'ÊÜôù' áêýñùóç", + "Ðßåóå 'ÐÜíù' ãéá áðïèÞêåõóç, 'ÊÜôù' ãéá áêýñùóç", "Tryck 'Upp' för att spara eller 'Ner' för att avsluta.", "Apãsaþi 'Sus' pentru salvare, 'Jos' pentru anulare", "'Fel' mentés, 'Le´ mégse", @@ -4421,7 +4464,7 @@ const tI18nPhrase Phrases[] = { "Tryk 'Op' for at gemme, 'Ned' for at annullere", }, // Key names: - { "Up", + { "Key$Up", "Auf", "Gor", "Su", @@ -4442,7 +4485,7 @@ const tI18nPhrase Phrases[] = { "Üles", "Op", }, - { "Down", + { "Key$Down", "Ab", "Dol", "Giù", @@ -4463,7 +4506,7 @@ const tI18nPhrase Phrases[] = { "Alla", "Ned", }, - { "Menu", + { "Key$Menu", "Menü", "Meni", "Menù", @@ -4484,7 +4527,7 @@ const tI18nPhrase Phrases[] = { "Menüü", "Menu", }, - { "Ok", + { "Key$Ok", "Ok", "Ok", "Ok", @@ -4505,7 +4548,7 @@ const tI18nPhrase Phrases[] = { "Ok", "Ok", }, - { "Back", + { "Key$Back", "Zurück", "Nazaj", "Indietro", @@ -4526,7 +4569,7 @@ const tI18nPhrase Phrases[] = { "Tagasi", "Tilbage", }, - { "Left", + { "Key$Left", "Links", "Levo", "Sinistra", @@ -4547,7 +4590,7 @@ const tI18nPhrase Phrases[] = { "Vasakule", "Venstre", }, - { "Right", + { "Key$Right", "Rechts", "Desno", "Destra", @@ -4568,7 +4611,7 @@ const tI18nPhrase Phrases[] = { "Paremale", "Højre", }, - { "Red", + { "Key$Red", "Rot", "Rdeèa", "Rosso", @@ -4589,7 +4632,7 @@ const tI18nPhrase Phrases[] = { "Punane", "Rød", }, - { "Green", + { "Key$Green", "Grün", "Zelena", "Verde", @@ -4610,7 +4653,7 @@ const tI18nPhrase Phrases[] = { "Roheline", "Grøn", }, - { "Yellow", + { "Key$Yellow", "Gelb", "Rumena", "Giallo", @@ -4631,7 +4674,7 @@ const tI18nPhrase Phrases[] = { "Kollane", "Gul", }, - { "Blue", + { "Key$Blue", "Blau", "Modra", "Blu", @@ -4652,7 +4695,28 @@ const tI18nPhrase Phrases[] = { "Sinine", "Blå", }, - { "Play", + { "Key$Info", + "Info", + "Info", + "",//TODO + "Info", + "",//TODO + "Info", + "",//TODO + "Tiedot", + "",//TODO + "",//TODO + "Ðëçñïöïñßåò", + "Info", + "Info", + "",//TODO + "",//TODO + "¸ÝäÞ", + "Info", + "Info", + "Info", + }, + { "Key$Play", "Wiedergabe", "Predvajaj", "Riproduci", @@ -4663,7 +4727,7 @@ const tI18nPhrase Phrases[] = { "Toista", "Odtworzenie", "Reproducir", - "AíáìåôÜäïóç", + "ÁíáðáñáãùãÞ", "Spela upp", "Redare", "Lejátszás", @@ -4673,7 +4737,7 @@ const tI18nPhrase Phrases[] = { "Start", "Afspil", }, - { "Pause", + { "Key$Pause", "Pause", "Pavza", "Pausa", @@ -4684,7 +4748,7 @@ const tI18nPhrase Phrases[] = { "Tauko", "Przerwa", "Pausa", - "ÄéÜëåéììá", + "Ðáýóç", "Pausa", "Pauzã", "Szünet", @@ -4694,7 +4758,7 @@ const tI18nPhrase Phrases[] = { "Paus", "Pause", }, - { "Stop", + { "Key$Stop", "Stop", "Ustavi", "Stop", @@ -4715,7 +4779,7 @@ const tI18nPhrase Phrases[] = { "Stopp", "Stop", }, - { "Record", + { "Key$Record", "Aufnehmen", "Snemaj", "Registra", @@ -4736,7 +4800,7 @@ const tI18nPhrase Phrases[] = { "Salvestamine", "Optag", }, - { "FastFwd", + { "Key$FastFwd", "Vorlauf", "Hitro naprej", "Avanti Veloce", @@ -4747,7 +4811,7 @@ const tI18nPhrase Phrases[] = { "Pikakelaus >>", "Naprzod", "Adelante rápido", - "Ðñïüèéóç åìðñüò", + "Ðñïþèçóç åìðñüò", "Snabbspolning framåt", "Derulare înainte", "Elöre pörgetni", @@ -4757,7 +4821,7 @@ const tI18nPhrase Phrases[] = { "Edasikerimine", "Spol fremad", }, - { "FastRew", + { "Key$FastRew", "Rücklauf", "Hitro nazaj", "Indietro Veloce", @@ -4768,7 +4832,7 @@ const tI18nPhrase Phrases[] = { "Pikakelaus <<", "Wstecz", "Atrás rápido", - "Ðñïüèéóç ðßóù", + "Ðñïþèçóç ðßóù", "Snabbspolning bakåt", "Derulare înapoi", "Vissza pörgetni", @@ -4778,7 +4842,7 @@ const tI18nPhrase Phrases[] = { "Tagasikerimine", "Spol tilbage", }, - { "Power", + { "Key$Power", "Ausschalten", "Izklop", "Power", @@ -4799,7 +4863,7 @@ const tI18nPhrase Phrases[] = { "Toide", "Sluk", }, - { "Channel+", + { "Key$Channel+", "Kanal+", "Program+", "Canale +", @@ -4820,7 +4884,7 @@ const tI18nPhrase Phrases[] = { "Kanal+", "Kanal+", }, - { "Channel-", + { "Key$Channel-", "Kanal-", "Program-", "Canale -", @@ -4831,7 +4895,7 @@ const tI18nPhrase Phrases[] = { "Kanava -", "Kanal-", "Canal -", - "ÊáíÜëç-", + "ÊáíÜëé-", "Kanal-", "Canal-", "Csatorna-", @@ -4841,7 +4905,7 @@ const tI18nPhrase Phrases[] = { "Kanal-", "Kanal-", }, - { "Volume+", + { "Key$Volume+", "Lautstärke+", "Glasnost+", "Volume +", @@ -4862,7 +4926,7 @@ const tI18nPhrase Phrases[] = { "Helitugevus+", "Lydstyrke+", }, - { "Volume-", + { "Key$Volume-", "Lautstärke-", "Glasnost-", "Volume -", @@ -4883,7 +4947,7 @@ const tI18nPhrase Phrases[] = { "Helitugevus-", "Lydstyrke-", }, - { "Mute", + { "Key$Mute", "Stumm", "Izklop zvoka", "Mute", @@ -4904,7 +4968,7 @@ const tI18nPhrase Phrases[] = { "Hääletu", "Sluk lyd", }, - { "Audio", + { "Key$Audio", "Audio", "Zvok", "",// TODO @@ -5105,7 +5169,7 @@ const tI18nPhrase Phrases[] = { "vapaana", "pozostalo", "libre", - "Êåíü", + "Åëåýèåñïò", "ledigt", "liber", "szabad", @@ -5168,7 +5232,7 @@ const tI18nPhrase Phrases[] = { " Lopeta toisto", " Zatrzymac odtwarzanie", " Parar reprodución", - " ÔÝëïò áíáìåôÜäïóçò", + " ÔÝëïò áíáðáñáãùãÞò", " Avsluta uppspelning", " Opreºte redarea", " Lejátszást befejzni", @@ -5273,7 +5337,7 @@ const tI18nPhrase Phrases[] = { "Muokkaus aloitettu", "Uruchomiony proces montazu", "Proceso modificación iniciado", - "Áñ÷Þ åðåîåñãáóßáò", + "Áñ÷éóå ç åðåîåñãáóßá", "Redigeringen startar", "Montajul înregistrãrii a început", "Vágás elindítva", @@ -5325,26 +5389,26 @@ const tI18nPhrase Phrases[] = { "Redigeerimine ebaõnnestus", "Redigeringsproces fejlede!", }, - { "scanning recordings...", - "Aufzeichnungen werden durchsucht...", - "iskanje posnetkov...", - "scansione registrazioni...", - "Doorzoeken opnames...", - "A pesquisar gravações...", - "Recherche des enregistrements...", - "Går igjennom opptakene...", - "haetaan tallenteita...", - "Skan nagran...", - "buscando grabaciones...", - "ÅîÝôáóç åããñáöþí...", - "Söker igenom inspelningarna...", - "Caut înregistrãri...", - "Felvett adások böngészése...", - "cercant gravacions...", - "ÁÚÐÝØàÞÒÐÝØÕ ×ÐßØáÕÙ...", - "pretra¾ivanje snimljenog...", - "salvestuste skaneerimine...", - "skanner optagelser...", + { "Recording started", + "Aufzeichnung gestartet", + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO + "",//TODO }, { "Pausing live video...", "Live-Signal wird angehalten...", @@ -5357,7 +5421,7 @@ const tI18nPhrase Phrases[] = { "Pysäytetään lähetys...", "Zatrzymany program biezacy...", "Emisión en directo parada...", - "ÄéÜëåéììá æùíôáíïý óÞìáôïò", + "ÐÜãùìá æùíôáíïý óÞìáôïò", "Pausar direktinspelningen", "Trec în pauzã emisiunea transmisã...", "Az élö adás megállítva...", @@ -5399,7 +5463,7 @@ const tI18nPhrase Phrases[] = { "Tällä laajennoksella ei ole asetuksia!", "Ten plugin niema parametrow!", "Este plugin no admite configuración", - "Áõôü ôï âßóìá äåí Ý÷åé ðáñÜìåôñïõò", + "ÁõôÞ ç åðÝêôáóç äåí Ý÷åé ðáñÜìåôñïõò!", "Den här modulen har inga parametrar", "Acest plugin nu se configureazã!", "Ennek a plugin-nak nincs setup-parametere!", @@ -5462,7 +5526,7 @@ const tI18nPhrase Phrases[] = { "Ei esitystä", "",// TODO "",// TODO - "Ï÷é ôßôëï", + "×ùñßò Ôßôëï", "ingen titel", "Fãrã titlu", "",// TODO diff --git a/interface.c b/interface.c index 5608478..b2e842d 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.70 2005/11/27 15:31:06 kls Exp $ + * $Id: interface.c 1.71 2006/01/04 15:44:19 kls Exp $ */ #include "interface.h" @@ -84,7 +84,9 @@ bool cInterface::QueryKeys(cRemote *Remote, cSkinDisplayMenu *DisplayMenu) eKeys NewKey = kUp; while (NewKey != kNone) { char *Prompt; - asprintf(&Prompt, tr("Press key for '%s'"), tr(cKey::ToString(NewKey))); + char buf[32]; + snprintf(buf, sizeof(buf), "Key$%s", cKey::ToString(NewKey)); + asprintf(&Prompt, tr("Press key for '%s'"), tr(buf)); DisplayMenu->SetItem(Prompt, 4, false, false); free(Prompt); cRemote::Clear(); diff --git a/keymacros.conf b/keymacros.conf index 42cddc2..4b74542 100644 --- a/keymacros.conf +++ b/keymacros.conf @@ -9,4 +9,5 @@ Red Recordings Green Schedule +Yellow Info Blue Timers @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: keys.c 1.9 2005/09/17 11:27:40 kls Exp $ + * $Id: keys.c 1.10 2006/01/05 15:39:26 kls Exp $ */ #include "keys.h" @@ -32,6 +32,7 @@ static tKey keyTable[] = { // "Up" and "Down" must be the first two keys! { k7, "7" }, { k8, "8" }, { k9, "9" }, + { kInfo, "Info" }, { kPlay, "Play" }, { kPause, "Pause" }, { kStop, "Stop" }, @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: keys.h 1.6 2004/12/27 11:10:59 kls Exp $ + * $Id: keys.h 1.7 2006/01/05 15:39:06 kls Exp $ */ #ifndef __KEYS_H @@ -26,6 +26,7 @@ enum eKeys { // "Up" and "Down" must be the first two keys! kYellow, kBlue, k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, + kInfo, kPlay, kPause, kStop, @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.c 1.376 2005/11/05 17:29:22 kls Exp $ + * $Id: menu.c 1.390 2006/01/08 11:39:57 kls Exp $ */ #include "menu.h" @@ -31,6 +31,7 @@ #define MAXWAIT4EPGINFO 3 // seconds #define MODETIMEOUT 3 // seconds +#define DISKSPACECHEK 5 // seconds between disk space checks in the main menu #define MAXRECORDCONTROLS (MAXDEVICES * MAXRECEIVERS) #define MAXINSTANTRECTIME (24 * 60 - 1) // 23:59 hours @@ -41,28 +42,25 @@ // --- cMenuEditCaItem ------------------------------------------------------- class cMenuEditCaItem : public cMenuEditIntItem { -private: - const cCaDefinition *ca; - bool allowCardNr; protected: virtual void Set(void); public: - cMenuEditCaItem(const char *Name, int *Value, bool AllowCardNr = false); + cMenuEditCaItem(const char *Name, int *Value); eOSState ProcessKey(eKeys Key); }; -cMenuEditCaItem::cMenuEditCaItem(const char *Name, int *Value, bool AllowCardNr) +cMenuEditCaItem::cMenuEditCaItem(const char *Name, int *Value) :cMenuEditIntItem(Name, Value, 0) { - ca = CaDefinitions.Get(*Value); - allowCardNr = AllowCardNr; Set(); } void cMenuEditCaItem::Set(void) { - if (ca) - SetValue(ca->Description()); + if (*value == CA_FTA) + SetValue(tr("Free To Air")); + else if (*value >= CA_ENCRYPTED_MIN) + SetValue(tr("encrypted")); else cMenuEditIntItem::Set(); } @@ -72,18 +70,8 @@ eOSState cMenuEditCaItem::ProcessKey(eKeys Key) eOSState state = cMenuEditItem::ProcessKey(Key); if (state == osUnknown) { - if (NORMALKEY(Key) == kLeft) { // TODO might want to increase the delta if repeated quickly? - if (ca && ca->Prev()) { - ca = (cCaDefinition *)ca->Prev(); - *value = ca->Number(); - } - } - else if (NORMALKEY(Key) == kRight) { - if (ca && ca->Next() && (allowCardNr || ((cCaDefinition *)ca->Next())->Number() > MAXDEVICES)) { - ca = (cCaDefinition *)ca->Next(); - *value = ca->Number(); - } - } + if (NORMALKEY(Key) == kLeft && *value >= CA_ENCRYPTED_MIN) + *value = CA_FTA; else return cMenuEditIntItem::ProcessKey(Key); Set(); @@ -265,7 +253,7 @@ void cMenuEditChannel::Setup(void) Add(new cMenuEditIntItem( tr("Dpid1"), &data.dpids[0], 0, 0x1FFF)); Add(new cMenuEditIntItem( tr("Dpid2"), &data.dpids[1], 0, 0x1FFF)); Add(new cMenuEditIntItem( tr("Tpid"), &data.tpid, 0, 0x1FFF)); - Add(new cMenuEditCaItem( tr("CA"), &data.caids[0], true));//XXX + Add(new cMenuEditCaItem( tr("CA"), &data.caids[0])); Add(new cMenuEditIntItem( tr("Sid"), &data.sid, 1, 0xFFFF)); /* XXX not yet used Add(new cMenuEditIntItem( tr("Nid"), &data.nid, 0)); @@ -380,12 +368,17 @@ void cMenuChannelItem::Set(void) // --- cMenuChannels --------------------------------------------------------- +#define CHANNELNUMBERTIMEOUT 1000 //ms + class cMenuChannels : public cOsdMenu { private: + int number; + cTimeMs numberTimer; void Setup(void); cChannel *GetChannel(int Index); void Propagate(void); protected: + eOSState Number(eKeys Key); eOSState Switch(void); eOSState Edit(void); eOSState New(void); @@ -400,6 +393,7 @@ public: cMenuChannels::cMenuChannels(void) :cOsdMenu(tr("Channels"), CHNUMWIDTH) { + number = 0; Setup(); Channels.IncBeingEdited(); } @@ -427,7 +421,7 @@ void cMenuChannels::Setup(void) if (cMenuChannelItem::SortMode() != cMenuChannelItem::csmNumber) Sort(); SetCurrent(currentItem); - SetHelp(tr("Edit"), tr("New"), tr("Delete"), cMenuChannelItem::SortMode() == cMenuChannelItem::csmNumber ? tr("Mark") : NULL); + SetHelp(tr("Button$Edit"), tr("Button$New"), tr("Button$Delete"), tr("Button$Mark")); Display(); } @@ -446,6 +440,30 @@ void cMenuChannels::Propagate(void) Channels.SetModified(true); } +eOSState cMenuChannels::Number(eKeys Key) +{ + if (HasSubMenu()) + return osContinue; + if (numberTimer.TimedOut()) + number = 0; + if (!number && Key == k0) { + cMenuChannelItem::IncSortMode(); + Setup(); + } + else { + number = number * 10 + Key - k0; + for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next()) { + if (!ci->Channel()->GroupSep() && ci->Channel()->Number() == number) { + SetCurrent(ci); + Display(); + break; + } + } + numberTimer.Set(CHANNELNUMBERTIMEOUT); + } + return osContinue; +} + eOSState cMenuChannels::Switch(void) { if (HasSubMenu()) @@ -530,14 +548,13 @@ eOSState cMenuChannels::ProcessKey(eKeys Key) default: if (state == osUnknown) { switch (Key) { - case k0: cMenuChannelItem::IncSortMode(); - Setup(); - break; + case k0 ... k9: + return Number(Key); case kOk: return Switch(); case kRed: return Edit(); case kGreen: return New(); case kYellow: return Delete(); - case kBlue: if (!HasSubMenu() && cMenuChannelItem::SortMode() == cMenuChannelItem::csmNumber) + case kBlue: if (!HasSubMenu()) Mark(); break; default: break; @@ -771,7 +788,7 @@ cMenuTimers::cMenuTimers(void) Add(new cMenuTimerItem(timer)); if (Setup.SortTimers) Sort(); - SetHelp(tr("Edit"), tr("New"), tr("Delete"), Setup.SortTimers ? tr("On/Off") : tr("Mark")); + SetHelp(tr("Button$Edit"), tr("Button$New"), tr("Button$Delete"), Setup.SortTimers ? tr("Button$On/Off") : tr("Button$Mark")); Timers.IncBeingEdited(); } @@ -897,7 +914,9 @@ cMenuEvent::cMenuEvent(const cEvent *Event, bool CanSwitch) cChannel *channel = Channels.GetByChannelID(event->ChannelID(), true); if (channel) { SetTitle(channel->Name()); - SetHelp(tr("Record"), NULL, NULL, CanSwitch ? tr("Switch") : NULL); + int TimerMatch = tmNone; + Timers.GetMatch(event, &TimerMatch); + SetHelp(TimerMatch == tmFull ? tr("Timer") : tr("Button$Record"), NULL, NULL, CanSwitch ? tr("Button$Switch") : NULL); } } } @@ -939,36 +958,59 @@ eOSState cMenuEvent::ProcessKey(eKeys Key) return state; } -// --- cMenuWhatsOnItem ------------------------------------------------------ +// --- cMenuScheduleItem ----------------------------------------------------- -class cMenuWhatsOnItem : public cOsdItem { +class cMenuScheduleItem : public cOsdItem { public: const cEvent *event; const cChannel *channel; - cMenuWhatsOnItem(const cEvent *Event, cChannel *Channel); + int timerMatch; + cMenuScheduleItem(const cEvent *Event, cChannel *Channel = NULL); + bool Update(bool Force = false); }; -cMenuWhatsOnItem::cMenuWhatsOnItem(const cEvent *Event, cChannel *Channel) +cMenuScheduleItem::cMenuScheduleItem(const cEvent *Event, cChannel *Channel) { event = Event; channel = Channel; - char *buffer = NULL; - int TimerMatch; - char t = Timers.GetMatch(Event, &TimerMatch) ? (TimerMatch == tmFull) ? 'T' : 't' : ' '; - char v = event->Vps() && (event->Vps() - event->StartTime()) ? 'V' : ' '; - char r = event->IsRunning() ? '*' : ' '; - asprintf(&buffer, "%d\t%.*s\t%s\t%c%c%c\t%s", channel->Number(), 6, channel->ShortName(true), *event->GetTimeString(), t, v, r, event->Title()); - SetText(buffer, false); + timerMatch = tmNone; + Update(true); +} + +static char *TimerMatchChars = " tT"; + +bool cMenuScheduleItem::Update(bool Force) +{ + bool result = false; + int OldTimerMatch = timerMatch; + Timers.GetMatch(event, &timerMatch); + if (Force || timerMatch != OldTimerMatch) { + char *buffer = NULL; + char t = TimerMatchChars[timerMatch]; + char v = event->Vps() && (event->Vps() - event->StartTime()) ? 'V' : ' '; + char r = event->IsRunning() ? '*' : ' '; + if (channel) + asprintf(&buffer, "%d\t%.*s\t%s\t%c%c%c\t%s", channel->Number(), 6, channel->ShortName(true), *event->GetTimeString(), t, v, r, event->Title()); + else + asprintf(&buffer, "%.*s\t%s\t%c%c%c\t%s", 6, *event->GetDateString(), *event->GetTimeString(), t, v, r, event->Title()); + SetText(buffer, false); + result = true; + } + return result; } // --- cMenuWhatsOn ---------------------------------------------------------- class cMenuWhatsOn : public cOsdMenu { private: + bool now; + int helpKeys; eOSState Record(void); eOSState Switch(void); static int currentChannel; static const cEvent *scheduleEvent; + bool Update(void); + void SetHelpKeys(void); public: cMenuWhatsOn(const cSchedules *Schedules, bool Now, int CurrentChannelNr); static int CurrentChannel(void) { return currentChannel; } @@ -983,18 +1025,47 @@ const cEvent *cMenuWhatsOn::scheduleEvent = NULL; cMenuWhatsOn::cMenuWhatsOn(const cSchedules *Schedules, bool Now, int CurrentChannelNr) :cOsdMenu(Now ? tr("What's on now?") : tr("What's on next?"), CHNUMWIDTH, 7, 6, 4) { + now = Now; + helpKeys = -1; for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { if (!Channel->GroupSep()) { const cSchedule *Schedule = Schedules->GetSchedule(Channel->GetChannelID()); if (Schedule) { const cEvent *Event = Now ? Schedule->GetPresentEvent() : Schedule->GetFollowingEvent(); if (Event) - Add(new cMenuWhatsOnItem(Event, Channel), Channel->Number() == CurrentChannelNr); + Add(new cMenuScheduleItem(Event, Channel), Channel->Number() == CurrentChannelNr); } } } currentChannel = CurrentChannelNr; - SetHelp(Count() ? tr("Record") : NULL, Now ? tr("Next") : tr("Now"), tr("Button$Schedule"), tr("Switch")); + SetHelpKeys(); +} + +bool cMenuWhatsOn::Update(void) +{ + bool result = false; + for (cOsdItem *item = First(); item; item = Next(item)) { + if (((cMenuScheduleItem *)item)->Update()) + result = true; + } + return result; +} + +void cMenuWhatsOn::SetHelpKeys(void) +{ + cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current()); + int NewHelpKeys = 0; + if (item) { + if (item->timerMatch == tmFull) + NewHelpKeys = 2; + else + NewHelpKeys = 1; + } + if (NewHelpKeys != helpKeys) { + const char *Red[] = { NULL, tr("Button$Record"), tr("Timer") }; + SetHelp(Red[NewHelpKeys], now ? tr("Button$Next") : tr("Button$Now"), tr("Button$Schedule"), tr("Button$Switch")); + helpKeys = NewHelpKeys; + } } const cEvent *cMenuWhatsOn::ScheduleEvent(void) @@ -1006,7 +1077,7 @@ const cEvent *cMenuWhatsOn::ScheduleEvent(void) eOSState cMenuWhatsOn::Switch(void) { - cMenuWhatsOnItem *item = (cMenuWhatsOnItem *)Get(Current()); + cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current()); if (item) { cChannel *channel = Channels.GetByChannelID(item->event->ChannelID(), true); if (channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true)) @@ -1018,21 +1089,39 @@ eOSState cMenuWhatsOn::Switch(void) eOSState cMenuWhatsOn::Record(void) { - cMenuWhatsOnItem *item = (cMenuWhatsOnItem *)Get(Current()); + cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current()); if (item) { + if (item->timerMatch == tmFull) { + int tm = tmNone; + cTimer *timer = Timers.GetMatch(item->event, &tm); + if (timer) + return AddSubMenu(new cMenuEditTimer(timer)); + } cTimer *timer = new cTimer(item->event); cTimer *t = Timers.GetTimer(timer); if (t) { delete timer; timer = t; + return AddSubMenu(new cMenuEditTimer(timer)); + } + else { + Timers.Add(timer); + timer->Matches(); + Timers.SetModified(); + isyslog("timer %s added (active)", *timer->ToDescr()); + if (HasSubMenu()) + CloseSubMenu(); + if (Update()) + Display(); + SetHelpKeys(); } - return AddSubMenu(new cMenuEditTimer(timer, !t)); } return osContinue; } eOSState cMenuWhatsOn::ProcessKey(eKeys Key) { + bool HadSubMenu = HasSubMenu(); eOSState state = cOsdMenu::ProcessKey(Key); if (state == osUnknown) { @@ -1042,7 +1131,7 @@ eOSState cMenuWhatsOn::ProcessKey(eKeys Key) case kYellow: state = osBack; // continue with kGreen case kGreen: { - cMenuWhatsOnItem *mi = (cMenuWhatsOnItem *)Get(Current()); + cMenuScheduleItem *mi = (cMenuScheduleItem *)Get(Current()); if (mi) { scheduleEvent = mi->event; currentChannel = mi->channel->Number(); @@ -1051,34 +1140,20 @@ eOSState cMenuWhatsOn::ProcessKey(eKeys Key) break; case kBlue: return Switch(); case kOk: if (Count()) - return AddSubMenu(new cMenuEvent(((cMenuWhatsOnItem *)Get(Current()))->event, true)); + return AddSubMenu(new cMenuEvent(((cMenuScheduleItem *)Get(Current()))->event, true)); break; default: break; } } + else if (!HasSubMenu()) { + if (HadSubMenu && Update()) + Display(); + if (Key != kNone) + SetHelpKeys(); + } return state; } -// --- cMenuScheduleItem ----------------------------------------------------- - -class cMenuScheduleItem : public cOsdItem { -public: - const cEvent *event; - cMenuScheduleItem(const cEvent *Event); -}; - -cMenuScheduleItem::cMenuScheduleItem(const cEvent *Event) -{ - event = Event; - char *buffer = NULL; - int TimerMatch; - char t = Timers.GetMatch(Event, &TimerMatch) ? (TimerMatch == tmFull) ? 'T' : 't' : ' '; - char v = event->Vps() && (event->Vps() - event->StartTime()) ? 'V' : ' '; - char r = event->IsRunning() ? '*' : ' '; - asprintf(&buffer, "%.*s\t%s\t%c%c%c\t%s", 6, *event->GetDateString(), *event->GetTimeString(), t, v, r, event->Title()); - SetText(buffer, false); -} - // --- cMenuSchedule --------------------------------------------------------- class cMenuSchedule : public cOsdMenu { @@ -1087,9 +1162,12 @@ private: const cSchedules *schedules; bool now, next; int otherChannel; + int helpKeys; eOSState Record(void); eOSState Switch(void); void PrepareSchedule(cChannel *Channel); + bool Update(void); + void SetHelpKeys(void); public: cMenuSchedule(void); virtual ~cMenuSchedule(); @@ -1101,12 +1179,13 @@ cMenuSchedule::cMenuSchedule(void) { now = next = false; otherChannel = 0; + helpKeys = -1; cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel()); if (channel) { cMenuWhatsOn::SetCurrentChannel(channel->Number()); schedules = cSchedules::Schedules(schedulesLock); PrepareSchedule(channel); - SetHelp(Count() ? tr("Record") : NULL, tr("Now"), tr("Next")); + SetHelpKeys(); } } @@ -1135,17 +1214,61 @@ void cMenuSchedule::PrepareSchedule(cChannel *Channel) } } +bool cMenuSchedule::Update(void) +{ + bool result = false; + for (cOsdItem *item = First(); item; item = Next(item)) { + if (((cMenuScheduleItem *)item)->Update()) + result = true; + } + return result; +} + +void cMenuSchedule::SetHelpKeys(void) +{ + cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current()); + int NewHelpKeys = 0; + if (item) { + if (item->timerMatch == tmFull) + NewHelpKeys = 2; + else + NewHelpKeys = 1; + } + if (NewHelpKeys != helpKeys) { + const char *Red[] = { NULL, tr("Button$Record"), tr("Timer") }; + SetHelp(Red[NewHelpKeys], tr("Button$Now"), tr("Button$Next")); + helpKeys = NewHelpKeys; + } +} + eOSState cMenuSchedule::Record(void) { cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current()); if (item) { + if (item->timerMatch == tmFull) { + int tm = tmNone; + cTimer *timer = Timers.GetMatch(item->event, &tm); + if (timer) + return AddSubMenu(new cMenuEditTimer(timer)); + } cTimer *timer = new cTimer(item->event); cTimer *t = Timers.GetTimer(timer); if (t) { delete timer; timer = t; + return AddSubMenu(new cMenuEditTimer(timer)); + } + else { + Timers.Add(timer); + timer->Matches(); + Timers.SetModified(); + isyslog("timer %s added (active)", *timer->ToDescr()); + if (HasSubMenu()) + CloseSubMenu(); + if (Update()) + Display(); + SetHelpKeys(); } - return AddSubMenu(new cMenuEditTimer(timer, !t)); } return osContinue; } @@ -1162,6 +1285,7 @@ eOSState cMenuSchedule::Switch(void) eOSState cMenuSchedule::ProcessKey(eKeys Key) { + bool HadSubMenu = HasSubMenu(); eOSState state = cOsdMenu::ProcessKey(Key); if (state == osUnknown) { @@ -1204,11 +1328,15 @@ eOSState cMenuSchedule::ProcessKey(eKeys Key) PrepareSchedule(channel); if (channel->Number() != cDevice::CurrentChannel()) { otherChannel = channel->Number(); - SetHelp(Count() ? tr("Record") : NULL, tr("Now"), tr("Next"), tr("Switch")); + SetHelp(Count() ? tr("Button$Record") : NULL, tr("Button$Now"), tr("Button$Next"), tr("Button$Switch")); } Display(); } } + else if (HadSubMenu && Update()) + Display(); + if (Key != kNone) + SetHelpKeys(); } return state; } @@ -1461,7 +1589,7 @@ cMenuRecording::cMenuRecording(const cRecording *Recording) { recording = Recording; if (recording) - SetHelp(tr("Play"), tr("Rewind")); + SetHelp(tr("Button$Play"), tr("Button$Rewind")); } void cMenuRecording::Display(void) @@ -1586,9 +1714,9 @@ void cMenuRecordings::SetHelpKeys(void) if (NewHelpKeys != helpKeys) { switch (NewHelpKeys) { case 0: SetHelp(NULL); break; - case 1: SetHelp(tr("Open")); break; + case 1: SetHelp(tr("Button$Open")); break; case 2: - case 3: SetHelp(RecordingCommands.Count() ? tr("Commands") : tr("Play"), tr("Rewind"), tr("Delete"), NewHelpKeys == 3 ? tr("Info") : NULL); + case 3: SetHelp(RecordingCommands.Count() ? tr("Commands") : tr("Button$Play"), tr("Button$Rewind"), tr("Button$Delete"), NewHelpKeys == 3 ? tr("Info") : NULL); } helpKeys = NewHelpKeys; } @@ -1876,6 +2004,7 @@ void cMenuSetupOSD::Set(void) Add(new cMenuEditBoolItem(tr("Setup.OSD$Channel info position"), &data.ChannelInfoPos, tr("bottom"), tr("top"))); Add(new cMenuEditIntItem( tr("Setup.OSD$Channel info time (s)"), &data.ChannelInfoTime, 1, 60)); Add(new cMenuEditBoolItem(tr("Setup.OSD$Info on channel switch"), &data.ShowInfoOnChSwitch)); + Add(new cMenuEditBoolItem(tr("Setup.OSD$Timeout requested channel info"), &data.TimeoutRequChInfo)); Add(new cMenuEditBoolItem(tr("Setup.OSD$Scroll pages"), &data.MenuScrollPage)); Add(new cMenuEditBoolItem(tr("Setup.OSD$Scroll wraps"), &data.MenuScrollWrap)); Add(new cMenuEditBoolItem(tr("Setup.OSD$Sort timers"), &data.SortTimers)); @@ -1918,7 +2047,7 @@ eOSState cMenuSetupOSD::ProcessKey(eKeys Key) themeIndex = d ? themes.GetThemeIndex(d) : 0; free(d); } - + Set(); Setup.OSDLanguage = OriginalOSDLanguage; } @@ -1943,7 +2072,7 @@ cMenuSetupEPG::cMenuSetupEPG(void) ; originalNumLanguages = numLanguages; SetSection(tr("EPG")); - SetHelp(tr("Scan")); + SetHelp(tr("Button$Scan")); Setup(); } @@ -2196,7 +2325,7 @@ cMenuSetupCICAM::cMenuSetupCICAM(void) } } } - SetHelp(tr("Menu"), tr("Reset")); + SetHelp(tr("Button$Menu"), tr("Button$Reset")); } eOSState cMenuSetupCICAM::Menu(void) @@ -2469,14 +2598,19 @@ cMenuPluginItem::cMenuPluginItem(const char *Name, int Index) // --- cMenuMain ------------------------------------------------------------- #define STOP_RECORDING tr(" Stop recording ") -#define ON_PRIMARY_INTERFACE tr("on primary interface") cOsdObject *cMenuMain::pluginOsdObject = NULL; -cMenuMain::cMenuMain(bool Replaying, eOSState State) +cMenuMain::cMenuMain(eOSState State) :cOsdMenu("") { - replaying = Replaying; + lastDiskSpaceCheck = 0; + lastFreeMB = 0; + replaying = false; + stopReplayItem = NULL; + cancelEditingItem = NULL; + stopRecordingItem = NULL; + recordControlsState = 0; Set(); // Initial submenus: @@ -2502,23 +2636,9 @@ cOsdObject *cMenuMain::PluginOsdObject(void) void cMenuMain::Set(void) { Clear(); - //XXX //SetTitle("VDR"); // this is done below, including disk usage + SetTitle("VDR"); SetHasHotkeys(); - // Title with disk usage: - -#define MB_PER_MINUTE 25.75 // this is just an estimate! - - char buffer[40]; - int FreeMB; - int Percent = VideoDiskSpace(&FreeMB); - int Minutes = int(double(FreeMB) / MB_PER_MINUTE); - int Hours = Minutes / 60; - Minutes %= 60; - snprintf(buffer, sizeof(buffer), "%s - %s %d%% - %2d:%02d %s", tr("VDR"), tr("Disk"), Percent, Hours, Minutes, tr("free")); - //XXX -> skin function!!! - SetTitle(buffer); - // Basic menu items: Add(new cOsdItem(hk(tr("Schedule")), osSchedule)); @@ -2545,37 +2665,82 @@ void cMenuMain::Set(void) if (Commands.Count()) Add(new cOsdItem(hk(tr("Commands")), osCommands)); - // Replay control: + Update(true); - if (replaying) - Add(new cOsdItem(tr(" Stop replaying"), osStopReplay)); + Display(); +} - // Record control: +#define MB_PER_MINUTE 25.75 // this is just an estimate! - if (cRecordControls::StopPrimary()) { - char *buffer = NULL; - asprintf(&buffer, "%s%s", STOP_RECORDING, ON_PRIMARY_INTERFACE); - Add(new cOsdItem(buffer, osStopRecord)); - free(buffer); - } +bool cMenuMain::Update(bool Force) +{ + bool result = false; - const char *s = NULL; - while ((s = cRecordControls::GetInstantId(s)) != NULL) { - char *buffer = NULL; - asprintf(&buffer, "%s%s", STOP_RECORDING, s); - Add(new cOsdItem(buffer, osStopRecord)); - free(buffer); + // Title with disk usage: + if (Force || time(NULL) - lastDiskSpaceCheck > DISKSPACECHEK) { + int FreeMB; + int Percent = VideoDiskSpace(&FreeMB); + if (Force || FreeMB != lastFreeMB) { + int Minutes = int(double(FreeMB) / MB_PER_MINUTE); + int Hours = Minutes / 60; + Minutes %= 60; + char buffer[40]; + snprintf(buffer, sizeof(buffer), "%s - %s %d%% - %2d:%02d %s", tr("VDR"), tr("Disk"), Percent, Hours, Minutes, tr("free")); + //XXX -> skin function!!! + SetTitle(buffer); + result = true; + } + lastDiskSpaceCheck = time(NULL); + } + + bool NewReplaying = cControl::Control() != NULL; + if (Force || NewReplaying != replaying) { + replaying = NewReplaying; + // Replay control: + if (replaying && !stopReplayItem) + Add(stopReplayItem = new cOsdItem(tr(" Stop replaying"), osStopReplay)); + else if (stopReplayItem && !replaying) { + Del(stopReplayItem->Index()); + stopReplayItem = NULL; } + // Color buttons: + SetHelp(!replaying ? tr("Button$Record") : NULL, tr("Button$Audio"), replaying ? NULL : tr("Button$Pause"), replaying ? tr("Button$Stop") : cReplayControl::LastReplayed() ? tr("Button$Resume") : NULL); + result = true; + } // Editing control: + bool CutterActive = cCutter::Active(); + if (CutterActive && !cancelEditingItem) { + Add(cancelEditingItem = new cOsdItem(tr(" Cancel editing"), osCancelEdit)); + result = true; + } + else if (cancelEditingItem && !CutterActive) { + Del(cancelEditingItem->Index()); + cancelEditingItem = NULL; + result = true; + } - if (cCutter::Active()) - Add(new cOsdItem(tr(" Cancel editing"), osCancelEdit)); - - // Color buttons: + // Record control: + if (cRecordControls::StateChanged(recordControlsState)) { + while (stopRecordingItem) { + cOsdItem *it = Next(stopRecordingItem); + Del(stopRecordingItem->Index()); + stopRecordingItem = it; + } + const char *s = NULL; + while ((s = cRecordControls::GetInstantId(s)) != NULL) { + char *buffer = NULL; + asprintf(&buffer, "%s%s", STOP_RECORDING, s); + cOsdItem *item = new cOsdItem(osStopRecord); + item->SetText(buffer, false); + Add(item); + if (!stopRecordingItem) + stopRecordingItem = item; + } + result = true; + } - SetHelp(!replaying ? tr("Record") : NULL, tr("Audio"), replaying ? NULL : tr("Pause"), replaying ? tr("Button$Stop") : cReplayControl::LastReplayed() ? tr("Resume") : NULL); - Display(); + return result; } eOSState cMenuMain::ProcessKey(eKeys Key) @@ -2595,11 +2760,7 @@ eOSState cMenuMain::ProcessKey(eKeys Key) case osStopRecord: if (Interface->Confirm(tr("Stop recording?"))) { cOsdItem *item = Get(Current()); if (item) { - const char *s = item->Text() + strlen(STOP_RECORDING); - if (strcmp(s, ON_PRIMARY_INTERFACE) == 0) - cRecordControls::StopPrimary(true); - else - cRecordControls::Stop(item->Text() + strlen(STOP_RECORDING)); + cRecordControls::Stop(item->Text() + strlen(STOP_RECORDING)); return osEnd; } } @@ -2647,6 +2808,8 @@ eOSState cMenuMain::ProcessKey(eKeys Key) default: break; } } + if (!HasSubMenu() && Update(HadSubMenu)) + Display(); if (Key != kNone) { if (Setup.OSDLanguage != osdLanguage) { Set(); @@ -2710,6 +2873,7 @@ cDisplayChannel::cDisplayChannel(int Number, bool Switched) withInfo = !Switched || Setup.ShowInfoOnChSwitch; displayChannel = Skins.Current()->DisplayChannel(withInfo); number = 0; + timeout = Switched || Setup.TimeoutRequChInfo; channel = Channels.GetByNumber(Number); lastPresent = lastFollowing = NULL; if (channel) { @@ -2893,7 +3057,7 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key) return osEnd; } }; - if (lastTime.Elapsed() < (uint64)(Setup.ChannelInfoTime * 1000)) { + if (!timeout || lastTime.Elapsed() < (uint64)(Setup.ChannelInfoTime * 1000)) { if (!number && group < 0 && channel && channel->Number() != cDevice::CurrentChannel()) Refresh(); // makes sure a channel switch through the SVDRP CHAN command is displayed DisplayInfo(); @@ -2996,7 +3160,7 @@ cDisplayTracks::cDisplayTracks(void) } } timeout.Set(TRACKTIMEOUT); - displayTracks = Skins.Current()->DisplayTracks(tr("Audio"), numTracks, descriptions); + displayTracks = Skins.Current()->DisplayTracks(tr("Button$Audio"), numTracks, descriptions); Show(); } @@ -3147,7 +3311,7 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause) recorder = new cRecorder(fileName, ch->Ca(), timer->Priority(), ch->Vpid(), ch->Apids(), ch->Dpids(), ch->Spids()); if (device->AttachReceiver(recorder)) { Recording.WriteInfo(); - cStatus::MsgRecording(device, Recording.Name()); + cStatus::MsgRecording(device, Recording.Name(), Recording.FileName(), true); if (!Timer && !cReplayControl::LastReplayed()) // an instant recording, maybe from cRecordControls::PauseLiveVideo() cReplayControl::SetRecording(fileName, Recording.Name()); Recordings.AddByName(fileName); @@ -3206,7 +3370,7 @@ void cRecordControl::Stop(void) DELETENULL(recorder); timer->SetRecording(false); timer = NULL; - cStatus::MsgRecording(device, NULL); + cStatus::MsgRecording(device, NULL, fileName, false); cRecordingUserCommand::InvokeCommand(RUC_AFTERRECORDING, fileName); } } @@ -3222,9 +3386,11 @@ bool cRecordControl::Process(time_t t) // --- cRecordControls ------------------------------------------------------- cRecordControl *cRecordControls::RecordControls[MAXRECORDCONTROLS] = { NULL }; +int cRecordControls::state = 0; bool cRecordControls::Start(cTimer *Timer, bool Pause) { + ChangeState(); int ch = Timer ? Timer->Channel()->Number() : cDevice::CurrentChannel(); cChannel *channel = Channels.GetByNumber(ch); @@ -3262,6 +3428,7 @@ bool cRecordControls::Start(cTimer *Timer, bool Pause) void cRecordControls::Stop(const char *InstantId) { + ChangeState(); for (int i = 0; i < MAXRECORDCONTROLS; i++) { if (RecordControls[i]) { const char *id = RecordControls[i]->InstantId(); @@ -3281,6 +3448,7 @@ void cRecordControls::Stop(const char *InstantId) void cRecordControls::Stop(cDevice *Device) { + ChangeState(); for (int i = 0; i < MAXRECORDCONTROLS; i++) { if (RecordControls[i]) { if (RecordControls[i]->Device() == Device) { @@ -3291,20 +3459,6 @@ void cRecordControls::Stop(cDevice *Device) } } -bool cRecordControls::StopPrimary(bool DoIt) -{ - if (cDevice::PrimaryDevice()->Receiving()) { - //XXX+ disabled for the moment - might become obsolete with DVB_DRIVER_VERSION >= 2002090101 - cDevice *device = NULL;//XXX cDevice::GetDevice(cDevice::PrimaryDevice()->Ca(), 0); - if (device) { - if (DoIt) - Stop(cDevice::PrimaryDevice()); - return true; - } - } - return false; -} - bool cRecordControls::PauseLiveVideo(void) { Skins.Message(mtStatus, tr("Pausing live video...")); @@ -3349,8 +3503,10 @@ void cRecordControls::Process(time_t t) { for (int i = 0; i < MAXRECORDCONTROLS; i++) { if (RecordControls[i]) { - if (!RecordControls[i]->Process(t)) + if (!RecordControls[i]->Process(t)) { DELETENULL(RecordControls[i]); + ChangeState(); + } } } } @@ -3365,6 +3521,7 @@ void cRecordControls::ChannelDataModified(cChannel *Channel) RecordControls[i]->Stop(); // This will restart the recording, maybe even from a different // device in case conditional access has changed. + ChangeState(); } } } @@ -3384,6 +3541,15 @@ void cRecordControls::Shutdown(void) { for (int i = 0; i < MAXRECORDCONTROLS; i++) DELETENULL(RecordControls[i]); + ChangeState(); +} + +bool cRecordControls::StateChanged(int &State) +{ + int NewState = state; + bool Result = State != NewState; + State = state; + return Result; } // --- cReplayControl -------------------------------------------------------- @@ -3403,13 +3569,13 @@ cReplayControl::cReplayControl(void) timeSearchActive = false; marks.Load(fileName); cRecording Recording(fileName); - cStatus::MsgReplaying(this, Recording.Name()); + cStatus::MsgReplaying(this, Recording.Name(), Recording.FileName(), true); } cReplayControl::~cReplayControl() { Hide(); - cStatus::MsgReplaying(this, NULL); + cStatus::MsgReplaying(this, NULL, fileName, false); Stop(); } @@ -3698,6 +3864,14 @@ void cReplayControl::EditTest(void) } } +cOsdObject *cReplayControl::GetInfo(void) +{ + cRecording *Recording = Recordings.GetByName(cReplayControl::LastReplayed()); + if (Recording) + return new cMenuRecording(Recording); + return NULL; +} + eOSState cReplayControl::ProcessKey(eKeys Key) { if (!Active()) @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.h 1.77 2005/11/05 17:26:09 kls Exp $ + * $Id: menu.h 1.81 2006/01/06 11:30:38 kls Exp $ */ #ifndef __MENU_H @@ -55,11 +55,18 @@ public: class cMenuMain : public cOsdMenu { private: + time_t lastDiskSpaceCheck; + int lastFreeMB; bool replaying; + cOsdItem *stopReplayItem; + cOsdItem *cancelEditingItem; + cOsdItem *stopRecordingItem; + int recordControlsState; static cOsdObject *pluginOsdObject; void Set(void); + bool Update(bool Force = false); public: - cMenuMain(bool Replaying, eOSState State = osUnknown); + cMenuMain(eOSState State = osUnknown); virtual eOSState ProcessKey(eKeys Key); static cOsdObject *PluginOsdObject(void); }; @@ -71,6 +78,7 @@ private: bool withInfo; cTimeMs lastTime; int number; + bool timeout; cChannel *channel; const cEvent *lastPresent; const cEvent *lastFollowing; @@ -189,11 +197,11 @@ public: class cRecordControls { private: static cRecordControl *RecordControls[]; + static int state; public: static bool Start(cTimer *Timer = NULL, bool Pause = false); static void Stop(const char *InstantId); static void Stop(cDevice *Device); - static bool StopPrimary(bool DoIt = false); static bool PauseLiveVideo(void); static const char *GetInstantId(const char *LastInstantId); static cRecordControl *GetRecordControl(const char *FileName); @@ -201,6 +209,8 @@ public: static void ChannelDataModified(cChannel *Channel); static bool Active(void); static void Shutdown(void); + static void ChangeState(void) { state++; } + static bool StateChanged(int &State); }; class cReplayControl : public cDvbPlayerControl { @@ -230,6 +240,7 @@ private: public: cReplayControl(void); virtual ~cReplayControl(); + virtual cOsdObject *GetInfo(void); virtual eOSState ProcessKey(eKeys Key); virtual void Show(void); virtual void Hide(void); diff --git a/menuitems.c b/menuitems.c index 4b4e5f2..88e8a50 100644 --- a/menuitems.c +++ b/menuitems.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menuitems.c 1.24 2005/11/12 12:22:10 kls Exp $ + * $Id: menuitems.c 1.29 2006/01/07 15:37:03 kls Exp $ */ #include "menuitems.h" @@ -15,7 +15,7 @@ #include "skins.h" #include "status.h" -const char *FileNameChars = " abcdefghijklmnopqrstuvwxyz0123456789-.#~"; +const char *FileNameChars = " abcdefghijklmnopqrstuvwxyz0123456789-.#~,/_@"; // --- cMenuEditItem --------------------------------------------------------- @@ -116,7 +116,7 @@ void cMenuEditBoolItem::Set(void) // --- cMenuEditBitItem ------------------------------------------------------ -cMenuEditBitItem::cMenuEditBitItem(const char *Name, int *Value, int Mask, const char *FalseString, const char *TrueString) +cMenuEditBitItem::cMenuEditBitItem(const char *Name, uint *Value, uint Mask, const char *FalseString, const char *TrueString) :cMenuEditBoolItem(Name, &bit, FalseString, TrueString) { value = Value; @@ -243,6 +243,9 @@ cMenuEditStrItem::cMenuEditStrItem(const char *Name, char *Value, int Length, co pos = -1; insert = uppercase = false; newchar = true; + charMap = tr(" 0\t-.#~,/_@1\tabc2\tdef3\tghi4\tjkl5\tmno6\tpqrs7\ttuv8\twxyz9"); + currentChar = NULL; + lastKey = kNone; Set(); } @@ -253,8 +256,8 @@ cMenuEditStrItem::~cMenuEditStrItem() void cMenuEditStrItem::SetHelpKeys(void) { - if (pos >= 0) - cSkinDisplay::Current()->SetButtons(tr("ABC/abc"), tr(insert ? "Overwrite" : "Insert"), tr("Delete")); + if (InEditMode()) + cSkinDisplay::Current()->SetButtons(tr("Button$ABC/abc"), tr(insert ? "Button$Overwrite" : "Button$Insert"), tr("Button$Delete")); else cSkinDisplay::Current()->SetButtons(NULL); } @@ -264,7 +267,7 @@ void cMenuEditStrItem::Set(void) char buf[1000]; const char *fmt = insert && newchar ? "[]%c%s" : "[%c]%s"; - if (pos >= 0) { + if (InEditMode()) { const cFont *font = cFont::GetFont(fontOsd); strncpy(buf, value, pos); snprintf(buf + pos, sizeof(buf) - pos - 2, fmt, *(value + pos), value + pos + 1); @@ -320,9 +323,12 @@ char cMenuEditStrItem::Inc(char c, bool Up) eOSState cMenuEditStrItem::ProcessKey(eKeys Key) { + bool SameKey = Key == lastKey; + if (Key != kNone) + lastKey = Key; switch (Key) { case kRed: // Switch between upper- and lowercase characters - if (pos >= 0) { + if (InEditMode()) { if (!insert || !newchar) { uppercase = !uppercase; value[pos] = uppercase ? toupper(value[pos]) : tolower(value[pos]); @@ -332,7 +338,7 @@ eOSState cMenuEditStrItem::ProcessKey(eKeys Key) return osUnknown; break; case kGreen: // Toggle insert/overwrite modes - if (pos >= 0) { + if (InEditMode()) { insert = !insert; newchar = true; SetHelpKeys(); @@ -342,7 +348,7 @@ eOSState cMenuEditStrItem::ProcessKey(eKeys Key) break; case kYellow|k_Repeat: case kYellow: // Remove the character at current position; in insert mode it is the character to the right of cursor - if (pos >= 0) { + if (InEditMode()) { if (strlen(value) > 1) { if (!insert || pos < int(strlen(value)) - 1) memmove(value + pos, value + pos + 1, strlen(value) - pos); @@ -361,7 +367,7 @@ eOSState cMenuEditStrItem::ProcessKey(eKeys Key) break; case kBlue|k_Repeat: case kBlue: // consume the key only if in edit-mode - if (pos >= 0) + if (InEditMode()) ; else return osUnknown; @@ -395,7 +401,7 @@ eOSState cMenuEditStrItem::ProcessKey(eKeys Key) case kUp|k_Repeat: case kUp: case kDown|k_Repeat: - case kDown: if (pos >= 0) { + case kDown: if (InEditMode()) { if (insert && newchar) { // create a new character in insert mode if (int(strlen(value)) < length - 1) { @@ -412,7 +418,39 @@ eOSState cMenuEditStrItem::ProcessKey(eKeys Key) else return cMenuEditItem::ProcessKey(Key); break; - case kOk: if (pos >= 0) { + case k0 ... k9: { + if (!SameKey) + currentChar = NULL; + if (InEditMode()) { + if (insert && newchar) { + // create a new character in insert mode + if (int(strlen(value)) < length - 1) { + memmove(value + pos + 1, value + pos, strlen(value) - pos + 1); + value[pos] = ' '; + } + } + if (!currentChar || !*currentChar || *currentChar == '\t') { + // find the beginning of the character map entry for Key + int n = Key - k0; + currentChar = charMap; + while (n > 0 && *currentChar) { + if (*currentChar++ == '\t') + n--; + } + } + if (*currentChar && *currentChar != '\t') { + value[pos] = *currentChar; + if (uppercase) + value[pos] = toupper(value[pos]); + currentChar++; + } + newchar = false; + } + else + return cMenuEditItem::ProcessKey(Key); + } + break; + case kOk: if (InEditMode()) { pos = -1; newchar = true; stripspace(value); @@ -420,7 +458,7 @@ eOSState cMenuEditStrItem::ProcessKey(eKeys Key) break; } // run into default - default: if (pos >= 0 && BASICKEY(Key) == kKbd) { + default: if (InEditMode() && BASICKEY(Key) == kKbd) { int c = KEYKBD(Key); if (c <= 0xFF) { const char *p = strchr(allowed, tolower(c)); @@ -667,6 +705,7 @@ eOSState cMenuEditDateItem::ProcessKey(eKeys Key) } else { *weekdays = days[cTimer::GetWDay(*value)]; + dayindex = FindDayIndex(*weekdays); oldvalue = *value; *value = 0; } diff --git a/menuitems.h b/menuitems.h index f9afc15..bfaf436 100644 --- a/menuitems.h +++ b/menuitems.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menuitems.h 1.12 2005/11/11 13:26:51 kls Exp $ + * $Id: menuitems.h 1.15 2006/01/06 15:16:25 kls Exp $ */ #ifndef __MENUITEMS_H @@ -44,12 +44,12 @@ public: class cMenuEditBitItem : public cMenuEditBoolItem { protected: - int *value; + uint *value; + uint mask; int bit; - int mask; virtual void Set(void); public: - cMenuEditBitItem(const char *Name, int *Value, int Mask, const char *FalseString = NULL, const char *TrueString = NULL); + cMenuEditBitItem(const char *Name, uint *Value, uint Mask, const char *FalseString = NULL, const char *TrueString = NULL); }; class cMenuEditNumItem : public cMenuEditItem { @@ -82,9 +82,14 @@ private: char *allowed; int pos; bool insert, newchar, uppercase; + const char *charMap; + const char *currentChar; + eKeys lastKey; void SetHelpKeys(void); virtual void Set(void); char Inc(char c, bool Up); +protected: + bool InEditMode(void) { return pos >= 0; } public: cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed); ~cMenuEditStrItem(); @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osd.c 1.64 2005/11/04 14:19:31 kls Exp $ + * $Id: osd.c 1.65 2005/12/30 15:42:04 kls Exp $ */ #include "osd.h" @@ -145,7 +145,7 @@ void cBitmap::SetSize(int Width, int Height) esyslog("ERROR: can't allocate bitmap!"); } else - esyslog("ERROR: illegal bitmap parameters (%d, %d)!", width, height); + esyslog("ERROR: invalid bitmap parameters (%d, %d)!", width, height); } bool cBitmap::Contains(int x, int y) const @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osd.h 1.49 2005/06/19 10:35:25 kls Exp $ + * $Id: osd.h 1.50 2005/12/18 12:56:21 kls Exp $ */ #ifndef __OSD_H @@ -37,6 +37,7 @@ enum eOsdError { oeOk, oeAreasOverlap, oeWrongAlignment, oeOutOfMemory, + oeWrongAreaSize, oeUnknown, }; @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osdbase.c 1.24 2005/10/09 10:56:26 kls Exp $ + * $Id: osdbase.c 1.28 2006/01/08 11:40:02 kls Exp $ */ #include "osdbase.h" @@ -19,7 +19,6 @@ cOsdItem::cOsdItem(eOSState State) { text = NULL; - offset = -1; state = State; selectable = true; fresh = true; @@ -28,7 +27,6 @@ cOsdItem::cOsdItem(eOSState State) cOsdItem::cOsdItem(const char *Text, eOSState State, bool Selectable) { text = NULL; - offset = -1; state = State; selectable = Selectable; fresh = true; @@ -61,6 +59,14 @@ eOSState cOsdItem::ProcessKey(eKeys Key) return Key == kOk ? state : osUnknown; } +// --- cOsdObject ------------------------------------------------------------ + +void cOsdObject::Show(void) +{ + if (isMenu) + ((cOsdMenu *)this)->Display(); +} + // --- cOsdMenu -------------------------------------------------------------- cSkinDisplayMenu *cOsdMenu::displayMenu = NULL; @@ -155,10 +161,10 @@ void cOsdMenu::Del(int Index) { cList<cOsdItem>::Del(Get(Index)); int count = Count(); - while (current < count && !SelectableItem(current)) + while (current < count && !SelectableItem(current)) current++; if (current == count) { - while (current > 0 && !SelectableItem(current)) + while (current > 0 && !SelectableItem(current)) current--; } if (Index == first && first > 0) @@ -252,6 +258,8 @@ void cOsdMenu::DisplayCurrent(bool Current) void cOsdMenu::Clear(void) { + if (marked >= 0) + SetStatus(NULL); first = 0; current = marked = -1; cList<cOsdItem>::Clear(); @@ -363,7 +371,7 @@ void cOsdMenu::PageUp(void) CursorUp(); } -void cOsdMenu::PageDown(void) +void cOsdMenu::PageDown(void) { int oldCurrent = current; int oldFirst = first; @@ -455,6 +463,7 @@ eOSState cOsdMenu::ProcessKey(eKeys Key) } } switch (Key) { + case k0: return osUnknown; case k1...k9: return hasHotkeys ? HotKey(Key) : osUnknown; case kUp|k_Repeat: case kUp: CursorUp(); break; @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osdbase.h 1.12 2005/10/02 09:18:20 kls Exp $ + * $Id: osdbase.h 1.14 2006/01/06 11:55:30 kls Exp $ */ #ifndef __OSDBASE_H @@ -49,7 +49,6 @@ enum eOSState { osUnknown, class cOsdItem : public cListObject { private: char *text; - int offset; eOSState state; bool selectable; protected: @@ -78,7 +77,7 @@ public: virtual ~cOsdObject() {} bool NeedsFastResponse(void) { return needsFastResponse; } bool IsMenu(void) { return isMenu; } - virtual void Show(void) {} + virtual void Show(void); virtual eOSState ProcessKey(eKeys Key) { return osUnknown; } }; @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: player.c 1.9 2004/12/12 11:21:07 kls Exp $ + * $Id: player.c 1.11 2006/01/06 11:30:07 kls Exp $ */ #include "player.h" @@ -40,6 +40,7 @@ void cPlayer::Detach(void) // --- cControl -------------------------------------------------------------- cControl *cControl::control = NULL; +cMutex cControl::mutex; cControl::cControl(cPlayer *Player, bool Hidden) { @@ -54,19 +55,27 @@ cControl::~cControl() control = NULL; } +cOsdObject *cControl::GetInfo(void) +{ + return NULL; +} + cControl *cControl::Control(void) { + cMutexLock MutexLock(&mutex); return (control && !control->hidden) ? control : NULL; } void cControl::Launch(cControl *Control) { + cMutexLock MutexLock(&mutex); delete control; control = Control; } void cControl::Attach(void) { + cMutexLock MutexLock(&mutex); if (control && !control->attached && control->player && !control->player->IsAttached()) { if (cDevice::PrimaryDevice()->AttachPlayer(control->player)) control->attached = true; @@ -79,6 +88,7 @@ void cControl::Attach(void) void cControl::Shutdown(void) { + cMutexLock MutexLock(&mutex); cControl *c = control; // avoids recursions control = NULL; delete c; @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: player.h 1.17 2005/05/22 11:07:42 kls Exp $ + * $Id: player.h 1.19 2006/01/06 11:29:27 kls Exp $ */ #ifndef __PLAYER_H @@ -62,6 +62,7 @@ public: class cControl : public cOsdObject { private: static cControl *control; + static cMutex mutex; bool attached; bool hidden; protected: @@ -70,6 +71,7 @@ public: cControl(cPlayer *Player, bool Hidden = false); virtual ~cControl(); virtual void Hide(void) = 0; + virtual cOsdObject *GetInfo(void); bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false) { return player->GetIndex(Current, Total, SnapToIFrame); } bool GetReplayMode(bool &Play, bool &Forward, int &Speed) { return player->GetReplayMode(Play, Forward, Speed); } static void Launch(cControl *Control); @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: plugin.c 1.15 2005/08/27 16:13:24 kls Exp $ + * $Id: plugin.c 1.16 2006/01/08 11:40:05 kls Exp $ */ #include "plugin.h" @@ -25,7 +25,7 @@ char *cPlugin::configDirectory = NULL; -cPlugin::cPlugin(void) +cPlugin::cPlugin(void) { name = NULL; started = false; @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: rcu.c 1.10 2005/08/15 12:30:21 kls Exp $ + * $Id: rcu.c 1.13 2006/01/08 11:40:09 kls Exp $ */ #include "rcu.h" @@ -23,8 +23,8 @@ cRcuRemote::cRcuRemote(const char *DeviceName) dp = 0; mode = modeB; code = 0; - numberToSend = -1; - lastNumber = 0; + number = 0; + data = 0; receivedCommand = false; if ((f = open(DeviceName, O_RDWR | O_NONBLOCK)) >= 0) { struct termios t; @@ -32,7 +32,7 @@ cRcuRemote::cRcuRemote(const char *DeviceName) cfsetspeed(&t, B9600); cfmakeraw(&t); if (tcsetattr(f, TCSAFLUSH, &t) == 0) { - Number(0);//XXX 8888??? + SetNumber(8888); const char *Setup = GetSetup(); if (Setup) { code = *Setup; @@ -95,13 +95,12 @@ void cRcuRemote::Action(void) time_t LastCodeRefresh = 0; cTimeMs FirstTime; + unsigned char LastCode = 0, LastMode = 0; uint64 LastCommand = 0; + unsigned int LastData = 0; bool repeat = false; while (Running() && f >= 0) { - - LOCK_THREAD; - if (ReceiveByte(REPEATLIMIT) == 'X') { for (int i = 0; i < 6; i++) { int b = ReceiveByte(); @@ -140,11 +139,22 @@ void cRcuRemote::Action(void) LastCommand = 0; } else { - LastCommand = 0; - if (numberToSend >= 0) { - Number(numberToSend); - numberToSend = -1; + unsigned int d = data; + if (d != LastData) { + SendData(d); + LastData = d; } + unsigned char c = code; + if (c != LastCode) { + SendCommand(c); + LastCode = c; + } + unsigned char m = mode; + if (m != LastMode) { + SendCommand(m); + LastMode = m; + } + LastCommand = 0; } if (code && time(NULL) - LastCodeRefresh > 60) { SendCommand(code); // in case the PIC listens to the wrong code @@ -192,8 +202,6 @@ bool cRcuRemote::SendByteHandshake(unsigned char c) bool cRcuRemote::SendByte(unsigned char c) { - LOCK_THREAD; - for (int retry = 5; retry--;) { if (SendByteHandshake(c)) return true; @@ -201,32 +209,34 @@ bool cRcuRemote::SendByte(unsigned char c) return false; } -bool cRcuRemote::SetCode(unsigned char Code) +bool cRcuRemote::SendData(unsigned int n) +{ + for (int i = 0; i < 4; i++) { + if (!SendByte(n & 0x7F)) + return false; + n >>= 8; + } + return SendCommand(mode); +} + +void cRcuRemote::SetCode(unsigned char Code) { code = Code; - return SendCommand(code); } -bool cRcuRemote::SetMode(unsigned char Mode) +void cRcuRemote::SetMode(unsigned char Mode) { mode = Mode; - return SendCommand(mode); } bool cRcuRemote::SendCommand(unsigned char Cmd) -{ +{ return SendByte(Cmd | 0x80); } -bool cRcuRemote::Digit(int n, int v) -{ - return SendByte(((n & 0x03) << 5) | (v & 0x0F) | (((dp >> n) & 0x01) << 4)); -} - -bool cRcuRemote::Number(int n, bool Hex) +void cRcuRemote::SetNumber(int n, bool Hex) { - LOCK_THREAD; - + number = n; if (!Hex) { char buf[8]; sprintf(buf, "%4d", n & 0xFFFF); @@ -237,19 +247,17 @@ bool cRcuRemote::Number(int n, bool Hex) n = (n << 4) | ((*d - '0') & 0x0F); } } - lastNumber = n; + unsigned int m = 0; for (int i = 0; i < 4; i++) { - if (!Digit(i, n)) - return false; + m <<= 8; + m |= ((i & 0x03) << 5) | (n & 0x0F) | (((dp >> i) & 0x01) << 4); n >>= 4; } - return SendCommand(mode); + data = m; } -bool cRcuRemote::String(char *s) +void cRcuRemote::SetString(char *s) { - LOCK_THREAD; - const char *chars = mode == modeH ? "0123456789ABCDEF" : "0123456789-EHLP "; int n = 0; @@ -262,16 +270,16 @@ bool cRcuRemote::String(char *s) } } } - return Number(n, true); + SetNumber(n, true); } void cRcuRemote::SetPoints(unsigned char Dp, bool On) -{ +{ if (On) dp |= Dp; else dp &= ~Dp; - Number(lastNumber, true); + SetNumber(number); } bool cRcuRemote::DetectCode(unsigned char *Code) @@ -291,12 +299,12 @@ bool cRcuRemote::DetectCode(unsigned char *Code) SetMode(modeH); char buf[5]; sprintf(buf, "C0D%c", *Code); - String(buf); + SetString(buf); SetCode(*Code); cCondWait::SleepMs(2 * REPEATDELAY); if (receivedCommand) { SetMode(modeB); - String("----"); + SetString("----"); return true; } if (*Code < 'D') { @@ -310,13 +318,11 @@ bool cRcuRemote::DetectCode(unsigned char *Code) void cRcuRemote::ChannelSwitch(const cDevice *Device, int ChannelNumber) { - if (ChannelNumber && Device->IsPrimaryDevice()) { - LOCK_THREAD; - numberToSend = cDevice::CurrentChannel(); - } + if (ChannelNumber && Device->IsPrimaryDevice()) + SetNumber(cDevice::CurrentChannel()); } -void cRcuRemote::Recording(const cDevice *Device, const char *Name) +void cRcuRemote::Recording(const cDevice *Device, const char *Name, const char *FileName, bool On) { SetPoints(1 << Device->DeviceNumber(), Device->Receiving()); } @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: rcu.h 1.4 2005/07/31 10:18:00 kls Exp $ + * $Id: rcu.h 1.6 2005/12/31 15:09:25 kls Exp $ */ #ifndef __RCU_H @@ -19,23 +19,23 @@ private: enum { modeH = 'h', modeB = 'b', modeS = 's' }; int f; unsigned char dp, code, mode; - int numberToSend; - int lastNumber; + int number; + unsigned int data; bool receivedCommand; bool SendCommand(unsigned char Cmd); int ReceiveByte(int TimeoutMs = 0); bool SendByteHandshake(unsigned char c); bool SendByte(unsigned char c); - bool Digit(int n, int v); - bool SetCode(unsigned char Code); - bool SetMode(unsigned char Mode); - bool Number(int n, bool Hex = false); + bool SendData(unsigned int n); + void SetCode(unsigned char Code); + void SetMode(unsigned char Mode); + void SetNumber(int n, bool Hex = false); void SetPoints(unsigned char Dp, bool On); - bool String(char *s); + void SetString(char *s); bool DetectCode(unsigned char *Code); virtual void Action(void); virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber); - virtual void Recording(const cDevice *Device, const char *Name); + virtual void Recording(const cDevice *Device, const char *Name, const char *FileName, bool On); public: cRcuRemote(const char *DeviceName); virtual ~cRcuRemote(); @@ -4,13 +4,13 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: recorder.c 1.16 2005/10/31 12:35:29 kls Exp $ + * $Id: recorder.c 1.17 2006/01/08 11:01:25 kls Exp $ */ +#include "recorder.h" #include <stdarg.h> #include <stdio.h> #include <unistd.h> -#include "recorder.h" #define RECORDERBUFSIZE MEGABYTE(5) @@ -171,6 +171,8 @@ void cRecorder::Action(void) int Count = remux->Put(b, r); if (Count) ringBuffer->Del(Count); + else + cCondWait::SleepMs(100); // avoid busy loop when resultBuffer is full in cRemux::Put() } } } diff --git a/recording.c b/recording.c index 7a81d77..0f704af 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.124 2005/11/04 14:19:44 kls Exp $ + * $Id: recording.c 1.132 2006/01/08 11:40:13 kls Exp $ */ #include "recording.h" @@ -48,8 +48,8 @@ #define MINDISKSPACE 1024 // MB -#define DELETEDLIFETIME 1 // hours after which a deleted recording will be actually removed -#define REMOVECHECKDELTA 3600 // seconds between checks for removing deleted files +#define REMOVECHECKDELTA 60 // seconds between checks for removing deleted files +#define DELETEDLIFETIME 300 // seconds after which a deleted recording will be actually removed #define DISKCHECKDELTA 100 // seconds between checks for free disk space #define REMOVELATENCY 10 // seconds to wait until next check after removing a file @@ -60,46 +60,67 @@ bool VfatFileSystem = false; -static cRecordings DeletedRecordings(true); +cRecordings DeletedRecordings(true); + +// --- cRemoveDeletedRecordingsThread ---------------------------------------- + +class cRemoveDeletedRecordingsThread : public cThread { +protected: + virtual void Action(void); +public: + cRemoveDeletedRecordingsThread(void); + }; + +cRemoveDeletedRecordingsThread::cRemoveDeletedRecordingsThread(void) +:cThread("remove deleted recordings") +{ +} + +void cRemoveDeletedRecordingsThread::Action(void) +{ + // Make sure only one instance of VDR does this: + cLockFile LockFile(VideoDirectory); + if (LockFile.Lock()) { + cThreadLock DeletedRecordingsLock(&DeletedRecordings); + for (cRecording *r = DeletedRecordings.First(); r; ) { + if (r->deleted && time(NULL) - r->deleted > DELETEDLIFETIME) { + cRecording *next = DeletedRecordings.Next(r); + r->Remove(); + DeletedRecordings.Del(r); + r = next; + RemoveEmptyVideoDirectories(); + continue; + } + r = DeletedRecordings.Next(r); + } + } +} + +static cRemoveDeletedRecordingsThread RemoveDeletedRecordingsThread; + +// --- void RemoveDeletedRecordings(void) { static time_t LastRemoveCheck = 0; - if (LastRemoveCheck == 0) { - DeletedRecordings.Update(); - LastRemoveCheck = time(NULL) - REMOVECHECKDELTA * 9 / 10; - } - else if (time(NULL) - LastRemoveCheck > REMOVECHECKDELTA) { - // Make sure only one instance of VDR does this: - cLockFile LockFile(VideoDirectory); - if (!LockFile.Lock()) - return; - // Remove the oldest file that has been "deleted": - cThreadLock DeletedRecordingsLock(&DeletedRecordings); - if (DeletedRecordings.Count()) { - cRecording *r = DeletedRecordings.First(); - cRecording *r0 = r; - while (r) { - if (r->start < r0->start) - r0 = r; - r = DeletedRecordings.Next(r); - } - if (r0 && time(NULL) - r0->start > DELETEDLIFETIME * 3600) { - r0->Remove(); - DeletedRecordings.Del(r0); - RemoveEmptyVideoDirectories(); - LastRemoveCheck += REMOVELATENCY; - return; - } + if (time(NULL) - LastRemoveCheck > REMOVECHECKDELTA) { + if (!RemoveDeletedRecordingsThread.Active()) { + cThreadLock DeletedRecordingsLock(&DeletedRecordings); + for (cRecording *r = DeletedRecordings.First(); r; r = DeletedRecordings.Next(r)) { + if (r->deleted && time(NULL) - r->deleted > DELETEDLIFETIME) { + RemoveDeletedRecordingsThread.Start(); + break; + } + } } - else - DeletedRecordings.Update(); LastRemoveCheck = time(NULL); } } void AssertFreeDiskSpace(int Priority) { + static cMutex Mutex; + cMutexLock MutexLock(&Mutex); // With every call to this function we try to actually remove // a file, or mark a file for removal ("delete" it), so that // it will get removed during the next call. @@ -160,7 +181,7 @@ void AssertFreeDiskSpace(int Priority) } // Unable to free disk space, but there's nothing we can do about that... isyslog("...no old recording found, giving up"); - Interface->Confirm(tr("Low disk space!"), 30); + Skins.QueueMessage(mtWarning, tr("Low disk space!"), 5, -1); } LastFreeDiskCheck = time(NULL); } @@ -400,6 +421,8 @@ cRecording::cRecording(cTimer *Timer, const cEvent *Event) sortBuffer = NULL; fileName = NULL; name = NULL; + fileSizeMB = -1; // unknown + deleted = 0; // set up the actual name: const char *Title = Event ? Event->Title() : NULL; const char *Subtitle = Event ? Event->ShortText() : NULL; @@ -453,6 +476,8 @@ cRecording::cRecording(cTimer *Timer, const cEvent *Event) cRecording::cRecording(const char *FileName) { resume = RESUME_NOT_INITIALIZED; + fileSizeMB = -1; // unknown + deleted = 0; titleBuffer = NULL; sortBuffer = NULL; fileName = strdup(FileName); @@ -525,7 +550,7 @@ cRecording::cRecording(const char *FileName) // so assume the short text is missing and concatenate // line 1 and line 2 to be the long text: int len = strlen(data[1]); - if (len > 80) { + if (len > 80) { data[1] = (char *)realloc(data[1], len + 1 + strlen(data[2]) + 1); strcat(data[1], "\n"); strcat(data[1], data[2]); @@ -714,7 +739,7 @@ bool cRecording::Delete(void) bool result = true; char *NewName = strdup(FileName()); char *ext = strrchr(NewName, '.'); - if (strcmp(ext, RECEXT) == 0) { + if (ext && strcmp(ext, RECEXT) == 0) { strncpy(ext, DELEXT, strlen(ext)); if (access(NewName, F_OK) == 0) { // the new name already exists, so let's remove that one first: @@ -814,6 +839,10 @@ void cRecordings::ScanVideoDir(const char *DirName, bool Foreground) Add(r); ChangeState(); Unlock(); + if (deleted) { + r->fileSizeMB = DirSizeMB(buffer); + r->deleted = time(NULL); + } } else delete r; @@ -883,12 +912,33 @@ void cRecordings::DelByName(const char *FileName) LOCK_THREAD; cRecording *recording = GetByName(FileName); if (recording) { - Del(recording); + cThreadLock DeletedRecordingsLock(&DeletedRecordings); + Del(recording, false); + char *ext = strrchr(recording->FileName(), '.'); + if (ext) { + strncpy(ext, DELEXT, strlen(ext)); + recording->fileSizeMB = DirSizeMB(recording->FileName()); + recording->deleted = time(NULL); + DeletedRecordings.Add(recording); + } + else + delete recording; ChangeState(); TouchUpdate(); } } +int cRecordings::TotalFileSizeMB(void) +{ + int size = 0; + LOCK_THREAD; + for (cRecording *recording = First(); recording; recording = Next(recording)) { + if (recording->fileSizeMB > 0) + size += recording->fileSizeMB; + } + return size; +} + void cRecordings::ResetResume(const char *ResumeFileName) { LOCK_THREAD; diff --git a/recording.h b/recording.h index 28a1cd2..d79b6b4 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.46 2005/10/31 12:27:12 kls Exp $ + * $Id: recording.h 1.48 2005/12/18 11:26:51 kls Exp $ */ #ifndef __RECORDING_H @@ -55,12 +55,14 @@ public: }; class cRecording : public cListObject { + friend class cRecordings; private: mutable int resume; mutable char *titleBuffer; mutable char *sortBuffer; mutable char *fileName; mutable char *name; + mutable int fileSizeMB; cRecordingInfo *info; static char *StripEpisodeName(char *s); char *SortName(void) const; @@ -69,6 +71,7 @@ public: time_t start; int priority; int lifetime; + time_t deleted; cRecording(cTimer *Timer, const cEvent *Event); cRecording(const char *FileName); virtual ~cRecording(); @@ -126,9 +129,11 @@ public: cRecording *GetByName(const char *FileName); void AddByName(const char *FileName); void DelByName(const char *FileName); + int TotalFileSizeMB(void); ///< Only for deleted recordings! }; extern cRecordings Recordings; +extern cRecordings DeletedRecordings; class cMark : public cListObject { public: @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: remote.c 1.45 2005/09/03 12:29:48 kls Exp $ + * $Id: remote.c 1.46 2006/01/01 14:21:07 kls Exp $ */ #include "remote.h" @@ -262,43 +262,72 @@ int cKbdRemote::MapCodeToFunc(uint64 Code) return (Code <= 0xFF) ? Code : kfNone; } -void cKbdRemote::Action(void) +int cKbdRemote::ReadKey(void) { cPoller Poller(STDIN_FILENO); + if (Poller.Poll(50)) { + uchar ch = 0; + int r = safe_read(STDIN_FILENO, &ch, 1); + if (r == 1) + return ch; + if (r < 0) + LOG_ERROR_STR("cKbdRemote"); + } + return -1; +} + +uint64 cKbdRemote::ReadKeySequence(void) +{ + uint64 k = 0; + int key1; + + if ((key1 = ReadKey()) >= 0) { + k = key1; + if (key1 == 0x1B) { + // Start of escape sequence + if ((key1 = ReadKey()) >= 0) { + k <<= 8; + k |= key1 & 0xFF; + switch (key1) { + case 0x4F: // 3-byte sequence + if ((key1 = ReadKey()) >= 0) { + k <<= 8; + k |= key1 & 0xFF; + } + break; + case 0x5B: // 3- or more-byte sequence + if ((key1 = ReadKey()) >= 0) { + k <<= 8; + k |= key1 & 0xFF; + switch (key1) { + case 0x31 ... 0x3F: // more-byte sequence + while (key1 != 0x7E) { + k <<= 8; + k |= key1 & 0xFF; + if ((key1 = ReadKey()) < 0) + break; // Sequence ends here + } + break; + } + } + break; + } + } + } + } + return k; +} + +void cKbdRemote::Action(void) +{ while (Running()) { - if (Poller.Poll(100)) { - uint64 Command = 0; - uint i = 0; - while (Running() && i < sizeof(Command)) { - uchar ch; - int r = read(STDIN_FILENO, &ch, 1); - if (r == 1) { - Command <<= 8; - Command |= ch; - i++; - } - else if (r == 0) { - // don't know why, but sometimes special keys that start with - // 0x1B ('ESC') cause a short gap between the 0x1B and the rest - // of their codes, so we'll need to wait some 100ms to see if - // there is more coming up - or whether this really is the 'ESC' - // key (if somebody knows how to clean this up, please let me know): - if (Command == 0x1B && Poller.Poll(100)) - continue; - if (Command) { - if (rawMode || !Put(Command)) { - int func = MapCodeToFunc(Command); - if (func) - Put(KBDKEY(func)); - } - } - break; - } - else { - LOG_ERROR; - break; - } - } + uint64 Command = ReadKeySequence(); + if (Command) { + if (rawMode || !Put(Command)) { + int func = MapCodeToFunc(Command); + if (func) + Put(KBDKEY(func)); + } } } } @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: remote.h 1.31 2005/09/03 12:28:42 kls Exp $ + * $Id: remote.h 1.32 2006/01/01 14:00:50 kls Exp $ */ #ifndef __REMOTE_H @@ -89,6 +89,8 @@ private: static bool rawMode; struct termios savedTm; virtual void Action(void); + int ReadKey(void); + uint64 ReadKeySequence(void); int MapCodeToFunc(uint64 Code); public: cKbdRemote(void); @@ -11,7 +11,7 @@ * The cRepacker family's code was originally written by Reinhard Nissl <rnissl@gmx.de>, * and adapted to the VDR coding style by Klaus.Schmidinger@cadsoft.de. * - * $Id: remux.c 1.47 2005/09/11 13:26:50 kls Exp $ + * $Id: remux.c 1.53 2006/01/08 11:40:16 kls Exp $ */ #include "remux.h" @@ -88,15 +88,21 @@ ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, // --- cRepacker ------------------------------------------------------------- +#define MIN_LOG_INTERVAL 10 // min. # of seconds between two consecutive log messages of a cRepacker +#define LOG(a...) (LogAllowed() && (esyslog(a), true)) + class cRepacker { protected: bool initiallySyncing; int maxPacketSize; uint8_t subStreamId; - static void DroppedData(const char *Reason, int Count) { esyslog("%s (dropped %d bytes)", Reason, Count); } + time_t lastLog; + int suppressedLogMessages; + bool LogAllowed(void); + void DroppedData(const char *Reason, int Count) { LOG("%s (dropped %d bytes)", Reason, Count); } public: static int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded); - cRepacker(void) { initiallySyncing = true; maxPacketSize = 6 + 65535; subStreamId = 0; } + cRepacker(void); virtual ~cRepacker() {} virtual void Reset(void) { initiallySyncing = true; } virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count) = 0; @@ -106,6 +112,30 @@ public: void SetSubStreamId(uint8_t SubStreamId) { subStreamId = SubStreamId; } }; +cRepacker::cRepacker(void) +{ + initiallySyncing = true; + maxPacketSize = 6 + 65535; + subStreamId = 0; + suppressedLogMessages = 0;; + lastLog = 0; +} + +bool cRepacker::LogAllowed(void) +{ + bool Allowed = time(NULL) - lastLog >= MIN_LOG_INTERVAL; + lastLog = time(NULL); + if (Allowed) { + if (suppressedLogMessages) { + esyslog("%d cRepacker messages suppressed", suppressedLogMessages); + suppressedLogMessages = 0; + } + } + else + suppressedLogMessages++; + return Allowed; +} + int cRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded) { if (CapacityNeeded >= Count && ResultBuffer->Free() < CapacityNeeded) { @@ -160,7 +190,7 @@ bool cCommonRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar // just skip packets with no payload int PesPayloadOffset = 0; if (AnalyzePesHeader(fragmentData, fragmentLen, PesPayloadOffset) <= phInvalid) - esyslog("cCommonRepacker: invalid PES packet encountered in fragment buffer!"); + LOG("cCommonRepacker: invalid PES packet encountered in fragment buffer!"); else if (6 + PacketLen <= PesPayloadOffset) { fragmentLen = 0; return true; // skip empty packet @@ -181,7 +211,7 @@ bool cCommonRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar // just skip packets with no payload int PesPayloadOffset = 0; if (AnalyzePesHeader(pesHeader, pesHeaderLen, PesPayloadOffset) <= phInvalid) - esyslog("cCommonRepacker: invalid PES packet encountered in header buffer!"); + LOG("cCommonRepacker: invalid PES packet encountered in header buffer!"); else if (6 + PacketLen <= PesPayloadOffset) { pesHeaderLen = 0; return true; // skip empty packet @@ -216,7 +246,8 @@ private: syncing, findPicture, scanPicture - } state; + }; + int state; public: cVideoRepacker(void); virtual void Reset(void); @@ -278,14 +309,14 @@ void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, // which kind of start code have we got? switch (*data) { case 0xB9 ... 0xFF: // system start codes - esyslog("cVideoRepacker: found system start code: stream seems to be scrambled or not demultiplexed"); + LOG("cVideoRepacker: found system start code: stream seems to be scrambled or not demultiplexed"); break; case 0xB0 ... 0xB1: // reserved start codes case 0xB6: - esyslog("cVideoRepacker: found reserved start code: stream seems to be scrambled"); + LOG("cVideoRepacker: found reserved start code: stream seems to be scrambled"); break; case 0xB4: // sequence error code - isyslog("cVideoRepacker: found sequence error code: stream seems to be damaged"); + LOG("cVideoRepacker: found sequence error code: stream seems to be damaged"); case 0xB2: // user data start code case 0xB5: // extension start code break; @@ -307,7 +338,7 @@ void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, if (initiallySyncing) // omit report for the typical initial case initiallySyncing = false; else if (skippedBytes > SkippedBytesLimit) // report that syncing dropped some bytes - esyslog("cVideoRepacker: skipped %d bytes to sync on next picture", skippedBytes - SkippedBytesLimit); + LOG("cVideoRepacker: skipped %d bytes to sync on next picture", skippedBytes - SkippedBytesLimit); skippedBytes = 0; // if there is a PES header available, then use it ... if (pesHeaderBackupLen > 0) { @@ -350,13 +381,13 @@ void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, // maximum we can hold in one PES packet packetTodo = maxPacketSize - pesHeaderLen; // go on with finding the picture data - ((int &)state)++; + state++; } break; case 0x01 ... 0xAF: // slice start codes if (state == findPicture) { // go on with scanning the picture data - ((int &)state)++; + state++; } break; } @@ -465,7 +496,7 @@ void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, // report that syncing dropped some bytes if (skippedBytes > SkippedBytesLimit) { if (!initiallySyncing) // omit report for the typical initial case - esyslog("cVideoRepacker: skipped %d bytes while syncing on next picture", skippedBytes - SkippedBytesLimit); + LOG("cVideoRepacker: skipped %d bytes while syncing on next picture", skippedBytes - SkippedBytesLimit); skippedBytes = SkippedBytesLimit; } } @@ -517,7 +548,8 @@ private: enum eState { syncing, scanFrame - } state; + }; + int state; int frameTodo; int frameSize; int cid; @@ -674,7 +706,7 @@ void cAudioRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, if (initiallySyncing) // omit report for the typical initial case initiallySyncing = false; else if (skippedBytes > SkippedBytesLimit) // report that syncing dropped some bytes - esyslog("cAudioRepacker(0x%02X): skipped %d bytes to sync on next audio frame", cid, skippedBytes - SkippedBytesLimit); + LOG("cAudioRepacker(0x%02X): skipped %d bytes to sync on next audio frame", cid, skippedBytes - SkippedBytesLimit); skippedBytes = 0; // if there is a PES header available, then use it ... if (pesHeaderBackupLen > 0) { @@ -714,10 +746,10 @@ void cAudioRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, payload = data; // maximum we can hold in one PES packet packetTodo = maxPacketSize - pesHeaderLen; - // expected remainder of audio frame: so far we have read 3 bytes from the frame header + // expected remainder of audio frame: so far we have read 3 bytes from the frame header frameTodo = frameSize - 3; // go on with collecting the frame's data - ((int &)state)++; + state++; } } } @@ -832,7 +864,7 @@ void cAudioRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, // report that syncing dropped some bytes if (skippedBytes > SkippedBytesLimit) { if (!initiallySyncing) // omit report for the typical initial case - esyslog("cAudioRepacker(0x%02X): skipped %d bytes while syncing on next audio frame", cid, skippedBytes - SkippedBytesLimit); + LOG("cAudioRepacker(0x%02X): skipped %d bytes while syncing on next audio frame", cid, skippedBytes - SkippedBytesLimit); skippedBytes = SkippedBytesLimit; } } @@ -898,14 +930,15 @@ private: uchar chk1; uchar chk2; int ac3todo; - enum { + enum eState { find_0b, find_77, store_chk1, store_chk2, get_length, output_packet - } state; + }; + int state; int skippedBytes; void ResetPesHeader(bool ContinuationFrame = false); void AppendSubStreamID(bool ContinuationFrame = false); @@ -1090,7 +1123,7 @@ void cDolbyRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, switch (state) { case find_0b: if (*data == 0x0B) { - ++(int &)state; + state++; // copy header information once for later use if (pesHeaderBackupLen > 0) { pesHeaderLen = pesHeaderBackupLen; @@ -1113,21 +1146,21 @@ void cDolbyRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, done++; todo--; skippedBytes++; // collect number of skipped bytes while syncing - ++(int &)state; + state++; continue; case store_chk1: chk1 = *data++; done++; todo--; skippedBytes++; // collect number of skipped bytes while syncing - ++(int &)state; + state++; continue; case store_chk2: chk2 = *data++; done++; todo--; skippedBytes++; // collect number of skipped bytes while syncing - ++(int &)state; + state++; continue; case get_length: ac3todo = 2 * frameSizes[*data]; @@ -1157,7 +1190,7 @@ void cDolbyRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, if (initiallySyncing) // omit report for the typical initial case initiallySyncing = false; else if (skippedBytes > SkippedBytesLimit) // report that syncing dropped some bytes - esyslog("cDolbyRepacker: skipped %d bytes to sync on next AC3 frame", skippedBytes - SkippedBytesLimit); + LOG("cDolbyRepacker: skipped %d bytes to sync on next AC3 frame", skippedBytes - SkippedBytesLimit); skippedBytes = 0; // append read data to header for common output processing pesHeader[pesHeaderLen++] = 0x0B; @@ -1165,7 +1198,7 @@ void cDolbyRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, pesHeader[pesHeaderLen++] = chk1; pesHeader[pesHeaderLen++] = chk2; ac3todo -= 4; - ++(int &)state; + state++; // fall through to output case output_packet: { int bite = 0; @@ -1188,7 +1221,7 @@ void cDolbyRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, // report that syncing dropped some bytes if (skippedBytes > SkippedBytesLimit) { if (!initiallySyncing) // omit report for the typical initial case - esyslog("cDolbyRepacker: skipped %d bytes while syncing on next AC3 frame", skippedBytes - 4); + LOG("cDolbyRepacker: skipped %d bytes while syncing on next AC3 frame", skippedBytes - 4); skippedBytes = SkippedBytesLimit; } } @@ -1248,9 +1281,10 @@ int cDolbyRepacker::BreakAt(const uchar *Data, int Count) #define CONT_CNT_MASK 0x0F // Flags: +#define PAY_LOAD 0x10 +#define ADAPT_FIELD 0x20 #define PAY_START 0x40 #define TS_ERROR 0x80 -#define ADAPT_FIELD 0x20 #define MAX_PLENGTH 0xFFFF // the maximum PES packet length (theoretically) #define MMAX_PLENGTH (64*MAX_PLENGTH) // some stations send PES packets that are extremely large, e.g. DVB-T in Finland or HDTV 1920x1080 @@ -1635,7 +1669,11 @@ void cTS2PES::ts_to_pes(const uint8_t *Buf) // don't need count (=188) if (Buf[1] & TS_ERROR) tsErrors++; - if ((Buf[3] ^ ccCounter) & CONT_CNT_MASK) { + + if (!(Buf[3] & (ADAPT_FIELD | PAY_LOAD))) + return; // discard TS packet with adaption_field_control set to '00'. + + if ((Buf[3] & PAY_LOAD) && ((Buf[3] ^ ccCounter) & CONT_CNT_MASK)) { // This should check duplicates and packets which do not increase the counter. // But as the errors usually come in bursts this should be enough to // show you there is something wrong with signal quality. @@ -1650,12 +1688,14 @@ void cTS2PES::ts_to_pes(const uint8_t *Buf) // don't need count (=188) } if (Buf[1] & PAY_START) { - if (plength == MMAX_PLENGTH - 6 && found > 6) { + if (found > 6) { + if (plength != MMAX_PLENGTH - 6 && plength != found - 6) + dsyslog("PES packet shortened to %d bytes (expected: %d bytes)", found, plength + 6); plength = found - 6; - found = 0; send_ipack(); reset_ipack(); } + found = 0; } uint8_t off = 0; @@ -1666,7 +1706,8 @@ void cTS2PES::ts_to_pes(const uint8_t *Buf) // don't need count (=188) return; } - instant_repack(Buf + 4 + off, TS_SIZE - 4 - off); + if (Buf[3] & PAY_LOAD) + instant_repack(Buf + 4 + off, TS_SIZE - 4 - off); } // --- cRemux ---------------------------------------------------------------- diff --git a/ringbuffer.c b/ringbuffer.c index 3473a9e..e00a524 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.21 2004/10/15 13:49:25 kls Exp $ + * $Id: ringbuffer.c 1.23 2005/12/30 15:42:08 kls Exp $ */ #include "ringbuffer.h" @@ -46,7 +46,7 @@ void cRingBuffer::UpdatePercentage(int Fill) int percent = Fill * 100 / (Size() - 1) / PERCENTAGEDELTA * PERCENTAGEDELTA; if (percent != lastPercent) { if (percent >= PERCENTAGETHRESHOLD && percent > lastPercent || percent < PERCENTAGETHRESHOLD && lastPercent >= PERCENTAGETHRESHOLD) { - dsyslog("buffer usage: %d%% (tid=%ld)", percent, getThreadTid); + dsyslog("buffer usage: %d%% (tid=%d)", percent, getThreadTid); lastPercent = percent; } } @@ -165,10 +165,10 @@ cRingBufferLinear::cRingBufferLinear(int Size, int Margin, bool Statistics, cons Clear(); } else - esyslog("ERROR: illegal margin for ring buffer (%d > %d)", Margin, Size / 2); + esyslog("ERROR: invalid margin for ring buffer (%d > %d)", Margin, Size / 2); } else - esyslog("ERROR: illegal size for ring buffer (%d)", Size); + esyslog("ERROR: invalid size for ring buffer (%d)", Size); #ifdef DEBUGRINGBUFFERS lastHead = head; lastTail = tail; @@ -286,7 +286,7 @@ uchar *cRingBufferLinear::Get(int &Count) uchar *p = NULL; int Head = head; if (getThreadTid <= 0) - getThreadTid = pthread_self(); + getThreadTid = cThread::ThreadId(); int rest = Size() - tail; if (rest < margin && Head < tail) { int t = margin - rest; diff --git a/ringbuffer.h b/ringbuffer.h index c47aac3..dba0e61 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.16 2004/10/15 13:50:46 kls Exp $ + * $Id: ringbuffer.h 1.17 2005/12/10 10:54:51 kls Exp $ */ #ifndef __RINGBUFFER_H @@ -23,7 +23,7 @@ private: int overflowCount; int overflowBytes; protected: - pthread_t getThreadTid; + tThreadId getThreadTid; int maxFill;//XXX int lastPercent; bool statistics;//XXX @@ -7,7 +7,7 @@ # # 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. +# as user 'vdr'. # # Since this script loads the DVB driver, it must be started # as user 'root'. @@ -18,11 +18,11 @@ # See the main source file 'vdr.c' for copyright information and # how to reach the author. # -# $Id: runvdr 1.14 2004/11/21 11:30:00 kls Exp $ +# $Id: runvdr 1.15 2005/12/31 16:04:53 kls Exp $ DVBDIR="../DVB/driver" VDRPRG="./vdr" -VDRCMD="$VDRPRG -w 60 $*" +VDRCMD="$VDRPRG -u $VDRUSR -w 60 $*" LSMOD="`/sbin/lsmod | grep -w '^dvb' | wc -l`" KILL="/usr/bin/killall -q -TERM" @@ -33,7 +33,7 @@ if [ $LSMOD -eq 0 ] ; then fi while (true) do - su $VDRUSR -c "$VDRCMD" + $VDRCMD if test $? -eq 0 -o $? -eq 2; then exit; fi date echo "restarting VDR" diff --git a/skinclassic.c b/skinclassic.c index fd3ab0b..18851a8 100644 --- a/skinclassic.c +++ b/skinclassic.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: skinclassic.c 1.12 2005/05/16 10:45:07 kls Exp $ + * $Id: skinclassic.c 1.13 2006/01/01 14:37:58 kls Exp $ */ #include "skinclassic.h" @@ -378,7 +378,6 @@ private: int x0, x1; int y0, y1, y2, y3; int lastCurrentWidth; - bool message; public: cSkinClassicDisplayReplay(bool ModeOnly); virtual ~cSkinClassicDisplayReplay(); @@ -397,7 +396,6 @@ cSkinClassicDisplayReplay::cSkinClassicDisplayReplay(bool ModeOnly) const cFont *font = cFont::GetFont(fontOsd); int lineHeight = font->Height(); lastCurrentWidth = 0; - message = false; x0 = 0; x1 = Setup.OSDWidth; y0 = 0; @@ -467,12 +465,9 @@ void cSkinClassicDisplayReplay::SetMessage(eMessageType Type, const char *Text) if (Text) { osd->SaveRegion(x0, y2, x1 - 1, y3 - 1); osd->DrawText(x0, y2, Text, Theme.Color(clrMessageStatusFg + 2 * Type), Theme.Color(clrMessageStatusBg + 2 * Type), font, x1 - x0, y3 - y2, taCenter); - message = true; } - else { + else osd->RestoreRegion(); - message = false; - } } void cSkinClassicDisplayReplay::Flush(void) @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: skins.c 1.6 2005/11/27 15:52:25 kls Exp $ + * $Id: skins.c 1.7 2006/01/08 11:40:18 kls Exp $ */ #include "skins.h" @@ -323,7 +323,7 @@ void cSkins::ProcessQueuedMessages(void) } else break; - } + } queueMessageMutex.Unlock(); } @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: skins.h 1.9 2005/11/27 15:41:44 kls Exp $ + * $Id: skins.h 1.10 2006/01/08 11:40:21 kls Exp $ */ #ifndef __SKINS_H @@ -197,7 +197,7 @@ public: ///< take care that it is erased from the display when a Current string ///< _with_ ".ff" is followed by one without it. virtual void SetTotal(const char *Total) = 0; - ///< Sets the total length of the recording, as a user readable + ///< Sets the total length of the recording, as a user readable ///< string if the form "h:mm:ss". virtual void SetJump(const char *Jump) = 0; ///< Sets the prompt that allows the user to enter a jump point. diff --git a/skinsttng.c b/skinsttng.c index e7f3d8b..1e2d8cd 100644 --- a/skinsttng.c +++ b/skinsttng.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: skinsttng.c 1.15 2005/08/15 11:14:59 kls Exp $ + * $Id: skinsttng.c 1.16 2006/01/01 14:38:14 kls Exp $ */ // Star Trek: The Next Generation® is a registered trademark of Paramount Pictures @@ -643,7 +643,6 @@ private: int y0, y1, y2, y3, y4, y5, y6, y7; tColor frameColor; int lastCurrentWidth; - bool message; public: cSkinSTTNGDisplayReplay(bool ModeOnly); virtual ~cSkinSTTNGDisplayReplay(); @@ -666,7 +665,6 @@ cSkinSTTNGDisplayReplay::cSkinSTTNGDisplayReplay(bool ModeOnly) int lineHeight = font->Height(); frameColor = Theme.Color(clrReplayFrame); lastCurrentWidth = 0; - message = false; cBitmap bm(play_xpm); x0 = 0; x1 = max(SymbolWidth, bm.Width()); @@ -772,12 +770,9 @@ void cSkinSTTNGDisplayReplay::SetMessage(eMessageType Type, const char *Text) osd->SaveRegion(x2, y6, x4 - 1, y7 - 1); osd->DrawRectangle(x2, y6, x3 - 1, y7 - 1, Theme.Color(clrBackground)); osd->DrawText(x3, y6, Text, Theme.Color(clrMessageStatusFg + 2 * Type), Theme.Color(clrMessageStatusBg + 2 * Type), font, x4 - x3, 0, taCenter); - message = true; } - else { + else osd->RestoreRegion(); - message = false; - } } void cSkinSTTNGDisplayReplay::Flush(void) @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: status.c 1.7 2005/01/09 11:51:04 kls Exp $ + * $Id: status.c 1.8 2005/12/31 15:10:10 kls Exp $ */ #include "status.h" @@ -29,16 +29,16 @@ void cStatus::MsgChannelSwitch(const cDevice *Device, int ChannelNumber) sm->ChannelSwitch(Device, ChannelNumber); } -void cStatus::MsgRecording(const cDevice *Device, const char *Name) +void cStatus::MsgRecording(const cDevice *Device, const char *Name, const char *FileName, bool On) { for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm)) - sm->Recording(Device, Name); + sm->Recording(Device, Name, FileName, On); } -void cStatus::MsgReplaying(const cControl *Control, const char *Name) +void cStatus::MsgReplaying(const cControl *Control, const char *Name, const char *FileName, bool On) { for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm)) - sm->Replaying(Control, Name); + sm->Replaying(Control, Name, FileName, On); } void cStatus::MsgSetVolume(int Volume, bool Absolute) @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: status.h 1.8 2005/01/09 11:50:21 kls Exp $ + * $Id: status.h 1.9 2005/12/31 15:15:25 kls Exp $ */ #ifndef __STATUS_H @@ -24,15 +24,17 @@ protected: // Indicates a channel switch on the given DVB device. // If ChannelNumber is 0, this is before the channel is being switched, // otherwise ChannelNumber is the number of the channel that has been switched to. - virtual void Recording(const cDevice *Device, const char *Name) {} - // The given DVB device has started recording Name. Name is the name of the - // recording, without any directory path. - // If Name is NULL, the recording has ended. - virtual void Replaying(const cControl *Control, const char *Name) {} - // The given player control has started replaying Name. Name is the name of the - // recording, without any directory path. In case of a player that can't provide + virtual void Recording(const cDevice *Device, const char *Name, const char *FileName, bool On) {} + // The given DVB device has started (On = true) or stopped (On = false) recording Name. + // Name is the name of the recording, without any directory path. The full file name + // of the recording is given in FileName, which may be NULL in case there is no + // actual file involved. If On is false, Name may be NULL. + virtual void Replaying(const cControl *Control, const char *Name, const char *FileName, bool On) {} + // The given player control has started (On = true) or stopped (On = false) replaying Name. + // Name is the name of the recording, without any directory path. In case of a player that can't provide // a name, Name can be a string that identifies the player type (like, e.g., "DVD"). - // If Name is NULL, the replay has ended. + // The full file name of the recording is given in FileName, which may be NULL in case there is no + // actual file involved. If On is false, Name may be NULL. virtual void SetVolume(int Volume, bool Absolute) {} // The volume has been set to the given value, either // absolutely or relative to the current volume. @@ -70,8 +72,8 @@ public: virtual ~cStatus(); // These functions are called whenever the related status information changes: static void MsgChannelSwitch(const cDevice *Device, int ChannelNumber); - static void MsgRecording(const cDevice *Device, const char *Name); - static void MsgReplaying(const cControl *Control, const char *Name); + static void MsgRecording(const cDevice *Device, const char *Name, const char *FileName, bool On); + static void MsgReplaying(const cControl *Control, const char *Name, const char *FileName, bool On); static void MsgSetVolume(int Volume, bool Absolute); static void MsgSetAudioTrack(int Index, const char * const *Tracks); static void MsgSetAudioChannel(int AudioChannel); @@ -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.84 2005/11/27 15:29:28 kls Exp $ + * $Id: svdrp.c 1.89 2005/12/30 15:42:29 kls Exp $ */ #include "svdrp.h" @@ -201,10 +201,16 @@ const char *HelpPages[] = { " Edit the recording with the given number. Before a recording can be\n" " edited, an LSTR command must have been executed in order to retrieve\n" " the recording numbers.", - "GRAB <filename> [ jpeg | pnm [ <quality> [ <sizex> <sizey> ] ] ]\n" + "GRAB <filename> [ <quality> [ <sizex> <sizey> ] ]\n" " Grab the current frame and save it to the given file. Images can\n" - " be stored as JPEG (default) or PNM, at the given quality (default\n" - " is 'maximum', only applies to JPEG) and size (default is full screen).", + " be stored as JPEG or PNM, depending on the given file name extension.\n" + " The quality of the grabbed image can be in the range 0..100, where 100\n" + " (the default) means \"best\" (only applies to JPEG). The size parameters\n" + " define the size of the resulting image (default is full screen).\n" + " If the file name is just an extension (.jpg, .jpeg or .pnm) the image\n" + " data will be sent to the SVDRP connection encoded in base64. The same\n" + " happens if '-' (a minus sign) is given as file name, in which case the\n" + " image format defaults to JPEG.", "HELP [ <topic> ]\n" " The HELP command gives help info.", "HITK [ <key> ]\n" @@ -307,6 +313,7 @@ const char *HelpPages[] = { 214 Help message 215 EPG or recording data record + 216 Image grab data (base 64) 220 VDR service ready 221 VDR service closing transmission channel 250 Requested VDR action okay, completed @@ -354,6 +361,8 @@ const char *GetHelpPage(const char *Cmd, const char **p) return NULL; } +char *cSVDRP::grabImageDir = NULL; + cSVDRP::cSVDRP(int Port) :socket(Port) { @@ -656,36 +665,54 @@ void cSVDRP::CmdGRAB(const char *Option) const char *delim = " \t"; char *strtok_next; FileName = strtok_r(p, delim, &strtok_next); - if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) { - if (strcasecmp(p, "JPEG") == 0) + // image type: + char *Extension = strrchr(FileName, '.'); + if (Extension) { + if (strcasecmp(Extension, ".jpg") == 0 || strcasecmp(Extension, ".jpeg") == 0) Jpeg = true; - else if (strcasecmp(p, "PNM") == 0) + else if (strcasecmp(Extension, ".pnm") == 0) Jpeg = false; else { - Reply(501, "Unknown image type \"%s\"", p); + Reply(501, "Unknown image type \"%s\"", Extension + 1); return; } + if (Extension == FileName) + FileName = NULL; + } + else if (strcmp(FileName, "-") == 0) + FileName = NULL; + else { + Reply(501, "Missing filename extension in \"%s\"", FileName); + return; } + // image quality (and obsolete type): if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) { - if (isnumber(p)) - Quality = atoi(p); - else { - Reply(501, "Illegal quality \"%s\"", p); - return; + if (strcasecmp(p, "JPEG") == 0 || strcasecmp(p, "PNM") == 0) { + // tolerate for backward compatibility + p = strtok_r(NULL, delim, &strtok_next); + } + if (p) { + if (isnumber(p)) + Quality = atoi(p); + else { + Reply(501, "Invalid quality \"%s\"", p); + return; + } } } + // image size: if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) { if (isnumber(p)) SizeX = atoi(p); else { - Reply(501, "Illegal sizex \"%s\"", p); + Reply(501, "Invalid sizex \"%s\"", p); return; } if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) { if (isnumber(p)) SizeY = atoi(p); else { - Reply(501, "Illegal sizey \"%s\"", p); + Reply(501, "Invalid sizey \"%s\"", p); return; } } @@ -698,8 +725,67 @@ void cSVDRP::CmdGRAB(const char *Option) Reply(501, "Unexpected parameter \"%s\"", p); return; } - if (cDevice::PrimaryDevice()->GrabImage(FileName, Jpeg, Quality, SizeX, SizeY)) - Reply(250, "Grabbed image %s", Option); + // canonicalize the file name: + char RealFileName[PATH_MAX]; + if (FileName) { + if (grabImageDir) { + char *s; + asprintf(&s, "%s/%s", grabImageDir, FileName); + FileName = s; + char *slash = strrchr(FileName, '/'); // there definitely is one + *slash = 0; + char *r = realpath(FileName, RealFileName); + *slash = '/'; + if (!r) { + LOG_ERROR_STR(FileName); + Reply(501, "Invalid file name \"%s\"", FileName); + free(s); + return; + } + strcat(RealFileName, slash); + FileName = RealFileName; + free(s); + if (strncmp(FileName, grabImageDir, strlen(grabImageDir)) != 0) { + Reply(501, "Invalid file name \"%s\"", FileName); + return; + } + } + else { + Reply(550, "Grabbing to file not allowed (use \"GRAB -\" instead)"); + return; + } + } + // actual grabbing: + int ImageSize; + uchar *Image = cDevice::PrimaryDevice()->GrabImage(ImageSize, Jpeg, Quality, SizeX, SizeY); + if (Image) { + if (FileName) { + int fd = open(FileName, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC, DEFFILEMODE); + if (fd >= 0) { + if (safe_write(fd, Image, ImageSize) == ImageSize) { + isyslog("grabbed image to %s", FileName); + Reply(250, "Grabbed image %s", Option); + } + else { + LOG_ERROR_STR(FileName); + Reply(451, "Can't write to '%s'", FileName); + } + close(fd); + } + else { + LOG_ERROR_STR(FileName); + Reply(451, "Can't open '%s'", FileName); + } + } + else { + cBase64Encoder Base64(Image, ImageSize); + const char *s; + while ((s = Base64.NextLine()) != NULL) + Reply(-216, s); + Reply(216, "Grabbed image %s", Option); + } + free(Image); + } else Reply(451, "Grab image failed"); } @@ -1482,4 +1568,10 @@ bool cSVDRP::Process(void) return false; } +void cSVDRP::SetGrabImageDir(const char *GrabImageDir) +{ + free(grabImageDir); + grabImageDir = GrabImageDir ? strdup(GrabImageDir) : NULL; +} + //TODO more than one connection??? @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: svdrp.h 1.26 2005/11/27 15:26:42 kls Exp $ + * $Id: svdrp.h 1.27 2005/12/30 14:46:38 kls Exp $ */ #ifndef __SVDRP_H @@ -49,6 +49,7 @@ private: int length; char *cmdLine; time_t lastActivity; + static char *grabImageDir; void Close(bool Timeout = false); bool Send(const char *s, int length = -1); void Reply(int Code, const char *fmt, ...) __attribute__ ((format (printf, 3, 4))); @@ -87,6 +88,7 @@ public: ~cSVDRP(); bool HasConnection(void) { return file.IsOpen(); } bool Process(void); + static void SetGrabImageDir(const char *GrabImageDir); }; #endif //__SVDRP_H @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: thread.c 1.46 2005/11/27 15:15:53 kls Exp $ + * $Id: thread.c 1.50 2006/01/04 15:01:22 kls Exp $ */ #include "thread.h" @@ -12,6 +12,7 @@ #include <malloc.h> #include <stdarg.h> #include <sys/resource.h> +#include <sys/syscall.h> #include <sys/time.h> #include <sys/wait.h> #include <unistd.h> @@ -136,7 +137,9 @@ void cCondVar::Broadcast(void) cRwLock::cRwLock(bool PreferWriter) { - pthread_rwlockattr_t attr = { PreferWriter ? PTHREAD_RWLOCK_PREFER_WRITER_NP : PTHREAD_RWLOCK_PREFER_READER_NP }; + pthread_rwlockattr_t attr; + pthread_rwlockattr_init(&attr); + pthread_rwlockattr_setkind_np(&attr, PreferWriter ? PTHREAD_RWLOCK_PREFER_WRITER_NP : PTHREAD_RWLOCK_PREFER_READER_NP); pthread_rwlock_init(&rwlock, &attr); } @@ -170,7 +173,9 @@ void cRwLock::Unlock(void) cMutex::cMutex(void) { locked = 0; - pthread_mutexattr_t attr = { PTHREAD_MUTEX_ERRORCHECK_NP }; + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP); pthread_mutex_init(&mutex, &attr); } @@ -193,13 +198,14 @@ void cMutex::Unlock(void) // --- cThread --------------------------------------------------------------- -tThreadId cThread::mainThreadId = cThread::ThreadId(); +tThreadId cThread::mainThreadId = 0; bool cThread::emergencyExitRequested = false; cThread::cThread(const char *Description) { active = running = false; childTid = 0; + childThreadId = 0; description = NULL; SetDescription(Description); } @@ -230,11 +236,12 @@ void cThread::SetDescription(const char *Description, ...) void *cThread::StartThread(cThread *Thread) { + Thread->childThreadId = ThreadId(); if (Thread->description) - dsyslog("%s thread started (pid=%d, tid=%ld)", Thread->description, getpid(), pthread_self()); + dsyslog("%s thread started (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId); Thread->Action(); if (Thread->description) - dsyslog("%s thread ended (pid=%d, tid=%ld)", Thread->description, getpid(), pthread_self()); + dsyslog("%s thread ended (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId); Thread->running = false; Thread->active = false; return NULL; @@ -292,7 +299,7 @@ void cThread::Cancel(int WaitSeconds) return; cCondWait::SleepMs(10); } - esyslog("ERROR: thread %ld won't end (waited %d seconds) - canceling it...", childTid, WaitSeconds); + esyslog("ERROR: thread %d won't end (waited %d seconds) - canceling it...", childThreadId, WaitSeconds); } pthread_cancel(childTid); childTid = 0; @@ -308,6 +315,21 @@ bool cThread::EmergencyExit(bool Request) return emergencyExitRequested = true; // yes, it's an assignment, not a comparison! } +_syscall0(pid_t, gettid) + +tThreadId cThread::ThreadId(void) +{ + return gettid(); +} + +void cThread::SetMainThreadId(void) +{ + if (mainThreadId == 0) + mainThreadId = ThreadId(); + else + esyslog("ERROR: attempt to set main thread id to %d while it already is %d", ThreadId(), mainThreadId); +} + // --- cMutexLock ------------------------------------------------------------ cMutexLock::cMutexLock(cMutex *Mutex) @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: thread.h 1.32 2005/11/27 15:16:50 kls Exp $ + * $Id: thread.h 1.36 2006/01/08 11:40:23 kls Exp $ */ #ifndef __THREAD_H @@ -72,7 +72,7 @@ public: void Unlock(void); }; -typedef pthread_t tThreadId; +typedef pid_t tThreadId; class cThread { friend class cThreadLock; @@ -80,6 +80,7 @@ private: bool active; bool running; pthread_t childTid; + tThreadId childThreadId; cMutex mutex; char *description; static tThreadId mainThreadId; @@ -106,7 +107,7 @@ public: cThread(const char *Description = NULL); ///< Creates a new thread. ///< If Description is present, a log file entry will be made when - ///< the thread starts and stops. The Start() function must be called + ///< the thread starts and stops. The Start() function must be called ///< to actually start the thread. virtual ~cThread(); void SetDescription(const char *Description, ...) __attribute__ ((format (printf, 2, 3))); @@ -115,8 +116,9 @@ public: bool Active(void); ///< Checks whether the thread is still alive. static bool EmergencyExit(bool Request = false); - static tThreadId ThreadId(void) { return pthread_self(); } + static tThreadId ThreadId(void); static tThreadId IsMainThread(void) { return ThreadId() == mainThreadId; } + static void SetMainThreadId(void); }; // cMutexLock can be used to easily set a lock on mutex and make absolutely @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: timers.c 1.36 2005/09/09 15:22:33 kls Exp $ + * $Id: timers.c 1.41 2006/01/08 11:40:29 kls Exp $ */ #include "timers.h" @@ -20,14 +20,14 @@ // -- cTimer ----------------------------------------------------------------- -cTimer::cTimer(bool Instant, bool Pause) +cTimer::cTimer(bool Instant, bool Pause, cChannel *Channel) { startTime = stopTime = 0; recording = pending = inVpsMargin = false; flags = tfNone; if (Instant) SetFlags(tfActive | tfInstant); - channel = Channels.GetByNumber(cDevice::CurrentChannel()); + channel = Channel ? Channel : Channels.GetByNumber(cDevice::CurrentChannel()); time_t t = time(NULL); struct tm tm_r; struct tm *now = localtime_r(&t, &tm_r); @@ -110,7 +110,7 @@ cString cTimer::ToText(bool UseChannelID) char *buffer; strreplace(file, ':', '|'); strreplace(summary, '\n', '|'); - asprintf(&buffer, "%d:%s:%s:%04d:%04d:%d:%d:%s:%s\n", flags, UseChannelID ? *Channel()->GetChannelID().ToString() : *itoa(Channel()->Number()), *PrintDay(day, weekdays), start, stop, priority, lifetime, file, summary ? summary : ""); + asprintf(&buffer, "%u:%s:%s:%04d:%04d:%d:%d:%s:%s\n", flags, UseChannelID ? *Channel()->GetChannelID().ToString() : *itoa(Channel()->Number()), *PrintDay(day, weekdays), start, stop, priority, lifetime, file, summary ? summary : ""); strreplace(summary, '|', '\n'); strreplace(file, '|', ':'); return cString(buffer, true); @@ -244,7 +244,7 @@ bool cTimer::Parse(const char *s) s = s2; } bool result = false; - if (8 <= sscanf(s, "%d :%a[^:]:%a[^:]:%d :%d :%d :%d :%a[^:\n]:%a[^\n]", &flags, &channelbuffer, &daybuffer, &start, &stop, &priority, &lifetime, &filebuffer, &summary)) { + if (8 <= sscanf(s, "%u :%a[^:]:%a[^:]:%d :%d :%d :%d :%a[^:\n]:%a[^\n]", &flags, &channelbuffer, &daybuffer, &start, &stop, &priority, &lifetime, &filebuffer, &summary)) { if (summary && !*skipspace(summary)) { free(summary); summary = NULL; @@ -363,7 +363,7 @@ bool cTimer::Matches(time_t t, bool Directly) const } if (HasFlags(tfActive)) { - if (HasFlags(tfVps) && !Directly && event && event->Vps() && schedule && schedule->PresentSeenWithin(30)) { + if (HasFlags(tfVps) && !Directly && event && event->Vps() && event->Schedule() && event->Schedule()->PresentSeenWithin(30)) { startTime = event->StartTime(); stopTime = event->EndTime(); return event->IsRunning(true); @@ -425,7 +425,7 @@ time_t cTimer::StopTime(void) const return stopTime; } -void cTimer::SetEvent(const cSchedule *Schedule, const cEvent *Event) +void cTimer::SetEvent(const cEvent *Event) { if (event != Event) { //XXX TODO check event data, too??? if (Event) { @@ -436,7 +436,6 @@ void cTimer::SetEvent(const cSchedule *Schedule, const cEvent *Event) } else isyslog("timer %s set to no event", *ToDescr()); - schedule = Event ? Schedule : NULL; event = Event; } } @@ -463,22 +462,27 @@ void cTimer::SetInVpsMargin(bool InVpsMargin) inVpsMargin = InVpsMargin; } -void cTimer::SetFlags(int Flags) +void cTimer::SetPriority(int Priority) +{ + priority = Priority; +} + +void cTimer::SetFlags(uint Flags) { flags |= Flags; } -void cTimer::ClrFlags(int Flags) +void cTimer::ClrFlags(uint Flags) { flags &= ~Flags; } -void cTimer::InvFlags(int Flags) +void cTimer::InvFlags(uint Flags) { flags ^= Flags; } -bool cTimer::HasFlags(int Flags) const +bool cTimer::HasFlags(uint Flags) const { return (flags & Flags) == Flags; } @@ -616,7 +620,7 @@ void cTimers::SetEvents(void) distance = e->EndTime() - now; if (Event && overlap == Overlap) { if (Overlap > FULLMATCH) { // this means VPS - if (abs(Distance) < abs(distance)) + if (abs(Distance) < abs(distance)) break; // we've already found the closest VPS event } else if (e->Duration() <= Event->Duration()) @@ -629,7 +633,7 @@ void cTimers::SetEvents(void) } if (Event && Event->EndTime() < now - EXPIRELATENCY && !Event->IsRunning()) Event = NULL; - ti->SetEvent(Schedule, Event); + ti->SetEvent(Event); } } } @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: timers.h 1.19 2005/05/07 10:36:35 kls Exp $ + * $Id: timers.h 1.23 2006/01/06 14:13:17 kls Exp $ */ #ifndef __TIMERS_H @@ -29,7 +29,7 @@ class cTimer : public cListObject { private: mutable time_t startTime, stopTime; bool recording, pending, inVpsMargin; - int flags; + uint flags; cChannel *channel; mutable time_t day; ///< midnight of the day this timer shall hit, or of the first day it shall hit in case of a repeating timer int weekdays; ///< bitmask, lowest bits: SSFTWTM (the 'M' is the LSB) @@ -39,10 +39,9 @@ private: int lifetime; char file[MaxFileName]; char *summary; - const cSchedule *schedule; const cEvent *event; public: - cTimer(bool Instant = false, bool Pause = false); + cTimer(bool Instant = false, bool Pause = false, cChannel *Channel = NULL); cTimer(const cEvent *Event); virtual ~cTimer(); cTimer& operator= (const cTimer &Timer); @@ -50,7 +49,7 @@ public: bool Recording(void) const { return recording; } bool Pending(void) const { return pending; } bool InVpsMargin(void) const { return inVpsMargin; } - int Flags(void) const { return flags; } + uint Flags(void) const { return flags; } const cChannel *Channel(void) const { return channel; } time_t Day(void) const { return day; } int WeekDays(void) const { return weekdays; } @@ -78,14 +77,15 @@ public: bool Expired(void) const; time_t StartTime(void) const; time_t StopTime(void) const; - void SetEvent(const cSchedule *Schedule, const cEvent *Event); + void SetEvent(const cEvent *Event); void SetRecording(bool Recording); void SetPending(bool Pending); void SetInVpsMargin(bool InVpsMargin); - void SetFlags(int Flags); - void ClrFlags(int Flags); - void InvFlags(int Flags); - bool HasFlags(int Flags) const; + void SetPriority(int Priority); + void SetFlags(uint Flags); + void ClrFlags(uint Flags); + void InvFlags(uint Flags); + bool HasFlags(uint Flags) const; void Skip(void); void OnOff(void); cString PrintFirstDay(void) const; @@ -4,13 +4,20 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: tools.c 1.104 2005/11/26 14:12:31 kls Exp $ + * $Id: tools.c 1.109 2006/01/08 11:40:35 kls Exp $ */ #include "tools.h" #include <ctype.h> #include <dirent.h> #include <errno.h> +extern "C" { +#ifdef boolean +#define HAVE_BOOLEAN +#endif +#include <jpeglib.h> +#undef boolean +} #include <stdarg.h> #include <stdlib.h> #include <sys/time.h> @@ -421,6 +428,42 @@ bool RemoveEmptyDirectories(const char *DirName, bool RemoveThis) return false; } +int DirSizeMB(const char *DirName) +{ + cReadDir d(DirName); + if (d.Ok()) { + int size = 0; + struct dirent *e; + while (size >= 0 && (e = d.Next()) != NULL) { + if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..")) { + char *buffer; + asprintf(&buffer, "%s/%s", DirName, e->d_name); + struct stat st; + if (stat(buffer, &st) == 0) { + if (S_ISDIR(st.st_mode)) { + int n = DirSizeMB(buffer); + if (n >= 0) + size += n; + else + size = -1; + } + else + size += st.st_size / MEGABYTE(1); + } + else { + LOG_ERROR_STR(buffer); + size = -1; + } + free(buffer); + } + } + return size; + } + else + LOG_ERROR_STR(DirName); + return -1; +} + char *ReadLink(const char *FileName) { char RealName[PATH_MAX]; @@ -615,6 +658,145 @@ cString TimeString(time_t t) return buf; } +// --- RgbToJpeg ------------------------------------------------------------- + +#define JPEGCOMPRESSMEM 500000 + +struct tJpegCompressData { + int size; + uchar *mem; + }; + +static void JpegCompressInitDestination(j_compress_ptr cinfo) +{ + tJpegCompressData *jcd = (tJpegCompressData *)cinfo->client_data; + if (jcd) { + cinfo->dest->free_in_buffer = jcd->size = JPEGCOMPRESSMEM; + cinfo->dest->next_output_byte = jcd->mem = MALLOC(uchar, jcd->size); + } +} + +static boolean JpegCompressEmptyOutputBuffer(j_compress_ptr cinfo) +{ + tJpegCompressData *jcd = (tJpegCompressData *)cinfo->client_data; + if (jcd) { + int Used = jcd->size; + jcd->size += JPEGCOMPRESSMEM; + jcd->mem = (uchar *)realloc(jcd->mem, jcd->size); + if (jcd->mem) { + cinfo->dest->next_output_byte = jcd->mem + Used; + cinfo->dest->free_in_buffer = jcd->size - Used; + return TRUE; + } + } + return FALSE; +} + +static void JpegCompressTermDestination(j_compress_ptr cinfo) +{ + tJpegCompressData *jcd = (tJpegCompressData *)cinfo->client_data; + if (jcd) { + int Used = cinfo->dest->next_output_byte - jcd->mem; + if (Used < jcd->size) { + jcd->size = Used; + jcd->mem = (uchar *)realloc(jcd->mem, jcd->size); + } + } +} + +uchar *RgbToJpeg(uchar *Mem, int Width, int Height, int &Size, int Quality) +{ + if (Quality < 0) + Quality = 0; + else if (Quality > 100) + Quality = 100; + + jpeg_destination_mgr jdm; + + jdm.init_destination = JpegCompressInitDestination; + jdm.empty_output_buffer = JpegCompressEmptyOutputBuffer; + jdm.term_destination = JpegCompressTermDestination; + + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + cinfo.dest = &jdm; + tJpegCompressData jcd; + cinfo.client_data = &jcd; + cinfo.image_width = Width; + cinfo.image_height = Height; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, Quality, true); + jpeg_start_compress(&cinfo, true); + + int rs = Width * 3; + JSAMPROW rp[Height]; + for (int k = 0; k < Height; k++) + rp[k] = &Mem[rs * k]; + jpeg_write_scanlines(&cinfo, rp, Height); + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + + Size = jcd.size; + return jcd.mem; +} + +// --- cBase64Encoder -------------------------------------------------------- + +const char *cBase64Encoder::b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +cBase64Encoder::cBase64Encoder(const uchar *Data, int Length, int MaxResult) +{ + data = Data; + length = Length; + maxResult = MaxResult; + i = 0; + result = MALLOC(char, maxResult + 1); +} + +cBase64Encoder::~cBase64Encoder() +{ + free(result); +} + +const char *cBase64Encoder::NextLine(void) +{ + int r = 0; + while (i < length && r < maxResult - 3) { + result[r++] = b64[(data[i] >> 2) & 0x3F]; + char c = (data[i] << 4) & 0x3F; + if (++i < length) + c |= (data[i] >> 4) & 0x0F; + result[r++] = b64[c]; + if (i < length) { + c = (data[i] << 2) & 0x3F; + if (++i < length) + c |= (data[i] >> 6) & 0x03; + result[r++] = b64[c]; + } + else { + i++; + result[r++] = '='; + } + if (i < length) { + c = data[i] & 0x3F; + result[r++] = b64[c]; + } + else + result[r++] = '='; + i++; + } + if (r > 0) { + result[r] = 0; + return result; + } + return NULL; +} + // --- cReadLine ------------------------------------------------------------- cReadLine::cReadLine(void) @@ -858,6 +1040,8 @@ bool cSafeFile::Close(void) // --- cUnbufferedFile ------------------------------------------------------- +//#define USE_FADVISE + #define READ_AHEAD MEGABYTE(2) #define WRITE_BUFFER MEGABYTE(10) @@ -882,6 +1066,7 @@ int cUnbufferedFile::Open(const char *FileName, int Flags, mode_t Mode) int cUnbufferedFile::Close(void) { +#ifdef USE_FADVISE if (fd >= 0) { if (ahead > end) end = ahead; @@ -894,6 +1079,7 @@ int cUnbufferedFile::Close(void) begin = end = ahead = -1; written = 0; } +#endif int OldFd = fd; fd = -1; return close(OldFd); @@ -909,6 +1095,7 @@ off_t cUnbufferedFile::Seek(off_t Offset, int Whence) ssize_t cUnbufferedFile::Read(void *Data, size_t Size) { if (fd >= 0) { +#ifdef USE_FADVISE off_t pos = lseek(fd, 0, SEEK_CUR); // jump forward - adjust end position if (pos > end) @@ -922,7 +1109,9 @@ ssize_t cUnbufferedFile::Read(void *Data, size_t Size) if (begin >= 0 && end > begin) posix_fadvise(fd, begin - KILOBYTE(200), end - begin + KILOBYTE(200), POSIX_FADV_DONTNEED);//XXX macros/parameters??? begin = pos; +#endif ssize_t bytesRead = safe_read(fd, Data, Size); +#ifdef USE_FADVISE if (bytesRead > 0) { pos += bytesRead; end = pos; @@ -935,6 +1124,7 @@ ssize_t cUnbufferedFile::Read(void *Data, size_t Size) } else end = pos; +#endif return bytesRead; } return -1; @@ -943,8 +1133,11 @@ ssize_t cUnbufferedFile::Read(void *Data, size_t Size) ssize_t cUnbufferedFile::Write(const void *Data, size_t Size) { if (fd >=0) { +#ifdef USE_FADVISE off_t pos = lseek(fd, 0, SEEK_CUR); +#endif ssize_t bytesWritten = safe_write(fd, Data, Size); +#ifdef USE_FADVISE if (bytesWritten >= 0) { written += bytesWritten; if (begin >= 0) { @@ -964,6 +1157,7 @@ ssize_t cUnbufferedFile::Write(const void *Data, size_t Size) written = 0; } } +#endif return bytesWritten; } return -1; @@ -1090,7 +1284,7 @@ int cListObject::Index(void) const // --- cListBase ------------------------------------------------------------- cListBase::cListBase(void) -{ +{ objects = lastObject = NULL; count = 0; } @@ -1101,7 +1295,7 @@ cListBase::~cListBase() } void cListBase::Add(cListObject *Object, cListObject *After) -{ +{ if (After && After != lastObject) { After->Next()->Insert(Object); After->Append(Object); @@ -1117,7 +1311,7 @@ void cListBase::Add(cListObject *Object, cListObject *After) } void cListBase::Ins(cListObject *Object, cListObject *Before) -{ +{ if (Before && Before != objects) { Before->Prev()->Append(Object); Before->Insert(Object); @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: tools.h 1.84 2005/11/26 14:03:47 kls Exp $ + * $Id: tools.h 1.89 2006/01/08 11:40:37 kls Exp $ */ #ifndef __TOOLS_H @@ -110,6 +110,7 @@ bool DirectoryOk(const char *DirName, bool LogErrors = false); bool MakeDirs(const char *FileName, bool IsDirectory = false); bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks = false); bool RemoveEmptyDirectories(const char *DirName, bool RemoveThis = false); +int DirSizeMB(const char *DirName); ///< returns the total size of the files in the given directory, or -1 in case of an error char *ReadLink(const char *FileName); ///< returns a new string allocated on the heap, which the caller must delete (or NULL in case of an error) bool SpinUpDisk(const char *FileName); void TouchFile(const char *FileName); @@ -120,6 +121,39 @@ cString DayDateTime(time_t t = 0); cString TimeToString(time_t t); cString DateString(time_t t); cString TimeString(time_t t); +uchar *RgbToJpeg(uchar *Mem, int Width, int Height, int &Size, int Quality = 100); + ///< Converts the given Memory to a JPEG image and returns a pointer + ///< to the resulting image. Mem must point to a data block of exactly + ///< (Width * Height) triplets of RGB image data bytes. Upon return, Size + ///< will hold the number of bytes of the resulting JPEG data. + ///< Quality can be in the range 0..100 and controls the quality of the + ///< resulting image, where 100 is "best". The caller takes ownership of + ///< the result and has to delete it once it is no longer needed. + ///< The result may be NULL in case of an error. + +class cBase64Encoder { +private: + const uchar *data; + int length; + int maxResult; + int i; + char *result; + static const char *b64; +public: + cBase64Encoder(const uchar *Data, int Length, int MaxResult = 64); + ///< Sets up a new base 64 encoder for the given Data, with the given Length. + ///< Data will not be copied and must be valid as long as NextLine() will be + ///< called. MaxResult defines the maximum number of characters in any + ///< result line. The resulting lines may be shorter than MaxResult in case + ///< its value is not a multiple of 4. + ~cBase64Encoder(); + const char *NextLine(void); + ///< Returns the next line of encoded data (terminated by '\0'), or NULL if + ///< there is no more encoded data. The caller must call NextLine() and process + ///< each returned line until NULL is returned, in order to get the entire + ///< data encoded. The returned data is only valid until the next time NextLine() + ///< is called, or until the object is destroyed. + }; class cTimeMs { private: @@ -152,7 +186,7 @@ public: bool Add(int FileHandle, bool Out); bool Poll(int TimeoutMs = 0); }; - + class cReadDir { private: DIR *directory; @@ -2,15 +2,15 @@ .\" ** The above line should force tbl to be a preprocessor ** .\" Man page for vdr .\" -.\" Copyright (C) 2004 Klaus Schmidinger +.\" Copyright (C) 2006 Klaus Schmidinger .\" .\" You may distribute under the terms of the GNU General Public .\" License as specified in the file COPYING that comes with the .\" vdr distribution. .\" -.\" $Id: vdr.1 1.15 2005/10/09 12:31:03 kls Exp $ +.\" $Id: vdr.1 1.20 2006/01/08 11:51:36 kls Exp $ .\" -.TH vdr 1 "19 Dec 2004" "1.3.18" "Video Disk Recorder" +.TH vdr 1 "08 Jan 2006" "1.3.38" "Video Disk Recorder" .SH NAME vdr - the Video Disk Recorder .SH SYNOPSIS @@ -59,6 +59,13 @@ Use \fB\-E\-\fR to disable this. If \fIfile\fR is a directory, the file \fIepg.data\fR will be created in that directory. .TP +.BI \-g,\ \-\-grab= dir +Write images from the SVDRP command GRAB into the +given directory \fIdir\fR. \fIdir\fR must be the full path name of an +existing directory, without any "..", double '/' +or symlinks. By default, or if \fB\-g\-\fR is given, +grabbing images to disk is disabled. +.TP .B \-h, \-\-help Print a help message and exit. .TP @@ -121,6 +128,13 @@ Call \fIcmd\fR to shutdown the computer. .BI \-t\ tty ,\ \-\-terminal= tty Set the controlling terminal. .TP +.BI \-u\ user ,\ \-\-user= user +Run as user \fIuser\fR in case vdr was started as user 'root'. +Starting vdr as 'root' is necessary if the system time shall +be set from the transponder data, but for security reasons +during normal operation vdr switches to a lesser privileged +user id. By default the user 'vdr' is used. +.TP .BI \-v\ dir ,\ \-\-video= dir Use \fIdir\fR as video directory. The default is \fI/video\fR. @@ -199,7 +213,7 @@ See the file \fICONTRIBUTORS\fR in the \fBvdr\fR source distribution. .SH REPORTING BUGS Report bugs to <vdr\-bugs@cadsoft.de>. .SH COPYRIGHT -Copyright \(co 2004 Klaus Schmidinger. +Copyright \(co 2006 Klaus Schmidinger. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. @@ -2,15 +2,15 @@ .\" ** The above line should force tbl to be a preprocessor ** .\" Man page for vdr file formats .\" -.\" Copyright (C) 2004 Klaus Schmidinger +.\" Copyright (C) 2006 Klaus Schmidinger .\" .\" You may distribute under the terms of the GNU General Public .\" License as specified in the file COPYING that comes with the .\" vdr distribution. .\" -.\" $Id: vdr.5 1.39 2005/09/26 21:38:44 kls Exp $ +.\" $Id: vdr.5 1.45 2006/01/08 11:52:03 kls Exp $ .\" -.TH vdr 5 "19 Mar 2005" "1.3.23" "Video Disk Recorder Files" +.TH vdr 5 "08 Jan 2006" "1.3.38" "Video Disk Recorder Files" .SH NAME vdr file formats - the Video Disk Recorder Files .SH DESCRIPTION @@ -152,7 +152,7 @@ tab (@); l l. \fB0000\fR@Free To Air \fB0001...000F\fR@explicitly requires the device with the given number -\fB0010...00FF\fR@reserved for user defined assignments defined in \fIca.conf\fR +\fB0010...00FF\fR@reserved for user defined assignments \fB0100...FFFF\fR@specific decryption methods as broadcast in the data stream\fR .TE Values in the range 0001...00FF will not be overwritten, all other values @@ -207,7 +207,7 @@ separated by ':' characters. Example: The fields in a timer definition have the following meaning (from left to right): .TP -.B Status +.B Flags The individual bits in this field have the following meaning: .TS tab (@); @@ -219,7 +219,7 @@ l l. .TE Bits other than these can be used by external programs to mark active timers and recognize if the user has modified them. When a user modifies an active -timer, the upper 16 bits of this 32 bit parameter will be explicitly set to 0. +timer, the upper 16 bits of this unsigned 32 bit parameter will be explicitly set to 0. Note: in order to allow future extensibility, external programs using the \fBstatus\fR parameter should only use the upper 16 bit of this 32 bit parameter @@ -227,7 +227,7 @@ and leave the lower 16 bit untouched. .TP .B Channel The channel to record from. This is either the channel number as shown in the -on-screen menus, or a complete channel ID. When reading \fItimers.conf\fR +on-screen menus, or a complete channel ID. When reading \fItimers.conf\fR any channel numbers will be mapped to the respective channel ids and when the file is written again, there will only be channel ids. Channel numbers are accepted as input in order to allow easier creation of timers when @@ -389,24 +389,6 @@ l l. There can be any number of actions in a line, including none at all - in which case the entry would be used only to set the LOF to use for the given frequency range and polarization. -.SS CONDITIONAL ACCESS -The file \fIca.conf\fR defines the numbers to be used in the \fBConditional access\fR -field of channels in \fIchannels.conf\fR and assigns descriptive texts to them. -Example: - -\fB101 Premiere World\fR - -Anything after (and including) a '#' character is comment. - -Value lines consist of an integer number, followed by a text describing -this decryption method (typically the name of the pay tv service using this -decryption method). - -The special value \fB0\fR means \fBFree To Air\fR, which can be used for -channels that don't require additional decryption hardware. - -The values \fB1...4\fR can be used for channels that for some reason explicitly -need a given DVB card (for backward compatibility). .SS REMOTE CONTROL KEYS The file \fIremote.conf\fR contains the key assignments for all remote control units. Each line consists of one key assignment in the following format: @@ -627,7 +609,7 @@ The following tag characters are defined: tab (@); l l. \fBC\fR@<channel id> <channel name> -\fBE\fR@<event id> <start time> <duration> <table id> +\fBE\fR@<event id> <start time> <duration> <table id> <version> \fBT\fR@<title> \fBS\fR@<short text> \fBD\fR@<description> @@ -654,6 +636,7 @@ l l. <start time> @is the time (as a time_t integer) in UTC when this event starts <duration> @is the time (in seconds) that this event will take <table id> @is a hex number that indicates the table this event is contained in (if this is left empty or 0 this event will not be overwritten or modified by data that comes from the DVB stream) +<version> @is a hex number that indicates the event's version number inside its table (optional) <title> @is the title of the event <short text> @is the short text of the event (typically the name of the episode etc.) <description> @is the description of the event (any '|' characters will be interpreted as newlines) @@ -673,7 +656,7 @@ Written by Klaus Schmidinger. .SH REPORTING BUGS Report bugs to <vdr\-bugs@cadsoft.de>. .SH COPYRIGHT -Copyright \(co 2004 Klaus Schmidinger. +Copyright \(co 2006 Klaus Schmidinger. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. @@ -1,7 +1,7 @@ /* * vdr.c: Video Disk Recorder main program * - * Copyright (C) 2000, 2003 Klaus Schmidinger + * Copyright (C) 2000, 2003, 2006 Klaus Schmidinger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -22,13 +22,17 @@ * * The project's page is at http://www.cadsoft.de/vdr * - * $Id: vdr.c 1.220 2005/11/27 15:56:18 kls Exp $ + * $Id: vdr.c 1.233 2006/01/08 11:49:03 kls Exp $ */ #include <getopt.h> +#include <grp.h> #include <locale.h> +#include <pwd.h> #include <signal.h> #include <stdlib.h> +#include <sys/capability.h> +#include <sys/prctl.h> #include <termios.h> #include <unistd.h> #include "audio.h" @@ -72,6 +76,57 @@ static int Interrupted = 0; +static bool SetUser(const char *UserName) +{ + if (UserName) { + struct passwd *user = getpwnam(UserName); + if (!user) { + fprintf(stderr, "vdr: unknown user: '%s'\n", UserName); + return false; + } + if (setgid(user->pw_gid) < 0) { + fprintf(stderr, "vdr: cannot set group id %u: %s\n", (unsigned int)user->pw_gid, strerror(errno)); + return false; + } + if (initgroups(user->pw_name, user->pw_gid) < 0) { + fprintf(stderr, "vdr: cannot set supplemental group ids for user %s: %s\n", user->pw_name, strerror(errno)); + return false; + } + if (setuid(user->pw_uid) < 0) { + fprintf(stderr, "vdr: cannot set user id %u: %s\n", (unsigned int)user->pw_uid, strerror(errno)); + return false; + } + } + return true; +} + +static bool SetCapSysTime(void) +{ + // drop all capabilities except cap_sys_time + cap_t caps = cap_from_text("= cap_sys_time=ep"); + if (!caps) { + fprintf(stderr, "vdr: cap_from_text failed: %s\n", strerror(errno)); + return false; + } + if (cap_set_proc(caps) == -1) { + fprintf(stderr, "vdr: cap_set_proc failed: %s\n", strerror(errno)); + cap_free(caps); + return false; + } + cap_free(caps); + return true; +} + +static bool SetKeepCaps(bool On) +{ + // set keeping capabilities during setuid() on/off + if (prctl(PR_SET_KEEPCAPS, On ? 1 : 0, 0, 0, 0) != 0) { + fprintf(stderr, "vdr: prctl failed\n"); + return false; + } + return true; +} + static void SignalHandler(int signum) { if (signum != SIGPIPE) { @@ -102,11 +157,14 @@ int main(int argc, char *argv[]) // Command line options: +#define DEFAULTVDRUSER "vdr" #define DEFAULTSVDRPPORT 2001 #define DEFAULTWATCHDOG 0 // seconds #define DEFAULTPLUGINDIR PLUGINDIR #define DEFAULTEPGDATAFILENAME "epg.data" + bool StartedAsRoot = false; + const char *VdrUser = DEFAULTVDRUSER; int SVDRPport = DEFAULTSVDRPPORT; const char *AudioCommand = NULL; const char *ConfigDirectory = NULL; @@ -144,6 +202,7 @@ int main(int argc, char *argv[]) { "daemon", no_argument, NULL, 'd' }, { "device", required_argument, NULL, 'D' }, { "epgfile", required_argument, NULL, 'E' }, + { "grab", required_argument, NULL, 'g' }, { "help", no_argument, NULL, 'h' }, { "lib", required_argument, NULL, 'L' }, { "lirc", optional_argument, NULL, 'l' | 0x100 }, @@ -156,6 +215,7 @@ int main(int argc, char *argv[]) { "record", required_argument, NULL, 'r' }, { "shutdown", required_argument, NULL, 's' }, { "terminal", required_argument, NULL, 't' }, + { "user", required_argument, NULL, 'u' }, { "version", no_argument, NULL, 'V' }, { "vfat", no_argument, NULL, 'v' | 0x100 }, { "video", required_argument, NULL, 'v' }, @@ -164,7 +224,7 @@ int main(int argc, char *argv[]) }; int c; - while ((c = getopt_long(argc, argv, "a:c:dD:E:hl:L:mp:P:r:s:t:v:Vw:", long_options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "a:c:dD:E:g:hl:L:mp:P:r:s:t:u:v:Vw:", long_options, NULL)) != -1) { switch (c) { case 'a': AudioCommand = optarg; break; @@ -183,6 +243,8 @@ int main(int argc, char *argv[]) break; case 'E': EpgDataFileName = (*optarg != '-' ? optarg : NULL); break; + case 'g': cSVDRP::SetGrabImageDir(*optarg != '-' ? optarg : NULL); + break; case 'h': DisplayHelp = true; break; case 'l': { @@ -248,6 +310,9 @@ int main(int argc, char *argv[]) return 2; } break; + case 'u': if (*optarg) + VdrUser = optarg; + break; case 'V': DisplayVersion = true; break; case 'v' | 0x100: @@ -270,6 +335,20 @@ int main(int argc, char *argv[]) } } + // Set user id in case we were started as root: + + if (getuid() == 0) { + StartedAsRoot = true; + if (!SetKeepCaps(true)) + return 2; + if (!SetUser(VdrUser)) + return 2; + if (!SetKeepCaps(false)) + return 2; + if (!SetCapSysTime()) + return 2; + } + // Help and version info: if (DisplayHelp || DisplayVersion) { @@ -285,11 +364,15 @@ int main(int argc, char *argv[]) " -D NUM, --device=NUM use only the given DVB device (NUM = 0, 1, 2...)\n" " there may be several -D options (default: all DVB\n" " devices will be used)\n" - " -E FILE --epgfile=FILE write the EPG data into the given FILE (default is\n" + " -E FILE, --epgfile=FILE write the EPG data into the given FILE (default is\n" " '%s' in the video directory)\n" " '-E-' disables this\n" " if FILE is a directory, the default EPG file will be\n" " created in that directory\n" + " -g DIR, --grab=DIR write images from the SVDRP command GRAB into the\n" + " given DIR; DIR must be the full path name of an\n" + " existing directory, without any \"..\", double '/'\n" + " or symlinks (default: none, same as -g-)\n" " -h, --help print this help and exit\n" " -l LEVEL, --log=LEVEL set log level (default: 3)\n" " 0 = no logging, 1 = errors only,\n" @@ -309,6 +392,8 @@ int main(int argc, char *argv[]) " -r CMD, --record=CMD call CMD before and after a recording\n" " -s CMD, --shutdown=CMD call CMD to shutdown the computer\n" " -t TTY, --terminal=TTY controlling tty\n" + " -u USER, --user=USER run as user USER (default: %s); only applicable\n" + " if started as root\n" " -v DIR, --video=DIR use DIR as video directory (default: %s)\n" " -V, --version print version information and exit\n" " --vfat encode special characters in recording names to\n" @@ -321,6 +406,7 @@ int main(int argc, char *argv[]) LIRC_DEVICE, DEFAULTSVDRPPORT, RCU_DEVICE, + DEFAULTVDRUSER, VideoDirectory, DEFAULTWATCHDOG ); @@ -371,7 +457,7 @@ int main(int argc, char *argv[]) if (DaemonMode) { if (daemon(1, 0) == -1) { - fprintf(stderr, "%m\n"); + fprintf(stderr, "vdr: %m\n"); esyslog("ERROR: %m"); return 2; } @@ -385,12 +471,16 @@ int main(int argc, char *argv[]) } isyslog("VDR version %s started", VDRVERSION); + if (StartedAsRoot) + isyslog("switched to user '%s'", VdrUser); + if (DaemonMode) + dsyslog("running as daemon (tid=%d)", cThread::ThreadId()); + cThread::SetMainThreadId(); // Main program loop variables - need to be here to have them initialized before any EXIT(): cOsdObject *Menu = NULL; - cOsdObject *Temp = NULL; - int LastChannel = -1; + int LastChannel = 0; int LastTimerChannel = -1; int PreviousChannel[2] = { 1, 1 }; int PreviousChannelIndex = 0; @@ -401,6 +491,7 @@ int main(int argc, char *argv[]) bool ForceShutdown = false; bool UserShutdown = false; bool TimerInVpsMargin = false; + bool IsInfoMenu = false; // Load plugins: @@ -423,7 +514,6 @@ int main(int argc, char *argv[]) Commands.Load(AddDirectory(ConfigDirectory, "commands.conf"), true) && RecordingCommands.Load(AddDirectory(ConfigDirectory, "reccmds.conf"), true) && SVDRPhosts.Load(AddDirectory(ConfigDirectory, "svdrphosts.conf"), true) && - CaDefinitions.Load(AddDirectory(ConfigDirectory, "ca.conf"), true) && Keys.Load(AddDirectory(ConfigDirectory, "remote.conf")) && KeyMacros.Load(AddDirectory(ConfigDirectory, "keymacros.conf"), true) )) @@ -434,6 +524,7 @@ int main(int argc, char *argv[]) // Recordings: Recordings.Update(); + DeletedRecordings.Update(); // EPG data: @@ -555,6 +646,8 @@ int main(int argc, char *argv[]) // Main program loop: +#define DELETE_MENU ((IsInfoMenu &= (Menu == NULL)), delete Menu, Menu = NULL) + while (!Interrupted) { // Handle emergency exits: if (cThread::EmergencyExit()) { @@ -576,8 +669,8 @@ int main(int argc, char *argv[]) && !(LastTimerChannel > 0 && Channels.SwitchTo(LastTimerChannel)) // ...or the one used by the last timer... && !cDevice::SwitchChannel(1) // ...or the next higher available one... && !cDevice::SwitchChannel(-1)) // ...or the next lower available one - ; - } + ; + } lastTime = time(NULL); // don't do this too often LastTimerChannel = -1; } @@ -626,7 +719,7 @@ int main(int argc, char *argv[]) // Channel display: if (!EITScanner.Active() && cDevice::CurrentChannel() != LastChannel) { if (!Menu) - Menu = Temp = new cDisplayChannel(cDevice::CurrentChannel(), LastChannel > 0); + Menu = new cDisplayChannel(cDevice::CurrentChannel(), LastChannel >= 0); LastChannel = cDevice::CurrentChannel(); LastChannelChanged = time(NULL); } @@ -667,8 +760,10 @@ int main(int argc, char *argv[]) TimerInVpsMargin = true; } } - if (!Menu && Recordings.NeedsUpdate()) + if (!Menu && Recordings.NeedsUpdate()) { Recordings.Update(); + DeletedRecordings.Update(); + } // CAM control: if (!Menu && !cOsd::IsOpen()) { Menu = CamControl(); @@ -692,22 +787,39 @@ int main(int argc, char *argv[]) // Menu control: case kMenu: key = kNone; // nobody else needs to see this key - if (Menu) { - DELETENULL(Menu); - if (!Temp) - break; - } - if (cControl::Control()) + if (Menu) + DELETE_MENU; + else if (cControl::Control() && cOsd::IsOpen()) cControl::Control()->Hide(); - Menu = new cMenuMain(cControl::Control()); - Temp = NULL; + else + Menu = new cMenuMain; + break; + // Info: + case kInfo: { + bool WasInfoMenu = IsInfoMenu; + DELETE_MENU; + if (!WasInfoMenu) { + IsInfoMenu = true; + if (cControl::Control()) { + cControl::Control()->Hide(); + Menu = cControl::Control()->GetInfo(); + if (Menu) + Menu->Show(); + else + IsInfoMenu = false; + } + else { + cRemote::Put(kOk, true); + cRemote::Put(kSchedule, true); + } + } + } break; #define DirectMainFunction(function)\ - DELETENULL(Menu);\ + DELETE_MENU;\ if (cControl::Control())\ cControl::Control()->Hide();\ - Menu = new cMenuMain(cControl::Control(), function);\ - Temp = NULL;\ + Menu = new cMenuMain(function);\ key = kNone; // nobody else needs to see this key case kSchedule: DirectMainFunction(osSchedule); break; case kChannels: DirectMainFunction(osChannels); break; @@ -717,18 +829,14 @@ int main(int argc, char *argv[]) case kCommands: DirectMainFunction(osCommands); break; case kUser1 ... kUser9: cRemote::PutMacro(key); key = kNone; break; case k_Plugin: { - DELETENULL(Menu); - Temp = NULL; + DELETE_MENU; if (cControl::Control()) cControl::Control()->Hide(); cPlugin *plugin = cPluginManager::GetPlugin(cRemote::GetPlugin()); if (plugin) { - Menu = Temp = plugin->MainMenuAction(); - if (Menu) { + Menu = plugin->MainMenuAction(); + if (Menu) Menu->Show(); - if (Menu->IsMenu()) - ((cOsdMenu*)Menu)->Display(); - } } else esyslog("ERROR: unknown plugin '%s'", cRemote::GetPlugin()); @@ -757,7 +865,7 @@ int main(int argc, char *argv[]) else cDevice::PrimaryDevice()->SetVolume(NORMALKEY(key) == kVolDn ? -VOLUMEDELTA : VOLUMEDELTA); if (!Menu && !cOsd::IsOpen()) - Menu = Temp = cDisplayVolume::Create(); + Menu = cDisplayVolume::Create(); cDisplayVolume::Process(key); key = kNone; // nobody else needs to see these keys break; @@ -765,12 +873,10 @@ int main(int argc, char *argv[]) case kAudio: if (cControl::Control()) cControl::Control()->Hide(); - if (Temp && !cDisplayTracks::IsOpen()) { - DELETENULL(Menu); - Temp = NULL; + if (!cDisplayTracks::IsOpen()) { + DELETE_MENU; + Menu = cDisplayTracks::Create(); } - if (!Menu && !cOsd::IsOpen()) - Menu = Temp = cDisplayTracks::Create(); else cDisplayTracks::Process(key); key = kNone; @@ -778,8 +884,7 @@ int main(int argc, char *argv[]) // Pausing live video: case kPause: if (!cControl::Control()) { - DELETENULL(Menu); - Temp = NULL; + DELETE_MENU; if (!cRecordControls::PauseLiveVideo()) Skins.Message(mtError, tr("No free DVB device to record!")); key = kNone; // nobody else needs to see this key @@ -797,8 +902,7 @@ int main(int argc, char *argv[]) break; // Power off: case kPower: isyslog("Power button pressed"); - DELETENULL(Menu); - Temp = NULL; + DELETE_MENU; if (!Shutdown) { Skins.Message(mtError, tr("Can't shutdown - option '-s' not given!")); break; @@ -828,53 +932,46 @@ int main(int argc, char *argv[]) state = osEnd; } switch (state) { - case osPause: DELETENULL(Menu); + case osPause: DELETE_MENU; cControl::Shutdown(); // just in case - Temp = NULL; if (!cRecordControls::PauseLiveVideo()) Skins.Message(mtError, tr("No free DVB device to record!")); break; - case osRecord: DELETENULL(Menu); - Temp = NULL; + case osRecord: DELETE_MENU; if (cRecordControls::Start()) - ;//XXX Skins.Message(mtInfo, tr("Recording")); + Skins.Message(mtInfo, tr("Recording started")); else Skins.Message(mtError, tr("No free DVB device to record!")); break; case osRecordings: - DELETENULL(Menu); + DELETE_MENU; cControl::Shutdown(); - Temp = NULL; - Menu = new cMenuMain(false, osRecordings); + Menu = new cMenuMain(osRecordings); break; - case osReplay: DELETENULL(Menu); + case osReplay: DELETE_MENU; cControl::Shutdown(); - Temp = NULL; cControl::Launch(new cReplayControl); break; case osStopReplay: - DELETENULL(Menu); + DELETE_MENU; cControl::Shutdown(); - Temp = NULL; break; case osSwitchDvb: - DELETENULL(Menu); + DELETE_MENU; cControl::Shutdown(); - Temp = NULL; Skins.Message(mtInfo, tr("Switching primary DVB...")); cDevice::SetPrimaryDevice(Setup.PrimaryDVB); break; - case osPlugin: DELETENULL(Menu); - Menu = Temp = cMenuMain::PluginOsdObject(); + case osPlugin: DELETE_MENU; + Menu = cMenuMain::PluginOsdObject(); if (Menu) Menu->Show(); break; case osBack: case osEnd: if (Interact == Menu) - DELETENULL(Menu); + DELETE_MENU; else cControl::Shutdown(); - Temp = NULL; break; default: ; } @@ -913,7 +1010,6 @@ int main(int argc, char *argv[]) case kPlay: if (cReplayControl::LastReplayed()) { cControl::Shutdown(); - Temp = NULL; cControl::Launch(new cReplayControl); } break; @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: videodir.c 1.13 2005/10/31 12:07:41 kls Exp $ + * $Id: videodir.c 1.14 2005/12/18 10:33:20 kls Exp $ */ #include "videodir.h" @@ -16,6 +16,7 @@ #include <string.h> #include <sys/stat.h> #include <unistd.h> +#include "recording.h" #include "tools.h" const char *VideoDirectory = VIDEODIR; @@ -185,12 +186,17 @@ bool VideoFileSpaceAvailable(int SizeMB) int VideoDiskSpace(int *FreeMB, int *UsedMB) { int free = 0, used = 0; + int deleted = DeletedRecordings.TotalFileSizeMB(); cVideoDirectory Dir; do { int u; free += Dir.FreeMB(&u); used += u; } while (Dir.Next()); + if (deleted > used) + deleted = used; // let's not get beyond 100% + free += deleted; + used -= deleted; if (FreeMB) *FreeMB = free; if (UsedMB) |