From a26aae3ce8d58e1c9b451d87ce4d1e8a7bca4690 Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Mon, 14 Sep 2015 16:24:00 +0200 Subject: [PATCH] Version 2.3.1 VDR developer version 2.3.1 is now available at MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ftp://ftp.tvdr.de/vdr/Developer/vdr-2.3.1.tar.bz2 A 'diff' against the previous version is available at ftp://ftp.tvdr.de/vdr/Developer/vdr-2.2.0-2.3.1.diff MD5 checksums: 391c2ed60e2f7d24563fe3ed5854bc4f vdr-2.3.1.tar.bz2 983fd4bad7d19cd98301d54173107129 vdr-2.2.0-2.3.1.diff WARNING: ======== This is a *developer* version. Even though *I* use it in my productive environment, I strongly recommend that you only use it under controlled conditions and for testing and debugging. *** PLEASE BE VERY CAREFUL WHEN USING THIS DEVELOPER VERSION, ESPECIALLY *** IF YOU ENABLE THE NEW SVDRP PEERING! KEEP BACKUPS OF ALL YOUR TIMERS *** AND OBSERVE VERY CLOSELY WHETHER EVERYTHING WORKS AS EXPECTED. THIS *** VERSION INTRODUCES SOME MAJOR CHANGES IN HANDLING GLOBAL LISTS AND *** LOCKING, SO ANYTHING CAN HAPPEN! YOU HAVE BEEN WARNED! The main focus of this developer version is on the new locking mechanism for global lists, and the ability to handle remote timers. Any plugins that access the global lists of timers, channels, schedules or recordings, will need to be adjusted (see below for details). Please do initial tests with plain vanilla VDR and just the output plugin you need. Known bugs/problems: - After deleting the last recording in a sub folder, the cursor may not be positioned correctly. - Instant recordings and pausing live video don't (yet) use the default SVDRP host for recording. From the HISTORY file: - The new function cOsd::MaxPixmapSize() can be called to determine the maximum size a cPixmap may have on the current OSD. The 'osddemo' example has been modified accordingly. Plugin authors may want to use this function in case they use pixmaps that are larger than the full OSD size. The default implementation sets this limit to 2048x2048 pixel. - The Setup/CAM menu now displays which device an individual CAM is currently assigned to (suggested by Frank Neumann). - Added detection of 24fps (thanks to Thomas Reufer). - Added a note about the VDR User Counter and VDR's facebook page to the README file. - The dvbhddevice plugin is no longer part of the VDR source archive. You can get the latest version of this plugin from the author's repository at https://bitbucket.org/powARman/dvbhddevice. - The dvbsddevice and rcu plugins are no longer part of the VDR source archive. You can get the latest versions of these plugins from ftp://ftp.tvdr.de/vdr/Plugins. - Added a section about Output Devices to the INSTALL file. - Fixed setting the source value of newly created channels, in case the NIT is received from a different, but very close satellite position (reported by Daniel Ribeiro). The code for handling different NITs has been removed from nit.c, because according to the DVB standard table id 0x40 carries only the NIT of the actual network. - Added some comment to cPixmap about the relation between OSD, ViewPort and DrawPort (suggested by Thomas Reufer). - Improved syncing on sections when parsing the NIT and SDT. - Fixed scaling subtitles (their areas could sometimes extend outside the actual OSD). - Reduced the priority of the "video directory scanner" thread (suggested by Thomas Reufer) and checking cIoThrottle::Engaged() when it is running. - The script that gets called for recordings is now also called right before a recording is edited, with the first parameter being "editing" (suggested by Dieter Ferdinand). - The new setup option "OSD/Default sort mode for recordings" can be used to define how recordings shall be sorted by default (either by time or by name, with "by time" being the default). If a particular sort mode has been selected for a folder by pressing '0', the default no longer applies to that folder. Repeating timers no longer write a ".sort" file into a recordings folder to have the recordings sorted by time. - The command line option -D now accepts the value '-' (as in -D-), which prevents VDR from using any DVB devices (suggested by Dietmar Spingler). - The -V and -h options now list the plugins in alphabetical order (suggested by Dietmar Spingler). - Fixed a compiler warning in font.c. - Commented out the line #define DEPRECATED_VIDEOSYSTEM in device.h. If a plugin doesn't compile with this version of VDR, you can uncomment this line as a quick workaround. In the long run the plugin will need to be adapted. - The function cOsd::GetBitmap() is now 'protected'. If a plugin doesn't compile with this version of VDR, you can uncomment the line //#define DEPRECATED_GETBITMAP in osd.h as a quick workaround. In the long run the plugin will need to be adapted. - The -u option now also accepts a numerical user id (suggested by Derek Kelly). - The SVDRP port now accepts multiple concurrent connections. You can now keep an SVDRP connection open as long as you wish, without preventing others from connecting. Note, though, that SVDRP connections still get closed automatically if there has been no activity for 300 seconds (configurable via "Setup/Miscellaneous/SVDRP timeout (s)"). - The SVDRP log messages have been unified and now always contain the IP and port number of the remote host. - SVDRP connections are now handled in a separate "SVDRP server handler" thread, which makes them more responsive. Note that there is only one thread that handles all concurrent SVDRP connections. That way each SVDRP command is guaranteed to be processed separately, without interfering with any other SVDRP commands that might be issued at the same time. Plugins that implement SVDRP commands may need to take care of proper locking if the commands access global data. - VDR now sends out a broadcast to port 6419/udp, which was assigned to 'svdrp-disc' by the IANA. VDRs listening on that port will automatically initiate an SVDRP connection to the broadcasting VDR, and in turn send out a broadcast to make other VDRs connect to them. That way all VDRs within the local network will have permanent "peer-to-peer" SVDRP connections between each other. The configuration in the svdrphosts.conf file is taken into account when considering whether or not to respond to an SVDRP discover broadcast. - The new SVDRP command PING is used by automatically established peer-to-peer connections to keep them alive. - The new function GetSVDRPServerNames() can be used to get a list of all VDRs this VDR is connected to via SVDRP. - The new function ExecSVDRPCommand() can be used to execute an SVDRP command on one of the servers this VDR is connected to, and retrieve the result. The helper functions SVDRPCode() and SVDRPValue() can be used to easily access the codes and values returned by ExecSVDRPCommand(). - The cTimer class now has a new member named 'remote', which holds the name of the remote server this timer will record on. If this is NULL, it is a local timer. - Timers from other VDRs that are connected to this VDR via SVDRP are now automatically fetched and stored in the global Timers list. In order for this to work, all of the channels used by timers on the remote VDR must also be defined on the local VDR (however, not necessarily in the same sequence). Automatic channel syncing will be implemented later. - The main menu of the LCARS skin now displays a small rectangle on the left side of a timer if this is a remote timer. The color of that rectangle changes if the timer is currently recording on the remote VDR. - Accessing the global Timers list now has to be protected by proper locking, because SVDRP commands are now executed in a separate thread. The introduction of this locking mechanism required the following changes: + The new classes cStateLock and cStateKey are used to implement locking with quick detection of state changes. + cConfig::cConfig() now has a parameter that indicates whether this list requires locking. + The global lists of Timers, Channels, Schedules and Recordings are no longer static variables. They are now pointers that need to be retrieved through a call to cTimers::GetTimersRead/Write(), cChannels::GetChannelsRead/Write(), cSchedules::GetSchedulesRead/Write() and cRecordings::GetRecordingsRead/Write(), respectively. + References from/to link channels are now removed in cChannels::Del() rather than cChannel::~cChannel(), to make sure the caller holds a proper lock. + cChannel::HasTimer() has been removed. This information is now retrieved via cSchedule::HasTimer(). + Several member functions of cChannel, cTimer, cMarks and cRecording have been made 'const', and some of them are now available as both 'const' and 'non-const' versions. + The cChannel::Set...() functions are now 'bool' and return true if they have actually changed any of the channels's members. + cChannels::SetModified() has been renamed to cChannels::SetModifiedByUser(). + cChannels::Modified() has been renamed to cChannels::ModifiedByUser(), and now has a 'State' parameter that allows the caller to see whether a channel has been modified since the last call to this function with the same State variable. + The macros CHANNELSMOD_NONE/_AUTO/_USER have been removed. + cMarks now requires locking via cStateKey. + cSortedTimers now requires a pointer to the list of timers. + cEvent::HasTimer() no longer scans the list of timers to check whether an event is referenced by a timer, but rather keeps score of how many timers reference it. This was necessary in order to avoid having to lock the list of timers from within a cEvent. + The new class cListGarbageCollector is used to temporary store any objects deleted from cLists that require locking. This allows pointers to such objects to be dereferenced even if the objects are no longer part of the list. + cListBase::Contains() can be used to check whether a particular object is still contained in that list. + Outdated events are no longer "phased out", but rather deleted right away and thus taken care of by the new "garbage collector" of the list. + Deleted cRecording objects are no longer kept in a list of "vanished" recordings, but are rather taken care of by the new "garbage collector" of the list. + cSchedules::ClearAll() has been removed. The functionality is now implemented directly in cSVDRPServer::CmdCLRE(). + tEventID has been changed to u_int16_t in order to make room for the new member numTimers in cEvent. + cSchedule now has a member Modified(), which can be used with a State variable to quickly determine whether this schedule has been modified since the last call to this function with the same State variable. + cSchedulesLock has been removed. Locking the list of schedules is now done via the cList's new locking mechanism. + The 'OnlyRunningStatus' parameters in cEpgHandler::BeginSegmentTransfer() and cEpgHandler::EndSegmentTransfer() are now obsolete. They are still present in the interface for backward compatibility, but may be removed in a future version. Their value is always 'false'. + The constant tcMod is no longer used in cStatus::TimerChange(). The definition is still there for backward compatibility. Plugins that access the global lists of Timers, Channels, Recordings or Schedules will need to be adapted as follows: + Instead of directly accessing the global variables Timers, Channels or Recordings, they need to set up a cStateKey variable and call the proper getter function, as in cStateKey StateKey; if (const cTimers *Timers = cTimers::GetTimersRead(StateKey)) { // access the timers StateKey.Remove(); } and cStateKey StateKey; if (cTimers *Timers = cTimers::GetTimersWrite(StateKey)) { // access the timers StateKey.Remove(); } See timers.h, thread.h and tools.h for details on this new locking mechanism. + There are convenience macros for easily accessing these lists without having to explicitly set up a cStateKey and calling its Remove() function. These macros have the form LOCK_*_READ/WRITE (with '*' being TIMERS, CHANNELS, SCHEDULES or RECORDINGS). Simply put such a macro before the point where you need to access the respective list, and there will be a pointer named Timers, Channels, Schedules or Recordings, respectively, which is valid until the end of the current block. + If a plugin needs to access several of the global lists in parallel, locking must always be done in the sequence Timers, Channels, Recordings, Schedules. This is necessary to make sure that different threads that need to lock several lists at the same time don't end up in a deadlock. + Some pointer variables may need to be made 'const'. The compiler will tell you about these. - cSectionSyncer has been improved to better handle missed sections. - Added a missing initialization of 'seen' in cChannel's copy constructor. - Background modifications of channels, timers and events are now displayed immediately in the corresponding menus. - cEIT now checks the version of the tables before doing any processing, which saves a lot of locking and processing. - If a timer is newly created with the Red button in the Schedule menu, and the timer is presented to the user in the "Edit timer" menu because it will start immediately, it now *must* be confirmed with "Ok" to set the timer. Otherwise the timer will not be created. - Recordings and deleted recordings are now scanned in a single thread. - The new SVDRP command POLL is used by automatically established peer-to-peer connections to trigger fetching remote timers. - You can now set DumpSVDRPDataTransfer in svdrp.c to true to have all SVDRP communication printed to the console for debugging. - Added a missing 'const' to cReceiver::Receive(), to protect the given Data from being modified. - The SVDRP commands that deal with timers (DELT, LSTT, MODT, NEWT, NEXT and UPDT) as well as any log messages that refer to timers, now use a unique id for each timer, which remains valid as long as this instance of VDR is running. This means that timers are no longer continuously numbered from 1 to N in LSTT. There may be gaps in the sequence, in case timers have been deleted. - The Timers menu now displays the name of the remote VDR in front of the timer's file name, if this is a remote timer. - The new options "Setup/Miscellaneous/SVDRP peering", ".../SVDRP host name" and ".../SVDRP default host" can be used to configure automatic peering between VDRs in the same network. Peering is disabled by default and can be enabled by setting "SVDRP peering" to "yes". - The function cTimer::ToText() no longer returns a newline character at the end of the string. The newline is now added by the caller as necessary. This was changed because cTimer::ToText() is now also needed in a context where the terminating newline can't be used. Consequently, cChannel::ToText() and cMark::ToText() have been modified accordingly. - All timer related response strings from SVDRP commands now use the channel ID instead of channel numbers. - The "Edit timer" menu now has a new parameter "Record on", which can be used to select the VDR on which this timer shall record. Timers can be freely moved between connected VDRs by simply selecting the desired machine in this field. - The SVDRP command DELT no longer checks whether the timer that shall be deleted is currently recording. - The character 0x0D is now stripped from EPG texts (reported by Janne Pänkälä). - The EPG scanner no longer moves the dish if there is a positioner. - The 'newplugin' script now creates the 'po' subdirectory for translations (thanks to Thomas Reufer). - Skins can now implement cSkinDisplayMenu::MenuOrientation() to display horizontal menus (thanks to Stefan Braun). - Fixed a possible stack overflow in cListBase::Sort() (thanks to Oliver Endriss). - Changed the description of the --chartab option in the INSTALL file to refer to "DVB SI table strings" instead of "EPG data". - The width and height of the OSD are now limited to the actual maximum dimensions of the output device, taking into account the top and left offset. - The new setup option "Recording/Record key handling" can be used to define what happens if the Record key on the remote control is pressed during live tv (suggested by Dietmar Spingler). - Empty adaptation field TS packets are now skipped when recording (thanks to Christopher Reimer, based on the "AFFcleaner" by Stefan Pöschel). --- CONTRIBUTORS | 28 + Doxyfile.filter | 2 +- HISTORY | 232 +++ INSTALL | 37 +- MANUAL | 29 +- Make.config.template | 2 +- Make.global | 2 +- Makefile | 2 +- PLUGINS.html | 18 +- PLUGINS/src/dvbhddevice/COPYING | 340 ---- PLUGINS/src/dvbhddevice/HISTORY | 108 -- PLUGINS/src/dvbhddevice/Makefile | 123 -- PLUGINS/src/dvbhddevice/README | 18 - PLUGINS/src/dvbhddevice/dvbhddevice.c | 97 - PLUGINS/src/dvbhddevice/dvbhdffdevice.c | 1104 ----------- PLUGINS/src/dvbhddevice/dvbhdffdevice.h | 130 -- PLUGINS/src/dvbhddevice/hdffcmd.c | 401 ---- PLUGINS/src/dvbhddevice/hdffcmd.h | 100 - PLUGINS/src/dvbhddevice/hdffosd.c | 836 -------- PLUGINS/src/dvbhddevice/hdffosd.h | 24 - PLUGINS/src/dvbhddevice/libhdffcmd/Makefile | 68 - .../src/dvbhddevice/libhdffcmd/bitbuffer.c | 79 - .../src/dvbhddevice/libhdffcmd/bitbuffer.h | 43 - PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd.h | 42 - .../src/dvbhddevice/libhdffcmd/hdffcmd_av.c | 506 ----- .../src/dvbhddevice/libhdffcmd/hdffcmd_av.h | 159 -- .../src/dvbhddevice/libhdffcmd/hdffcmd_base.c | 45 - .../src/dvbhddevice/libhdffcmd/hdffcmd_base.h | 55 - .../src/dvbhddevice/libhdffcmd/hdffcmd_defs.h | 125 -- .../dvbhddevice/libhdffcmd/hdffcmd_generic.c | 165 -- .../dvbhddevice/libhdffcmd/hdffcmd_generic.h | 36 - .../src/dvbhddevice/libhdffcmd/hdffcmd_hdmi.c | 120 -- .../src/dvbhddevice/libhdffcmd/hdffcmd_hdmi.h | 72 - .../src/dvbhddevice/libhdffcmd/hdffcmd_mux.c | 81 - .../src/dvbhddevice/libhdffcmd/hdffcmd_mux.h | 56 - .../src/dvbhddevice/libhdffcmd/hdffcmd_osd.c | 720 ------- .../src/dvbhddevice/libhdffcmd/hdffcmd_osd.h | 170 -- .../dvbhddevice/libhdffcmd/hdffcmd_remote.c | 67 - .../dvbhddevice/libhdffcmd/hdffcmd_remote.h | 39 - PLUGINS/src/dvbhddevice/menu.c | 65 - PLUGINS/src/dvbhddevice/menu.h | 29 - PLUGINS/src/dvbhddevice/po/de_DE.po | 128 -- PLUGINS/src/dvbhddevice/po/et_EE.po | 128 -- PLUGINS/src/dvbhddevice/po/fi_FI.po | 128 -- PLUGINS/src/dvbhddevice/po/it_IT.po | 131 -- PLUGINS/src/dvbhddevice/po/uk_UA.po | 129 -- PLUGINS/src/dvbhddevice/setup.c | 476 ----- PLUGINS/src/dvbhddevice/setup.h | 66 - PLUGINS/src/dvbsddevice/COPYING | 340 ---- PLUGINS/src/dvbsddevice/HISTORY | 65 - PLUGINS/src/dvbsddevice/Makefile | 94 - PLUGINS/src/dvbsddevice/README | 20 - PLUGINS/src/dvbsddevice/dvbsddevice.c | 61 - PLUGINS/src/dvbsddevice/dvbsdffdevice.c | 784 -------- PLUGINS/src/dvbsddevice/dvbsdffdevice.h | 115 -- PLUGINS/src/dvbsddevice/dvbsdffosd.c | 211 --- PLUGINS/src/dvbsddevice/dvbsdffosd.h | 22 - PLUGINS/src/epgtableid0/Makefile | 2 +- PLUGINS/src/epgtableid0/epgtableid0.c | 2 +- PLUGINS/src/hello/Makefile | 2 +- PLUGINS/src/hello/hello.c | 2 +- PLUGINS/src/osddemo/HISTORY | 7 + PLUGINS/src/osddemo/Makefile | 2 +- PLUGINS/src/osddemo/osddemo.c | 70 +- PLUGINS/src/pictures/HISTORY | 4 + PLUGINS/src/pictures/Makefile | 2 +- PLUGINS/src/pictures/entry.c | 2 +- PLUGINS/src/pictures/entry.h | 2 +- PLUGINS/src/pictures/menu.c | 4 +- PLUGINS/src/pictures/menu.h | 2 +- PLUGINS/src/pictures/pic2mpg | 2 +- PLUGINS/src/pictures/pictures.c | 4 +- PLUGINS/src/pictures/player.c | 2 +- PLUGINS/src/pictures/player.h | 2 +- PLUGINS/src/rcu/COPYING | 340 ---- PLUGINS/src/rcu/HISTORY | 30 - PLUGINS/src/rcu/Makefile | 94 - PLUGINS/src/rcu/README | 19 - PLUGINS/src/rcu/rcu.c | 420 ---- PLUGINS/src/servicedemo/Makefile | 2 +- PLUGINS/src/servicedemo/svccli.c | 2 +- PLUGINS/src/servicedemo/svcsvr.c | 2 +- PLUGINS/src/skincurses/Makefile | 2 +- PLUGINS/src/skincurses/skincurses.c | 2 +- PLUGINS/src/status/Makefile | 2 +- PLUGINS/src/status/status.c | 2 +- PLUGINS/src/svdrpdemo/Makefile | 2 +- PLUGINS/src/svdrpdemo/svdrpdemo.c | 2 +- README | 4 + args.c | 2 +- args.h | 2 +- audio.c | 2 +- audio.h | 2 +- channels.c | 351 ++-- channels.h | 114 +- ci.c | 17 +- ci.h | 2 +- config.c | 17 +- config.h | 21 +- cutter.c | 10 +- cutter.h | 2 +- device.c | 28 +- device.h | 4 +- diseqc.c | 2 +- diseqc.h | 2 +- dvbci.c | 2 +- dvbci.h | 2 +- dvbdevice.c | 7 +- dvbdevice.h | 3 +- dvbplayer.c | 27 +- dvbplayer.h | 4 +- dvbspu.c | 2 +- dvbspu.h | 2 +- dvbsubtitle.c | 5 +- dvbsubtitle.h | 4 +- eit.c | 119 +- eit.h | 10 +- eitscan.c | 22 +- eitscan.h | 2 +- epg.c | 272 ++- epg.h | 72 +- epg2html | 2 +- filter.c | 32 +- filter.h | 11 +- font.c | 4 +- font.h | 2 +- i18n.c | 2 +- i18n.h | 2 +- interface.c | 12 +- interface.h | 7 +- keys.c | 2 +- keys.h | 2 +- libsi/Makefile | 2 +- libsi/descriptor.c | 2 +- libsi/descriptor.h | 2 +- libsi/gendescr | 2 +- libsi/headers.h | 2 +- libsi/section.c | 2 +- libsi/section.h | 2 +- libsi/si.c | 2 +- libsi/si.h | 2 +- libsi/util.c | 2 +- libsi/util.h | 2 +- lirc.c | 2 +- lirc.h | 2 +- menu.c | 1491 +++++++++------ menu.h | 24 +- menuitems.c | 66 +- menuitems.h | 14 +- newplugin | 3 +- nit.c | 170 +- nit.h | 16 +- osd.c | 17 +- osd.h | 33 +- osdbase.c | 12 +- osdbase.h | 3 +- pat.c | 23 +- pat.h | 2 +- player.c | 2 +- player.h | 2 +- plugin.c | 36 +- plugin.h | 2 +- po/ar.po | 47 +- po/ca_ES.po | 47 +- po/cs_CZ.po | 47 +- po/da_DK.po | 47 +- po/de_DE.po | 47 +- po/el_GR.po | 47 +- po/es_ES.po | 47 +- po/et_EE.po | 47 +- po/fi_FI.po | 47 +- po/fr_FR.po | 47 +- po/hr_HR.po | 47 +- po/hu_HU.po | 47 +- po/it_IT.po | 47 +- po/lt_LT.po | 47 +- po/mk_MK.po | 47 +- po/nl_NL.po | 47 +- po/nn_NO.po | 47 +- po/pl_PL.po | 47 +- po/pt_PT.po | 47 +- po/ro_RO.po | 47 +- po/ru_RU.po | 47 +- po/sk_SK.po | 47 +- po/sl_SI.po | 47 +- po/sr_RS.po | 47 +- po/sv_SE.po | 47 +- po/tr_TR.po | 47 +- po/uk_UA.po | 47 +- po/zh_CN.po | 47 +- positioner.c | 2 +- positioner.h | 2 +- receiver.c | 2 +- receiver.h | 4 +- recorder.c | 29 +- recorder.h | 4 +- recording.c | 467 +++-- recording.h | 116 +- remote.c | 2 +- remote.h | 2 +- remux.c | 6 +- remux.h | 2 +- ringbuffer.c | 2 +- ringbuffer.h | 2 +- runvdr.template | 2 +- sdt.c | 61 +- sdt.h | 2 +- sections.c | 2 +- sections.h | 2 +- shutdown.c | 29 +- shutdown.h | 2 +- skinclassic.c | 2 +- skinclassic.h | 2 +- skinlcars.c | 67 +- skinlcars.h | 2 +- skins.c | 2 +- skins.h | 11 +- skinsttng.c | 2 +- skinsttng.h | 2 +- sourceparams.c | 4 +- sourceparams.h | 4 +- sources.c | 2 +- sources.h | 2 +- spu.c | 2 +- spu.h | 2 +- status.c | 2 +- status.h | 9 +- summary2info | 2 +- svdrp.c | 1687 ++++++++++++----- svdrp.h | 114 +- themes.c | 2 +- themes.h | 2 +- thread.c | 138 +- thread.h | 90 +- timers.c | 358 ++-- timers.h | 127 +- tools.c | 155 +- tools.h | 151 +- transfer.c | 4 +- transfer.h | 4 +- vdr.1 | 6 +- vdr.5 | 2 +- vdr.c | 344 ++-- videodir.c | 17 +- videodir.h | 3 +- 245 files changed, 6337 insertions(+), 12508 deletions(-) delete mode 100644 PLUGINS/src/dvbhddevice/COPYING delete mode 100644 PLUGINS/src/dvbhddevice/HISTORY delete mode 100644 PLUGINS/src/dvbhddevice/Makefile delete mode 100644 PLUGINS/src/dvbhddevice/README delete mode 100644 PLUGINS/src/dvbhddevice/dvbhddevice.c delete mode 100644 PLUGINS/src/dvbhddevice/dvbhdffdevice.c delete mode 100644 PLUGINS/src/dvbhddevice/dvbhdffdevice.h delete mode 100644 PLUGINS/src/dvbhddevice/hdffcmd.c delete mode 100644 PLUGINS/src/dvbhddevice/hdffcmd.h delete mode 100644 PLUGINS/src/dvbhddevice/hdffosd.c delete mode 100644 PLUGINS/src/dvbhddevice/hdffosd.h delete mode 100644 PLUGINS/src/dvbhddevice/libhdffcmd/Makefile delete mode 100644 PLUGINS/src/dvbhddevice/libhdffcmd/bitbuffer.c delete mode 100644 PLUGINS/src/dvbhddevice/libhdffcmd/bitbuffer.h delete mode 100644 PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd.h delete mode 100644 PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_av.c delete mode 100644 PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_av.h delete mode 100644 PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_base.c delete mode 100644 PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_base.h delete mode 100644 PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_defs.h delete mode 100644 PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_generic.c delete mode 100644 PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_generic.h delete mode 100644 PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_hdmi.c delete mode 100644 PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_hdmi.h delete mode 100644 PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_mux.c delete mode 100644 PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_mux.h delete mode 100644 PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_osd.c delete mode 100644 PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_osd.h delete mode 100644 PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_remote.c delete mode 100644 PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_remote.h delete mode 100644 PLUGINS/src/dvbhddevice/menu.c delete mode 100644 PLUGINS/src/dvbhddevice/menu.h delete mode 100644 PLUGINS/src/dvbhddevice/po/de_DE.po delete mode 100644 PLUGINS/src/dvbhddevice/po/et_EE.po delete mode 100644 PLUGINS/src/dvbhddevice/po/fi_FI.po delete mode 100644 PLUGINS/src/dvbhddevice/po/it_IT.po delete mode 100644 PLUGINS/src/dvbhddevice/po/uk_UA.po delete mode 100644 PLUGINS/src/dvbhddevice/setup.c delete mode 100644 PLUGINS/src/dvbhddevice/setup.h delete mode 100644 PLUGINS/src/dvbsddevice/COPYING delete mode 100644 PLUGINS/src/dvbsddevice/HISTORY delete mode 100644 PLUGINS/src/dvbsddevice/Makefile delete mode 100644 PLUGINS/src/dvbsddevice/README delete mode 100644 PLUGINS/src/dvbsddevice/dvbsddevice.c delete mode 100644 PLUGINS/src/dvbsddevice/dvbsdffdevice.c delete mode 100644 PLUGINS/src/dvbsddevice/dvbsdffdevice.h delete mode 100644 PLUGINS/src/dvbsddevice/dvbsdffosd.c delete mode 100644 PLUGINS/src/dvbsddevice/dvbsdffosd.h delete mode 100644 PLUGINS/src/rcu/COPYING delete mode 100644 PLUGINS/src/rcu/HISTORY delete mode 100644 PLUGINS/src/rcu/Makefile delete mode 100644 PLUGINS/src/rcu/README delete mode 100644 PLUGINS/src/rcu/rcu.c diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 6c52abf0..7f7a2d37 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -714,6 +714,7 @@ Oliver Endriss to detect the frame type for suggesting to ignore channels with an RID that is not 0 when checking for obsolete channels + for fixing a possible stack overflow in cListBase::Sort() Reinhard Walter Buchner for adding some satellites to 'sources.conf' @@ -2842,6 +2843,7 @@ Derek Kelly for reporting a missing template for DVBDIR in Make.config.template for suggesting to add ARGSDIR to the ONEDIR section of Make.config.template for suggesting to change the naming of "binary skip mode" to "adaptive skip mode" + for suggesting to make the -u option also accept a numerical user id Marcel Unbehaun for adding cRecordingInfo::GetEvent() @@ -2964,6 +2966,7 @@ Christopher Reimer for reporting a possible crash in the OSD demo for adding support for systemd for suggesting to replace VDR_CHARSET_OVERRIDE with a command line option + for making the recorder skip empty adaptation field TS packets Stefan Huskamp for suggesting to make entering characters via the number keys @@ -3003,6 +3006,8 @@ Frank Neumann for reporting a problem with tuning timeouts when using SCR with multiple tuners for fixing the German translation of "VDR will shut down in %s minutes" for adding support for "Satellite Channel Routing" (SCR) according to EN50607 ("JESS") + for suggesting to make the Setup/CAM menu display which device an individual CAM + is currently assigned to Gerald Dachs for reporting a problem with checking for minimum line length of 21 characters in @@ -3262,6 +3267,7 @@ Stefan Braun for suggesting to add the menu category mcRecordingEdit for marking menus that edit recording properties for suggesting to make cRecording::GetResume() public + for implementing the possibility for skins to display horizontal menus Jochen Dolze for changing cThread::SetIOPriority() from "best effort class" to "idle class" in order @@ -3311,6 +3317,11 @@ Thomas Reufer skipping" for suggesting to change the return value of cOsd::RenderPixmaps() from cPixmapMemory to cPixmap + for adding detection of 24fps + for suggesting to add some comment to cPixmap about the relation between OSD, + ViewPort and DrawPort + for suggesting to reduce the priority of the "video directory scanner" thread + for making the 'newplugin' script create the 'po' subdirectory for translations Eike Sauer for reporting a problem with channels that need more than 5 TS packets for detecting @@ -3345,6 +3356,9 @@ Dietmar Spingler for reporting a problem that led to a fix with EMM pids not being properly reset for CAMs that need to receive the TS for suggesting to add the channel name to log messages that reference a channel + for suggesting to provide a way of using no DVB devices at all + for suggesting that the -V and -h options should list the plugins in alphabetical order + for suggesting to implement the setup option "Recording/Record key handling" Stefan Schallenberg for adding the functions IndexOf(), InsertUnique(), AppendUnique() and RemoveElement() @@ -3359,6 +3373,8 @@ Dieter Ferdinand case replay was paused for reporting a problem with the system getting unresponsive when removing a huge number of files in the thread that removes deleted recordings + for suggesting to call the script that gets called for recordings also right before a + recording is edited Jasmin Jessich for modifying the CAM API so that it is possible to implement CAMs that can be freely @@ -3400,3 +3416,15 @@ Tomasz Maciej Nowak Gabriel Bonich for translating OSD texts to the Spanish language + +Daniel Ribeiro + for reporting a problem with setting the source value of newly created channels, in + case the NIT is received from a different, but very close satellite position, and + for helping to debug this + +Janne Pnkl + for reporting that some broadcasters use the character 0x0D in EPG texts + +Stefan Pschel + for coding the AFFcleaner, parts of which were used to make the recorder skip empty + adaptation field TS packets diff --git a/Doxyfile.filter b/Doxyfile.filter index 59ba7837..9948730d 100755 --- a/Doxyfile.filter +++ b/Doxyfile.filter @@ -9,7 +9,7 @@ # See the main source file 'vdr.c' for copyright information and # how to reach the author. # -# $Id: Doxyfile.filter 3.0 2013/02/17 10:54:05 kls Exp $ +# $Id: Doxyfile.filter 4.0 2013/02/17 10:54:05 kls Exp $ $TAG = "///<"; diff --git a/HISTORY b/HISTORY index 20e14735..7e8fd5c6 100644 --- a/HISTORY +++ b/HISTORY @@ -8595,3 +8595,235 @@ Video Disk Recorder Revision History - Fixed leading/trailing/multiple blanks in the translation files. - Bumped all version numbers to 2.2.0. - Official release. + +2015-09-14: Version 2.3.1 + +- The new function cOsd::MaxPixmapSize() can be called to determine the maximum size + a cPixmap may have on the current OSD. The 'osddemo' example has been modified + accordingly. Plugin authors may want to use this function in case they use pixmaps + that are larger than the full OSD size. The default implementation sets this limit + to 2048x2048 pixel. +- The Setup/CAM menu now displays which device an individual CAM is currently + assigned to (suggested by Frank Neumann). +- Added detection of 24fps (thanks to Thomas Reufer). +- Added a note about the VDR User Counter and VDR's facebook page to the README file. +- The dvbhddevice plugin is no longer part of the VDR source archive. + You can get the latest version of this plugin from the author's repository at + https://bitbucket.org/powARman/dvbhddevice. +- The dvbsddevice and rcu plugins are no longer part of the VDR source archive. + You can get the latest versions of these plugins from ftp://ftp.tvdr.de/vdr/Plugins. +- Added a section about Output Devices to the INSTALL file. +- Fixed setting the source value of newly created channels, in case the NIT is + received from a different, but very close satellite position (reported by Daniel + Ribeiro). The code for handling different NITs has been removed from nit.c, because + according to the DVB standard table id 0x40 carries only the NIT of the actual + network. +- Added some comment to cPixmap about the relation between OSD, ViewPort and DrawPort + (suggested by Thomas Reufer). +- Improved syncing on sections when parsing the NIT and SDT. +- Fixed scaling subtitles (their areas could sometimes extend outside the actual OSD). +- Reduced the priority of the "video directory scanner" thread (suggested by Thomas + Reufer) and checking cIoThrottle::Engaged() when it is running. +- The script that gets called for recordings is now also called right before a + recording is edited, with the first parameter being "editing" (suggested by + Dieter Ferdinand). +- The new setup option "OSD/Default sort mode for recordings" can be used to define + how recordings shall be sorted by default (either by time or by name, with "by time" + being the default). If a particular sort mode has been selected for a folder by + pressing '0', the default no longer applies to that folder. Repeating timers no + longer write a ".sort" file into a recordings folder to have the recordings sorted + by time. +- The command line option -D now accepts the value '-' (as in -D-), which prevents + VDR from using any DVB devices (suggested by Dietmar Spingler). +- The -V and -h options now list the plugins in alphabetical order (suggested by + Dietmar Spingler). +- Fixed a compiler warning in font.c. +- Commented out the line + #define DEPRECATED_VIDEOSYSTEM + in device.h. If a plugin doesn't compile with this version of VDR, you can uncomment + this line as a quick workaround. In the long run the plugin will need to be adapted. +- The function cOsd::GetBitmap() is now 'protected'. If a plugin doesn't compile with + this version of VDR, you can uncomment the line + //#define DEPRECATED_GETBITMAP + in osd.h as a quick workaround. In the long run the plugin will need to be adapted. +- The -u option now also accepts a numerical user id (suggested by Derek Kelly). +- The SVDRP port now accepts multiple concurrent connections. You can now keep an + SVDRP connection open as long as you wish, without preventing others from + connecting. Note, though, that SVDRP connections still get closed automatically + if there has been no activity for 300 seconds (configurable via + "Setup/Miscellaneous/SVDRP timeout (s)"). +- The SVDRP log messages have been unified and now always contain the IP and port + number of the remote host. +- SVDRP connections are now handled in a separate "SVDRP server handler" thread, + which makes them more responsive. Note that there is only one thread that handles + all concurrent SVDRP connections. That way each SVDRP command is guaranteed to be + processed separately, without interfering with any other SVDRP commands that might + be issued at the same time. Plugins that implement SVDRP commands may need to take + care of proper locking if the commands access global data. +- VDR now sends out a broadcast to port 6419/udp, which was assigned to 'svdrp-disc' + by the IANA. VDRs listening on that port will automatically initiate an SVDRP + connection to the broadcasting VDR, and in turn send out a broadcast to make + other VDRs connect to them. That way all VDRs within the local network will + have permanent "peer-to-peer" SVDRP connections between each other. The + configuration in the svdrphosts.conf file is taken into account when considering + whether or not to respond to an SVDRP discover broadcast. +- The new SVDRP command PING is used by automatically established peer-to-peer + connections to keep them alive. +- The new function GetSVDRPServerNames() can be used to get a list of all VDRs + this VDR is connected to via SVDRP. +- The new function ExecSVDRPCommand() can be used to execute an SVDRP command on + one of the servers this VDR is connected to, and retrieve the result. + The helper functions SVDRPCode() and SVDRPValue() can be used to easily access + the codes and values returned by ExecSVDRPCommand(). +- The cTimer class now has a new member named 'remote', which holds the name of the + remote server this timer will record on. If this is NULL, it is a local timer. +- Timers from other VDRs that are connected to this VDR via SVDRP are now + automatically fetched and stored in the global Timers list. In order for this + to work, all of the channels used by timers on the remote VDR must also be + defined on the local VDR (however, not necessarily in the same sequence). + Automatic channel syncing will be implemented later. +- The main menu of the LCARS skin now displays a small rectangle on the left side + of a timer if this is a remote timer. The color of that rectangle changes if + the timer is currently recording on the remote VDR. +- Accessing the global Timers list now has to be protected by proper locking, + because SVDRP commands are now executed in a separate thread. + The introduction of this locking mechanism required the following changes: + + The new classes cStateLock and cStateKey are used to implement locking + with quick detection of state changes. + + cConfig::cConfig() now has a parameter that indicates whether this list + requires locking. + + The global lists of Timers, Channels, Schedules and Recordings are no longer + static variables. They are now pointers that need to be retrieved through + a call to cTimers::GetTimersRead/Write(), cChannels::GetChannelsRead/Write(), + cSchedules::GetSchedulesRead/Write() and cRecordings::GetRecordingsRead/Write(), + respectively. + + References from/to link channels are now removed in cChannels::Del() rather + than cChannel::~cChannel(), to make sure the caller holds a proper lock. + + cChannel::HasTimer() has been removed. This information is now retrieved + via cSchedule::HasTimer(). + + Several member functions of cChannel, cTimer, cMarks and cRecording have + been made 'const', and some of them are now available as both 'const' and + 'non-const' versions. + + The cChannel::Set...() functions are now 'bool' and return true if they have + actually changed any of the channels's members. + + cChannels::SetModified() has been renamed to cChannels::SetModifiedByUser(). + + cChannels::Modified() has been renamed to cChannels::ModifiedByUser(), and + now has a 'State' parameter that allows the caller to see whether a channel + has been modified since the last call to this function with the same State + variable. + + The macros CHANNELSMOD_NONE/_AUTO/_USER have been removed. + + cMarks now requires locking via cStateKey. + + cSortedTimers now requires a pointer to the list of timers. + + cEvent::HasTimer() no longer scans the list of timers to check whether an event + is referenced by a timer, but rather keeps score of how many timers reference + it. This was necessary in order to avoid having to lock the list of timers from + within a cEvent. + + The new class cListGarbageCollector is used to temporary store any objects deleted + from cLists that require locking. This allows pointers to such objects to be + dereferenced even if the objects are no longer part of the list. + + cListBase::Contains() can be used to check whether a particular object is still + contained in that list. + + Outdated events are no longer "phased out", but rather deleted right away and thus + taken care of by the new "garbage collector" of the list. + + Deleted cRecording objects are no longer kept in a list of "vanished" recordings, + but are rather taken care of by the new "garbage collector" of the list. + + cSchedules::ClearAll() has been removed. The functionality is now implemented + directly in cSVDRPServer::CmdCLRE(). + + tEventID has been changed to u_int16_t in order to make room for the new member + numTimers in cEvent. + + cSchedule now has a member Modified(), which can be used with a State variable + to quickly determine whether this schedule has been modified since the last call + to this function with the same State variable. + + cSchedulesLock has been removed. Locking the list of schedules is now done via + the cList's new locking mechanism. + + The 'OnlyRunningStatus' parameters in cEpgHandler::BeginSegmentTransfer() and + cEpgHandler::EndSegmentTransfer() are now obsolete. They are still present in + the interface for backward compatibility, but may be removed in a future version. + Their value is always 'false'. + + The constant tcMod is no longer used in cStatus::TimerChange(). The definition is + still there for backward compatibility. + Plugins that access the global lists of Timers, Channels, Recordings or Schedules + will need to be adapted as follows: + + Instead of directly accessing the global variables Timers, Channels or Recordings, + they need to set up a cStateKey variable and call the proper getter function, + as in + cStateKey StateKey; + if (const cTimers *Timers = cTimers::GetTimersRead(StateKey)) { + // access the timers + StateKey.Remove(); + } + and + cStateKey StateKey; + if (cTimers *Timers = cTimers::GetTimersWrite(StateKey)) { + // access the timers + StateKey.Remove(); + } + See timers.h, thread.h and tools.h for details on this new locking mechanism. + + There are convenience macros for easily accessing these lists without having + to explicitly set up a cStateKey and calling its Remove() function. These macros + have the form LOCK_*_READ/WRITE (with '*' being TIMERS, CHANNELS, SCHEDULES or + RECORDINGS). Simply put such a macro before the point where you need to access + the respective list, and there will be a pointer named Timers, Channels, Schedules + or Recordings, respectively, which is valid until the end of the current block. + + If a plugin needs to access several of the global lists in parallel, locking must + always be done in the sequence Timers, Channels, Recordings, Schedules. This is + necessary to make sure that different threads that need to lock several lists at + the same time don't end up in a deadlock. + + Some pointer variables may need to be made 'const'. The compiler will tell you + about these. +- cSectionSyncer has been improved to better handle missed sections. +- Added a missing initialization of 'seen' in cChannel's copy constructor. +- Background modifications of channels, timers and events are now displayed immediately + in the corresponding menus. +- cEIT now checks the version of the tables before doing any processing, which saves + a lot of locking and processing. +- If a timer is newly created with the Red button in the Schedule menu, and the timer + is presented to the user in the "Edit timer" menu because it will start immediately, + it now *must* be confirmed with "Ok" to set the timer. Otherwise the timer will not + be created. +- Recordings and deleted recordings are now scanned in a single thread. +- The new SVDRP command POLL is used by automatically established peer-to-peer + connections to trigger fetching remote timers. +- You can now set DumpSVDRPDataTransfer in svdrp.c to true to have all SVDRP + communication printed to the console for debugging. +- Added a missing 'const' to cReceiver::Receive(), to protect the given Data from + being modified. +- The SVDRP commands that deal with timers (DELT, LSTT, MODT, NEWT, NEXT and UPDT) + as well as any log messages that refer to timers, now use a unique id for each + timer, which remains valid as long as this instance of VDR is running. This means + that timers are no longer continuously numbered from 1 to N in LSTT. There may be + gaps in the sequence, in case timers have been deleted. +- The Timers menu now displays the name of the remote VDR in front of the timer's + file name, if this is a remote timer. +- The new options "Setup/Miscellaneous/SVDRP peering", ".../SVDRP host name" and + ".../SVDRP default host" can be used to configure automatic peering between VDRs + in the same network. Peering is disabled by default and can be enabled by setting + "SVDRP peering" to "yes". +- The function cTimer::ToText() no longer returns a newline character at the end of + the string. The newline is now added by the caller as necessary. This was changed + because cTimer::ToText() is now also needed in a context where the terminating + newline can't be used. Consequently, cChannel::ToText() and cMark::ToText() have + been modified accordingly. +- All timer related response strings from SVDRP commands now use the channel ID + instead of channel numbers. +- The "Edit timer" menu now has a new parameter "Record on", which can be used to + select the VDR on which this timer shall record. Timers can be freely moved + between connected VDRs by simply selecting the desired machine in this field. +- The SVDRP command DELT no longer checks whether the timer that shall be deleted + is currently recording. +- The character 0x0D is now stripped from EPG texts (reported by Janne Pnkl). +- The EPG scanner no longer moves the dish if there is a positioner. +- The 'newplugin' script now creates the 'po' subdirectory for translations (thanks + to Thomas Reufer). +- Skins can now implement cSkinDisplayMenu::MenuOrientation() to display horizontal + menus (thanks to Stefan Braun). +- Fixed a possible stack overflow in cListBase::Sort() (thanks to Oliver Endriss). +- Changed the description of the --chartab option in the INSTALL file to refer to + "DVB SI table strings" instead of "EPG data". +- The width and height of the OSD are now limited to the actual maximum dimensions + of the output device, taking into account the top and left offset. +- The new setup option "Recording/Record key handling" can be used to define + what happens if the Record key on the remote control is pressed during + live tv (suggested by Dietmar Spingler). +- Empty adaptation field TS packets are now skipped when recording (thanks to + Christopher Reimer, based on the "AFFcleaner" by Stefan Pschel). diff --git a/INSTALL b/INSTALL index 864cfb1c..f2218d12 100644 --- a/INSTALL +++ b/INSTALL @@ -94,6 +94,24 @@ vdr:123:respawn:/usr/local/bin/vdr --terminal=/dev/tty8 -w 60 See the man page vdr(1) for complete information about all command line options. +Output devices +-------------- + +VDR by itself doesn't produce any audio or video output. In order to watch +live tv or recordings, you will need to use a plugin that supports the actual +hardware in your system, for instance: + +Plugin: Device: + +dvbsddevice Full-Featured SD DVB cards (Fujitsu-Siemens Design) + (comes with the VDR source) +dvbhddevice Full-featured HD DVB cards (Technotrend TT S2-6400) + https://bitbucket.org/powARman/dvbhddevice +rpihddevice Raspberry Pi + http://projects.vdr-developer.org/git/vdr-plugin-rpihddevice.git + +See http://linuxtv.org/vdrwiki/index.php/Output_devices for more. + Standard compliance ------------------- @@ -134,10 +152,10 @@ displayed correctly. Since BiDi support adds some runtime overhead by requiring additional memory allocation and copying, this feature is not compiled in by default, so that users that have no need for this don't get any overhead. -Workaround for providers not encoding their EPG data correctly --------------------------------------------------------------- +Workaround for providers not encoding their DVB SI table strings correctly +-------------------------------------------------------------------------- -According to "ETSI EN 300 468" the default character set fo SI data is +According to "ETSI EN 300 468" the default character set for SI data is ISO6937. But unfortunately some broadcasters actually use ISO-8859-9 or other encodings, but fail to correctly announce that. Users who want to set the default character set to something different can @@ -250,19 +268,20 @@ You can use the '-r' option to define a program or script that gets called before and after a recording is performed, and after an editing process has finished or a recording has been deleted. -The program will be called with two or three (in case of "edited") string -parameters. The first parameter is one of +The program will be called with two or three (in case of "editing" and "edited") +string parameters. The first parameter is one of before if this is *before* a recording starts started if this is after a recording has *started* after if this is *after* a recording has finished + editing if this is before *editing* a recording edited if this is after a recording has been *edited* deleted if this is after a recording has been *deleted* and the second parameter contains the full name of the recording's directory (which may not yet exists at that moment in the "before" case). -In the "edited" case it will be the name of the edited version (second -parameter) and the name of the source version (third parameter). +In the "editing" and "edited" case it will be the name of the edited version +(second parameter) and the name of the source version (third parameter). In the "deleted" case the extension of the directory name is ".del" instead of ".rec". @@ -286,6 +305,10 @@ case "$1" in after) echo "After recording $2" ;; + editing) + echo "Editing recording $2" + echo "Source recording $3" + ;; edited) echo "Edited recording $2" echo "Source recording $3" diff --git a/MANUAL b/MANUAL index 3c4003ed..971df402 100644 --- a/MANUAL +++ b/MANUAL @@ -498,6 +498,9 @@ Version 2.2 the name of the recording. First day: The date of the first day when this timer shall start recording (only available for repeating timers). + Record on: The name of the remote VDR this timer shall record on (only available + if there are any remote VDRs connected to this VDR). If this field + is empty, the timer will record on the local VDR. A timer can also be programmed by pressing the "Red" key on the "Schedule", "Now", "Next" or "Event" menus. @@ -654,6 +657,11 @@ Version 2.2 plain recordings. Set this option to "no" if you want folders to be interspersed with recordings when sorted alphabetically. + Default sort mode for recordings = by time + Controls whether recordings are sorted by time or by name. + If a particular sort mode has been selected for a folder by + pressing '0', the default no longer applies to that folder. + Number keys for characters = yes Controls whether the number keys can be used to enter characters in a text input field. You may want to set this @@ -882,8 +890,13 @@ Version 2.2 means that this recording will never be deleted automatically. - Pause priority = 10 The Priority and Lifetime values used when pausing live - Pause lifetime = 1 video. + Record key handling = 2 + Defines what happens if the Record key on the remote control + is pressed during live tv. + 0 = no instant recording + 1 = confirm instant recording + 2 = record instantly + The default is 2. Pause key handling = 2 Defines what happens if the Pause key on the remote control is pressed during live tv. @@ -892,6 +905,9 @@ Version 2.2 2 = pause live video The default is 2. + Pause priority = 10 The Priority and Lifetime values used when pausing live + Pause lifetime = 1 video. + Use episode name = yes Repeating timers use the EPG's 'Episode name' information to create recording file names in a hierarchical structure (for instance to gather all episodes of a series in a @@ -1066,6 +1082,15 @@ Version 2.2 connection after which the connection is automatically closed. Default is 300, a value of 0 means no timeout. + SVDRP peering = no Activates automatic connections between VDRs in the same + network. + + SVDRP host name The name of this VDR, which is used when connecting VDRs + via SVDRP. By default, the machine's host name is used. + + SVDRP default host The name of the VDR to be used by default when creating a + new timer. + Zap timeout = 3 The time (in seconds) until a channel counts as "previous" for switching with '0' diff --git a/Make.config.template b/Make.config.template index 8768f3b9..4b4317d6 100644 --- a/Make.config.template +++ b/Make.config.template @@ -6,7 +6,7 @@ # See the main source file 'vdr.c' for copyright information and # how to reach the author. # -# $Id: Make.config.template 3.4 2015/02/09 09:58:45 kls Exp $ +# $Id: Make.config.template 4.0 2015/02/09 09:58:45 kls Exp $ ### The C compiler and options: diff --git a/Make.global b/Make.global index 665cc285..73e5b49e 100644 --- a/Make.global +++ b/Make.global @@ -4,7 +4,7 @@ # See the main source file 'vdr.c' for copyright information and # how to reach the author. # -# $Id: Make.global 3.0 2012/12/29 12:03:01 kls Exp $ +# $Id: Make.global 4.0 2012/12/29 12:03:01 kls Exp $ # This is just a dummy file for plugins that use old style (version 1.7.33 # or earlier) Makefiles. diff --git a/Makefile b/Makefile index 97220364..b90cbd24 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ # See the main source file 'vdr.c' for copyright information and # how to reach the author. # -# $Id: Makefile 3.6 2015/02/09 12:28:24 kls Exp $ +# $Id: Makefile 4.0 2015/02/09 12:28:24 kls Exp $ .DELETE_ON_ERROR: diff --git a/PLUGINS.html b/PLUGINS.html index 6f47f2f5..dd7a154f 100644 --- a/PLUGINS.html +++ b/PLUGINS.html @@ -38,7 +38,7 @@ Copyright © 2015 Klaus Schmidinger
www.tvdr.de
-Important modifications introduced since version 2.0 are marked like this. +Important modifications introduced since version 2.2 are marked like this.

VDR provides an easy to use plugin interface that allows additional functionality @@ -99,12 +99,12 @@ structures and allows it to hook itself into specific areas to perform special a

  • Skins
  • Themes
  • Devices -
  • Positioners +
  • Positioners
  • Audio
  • Remote Control
  • Conditional Access
  • Electronic Program Guide -
  • The video directory +
  • The video directory @@ -1161,6 +1161,12 @@ The returned string may consist of several lines, separated by the newline chara ('\n'). Each of these lines will be preceded with the ReplyCode when presenting them to the caller, and the continuation character ('-') will be set for all but the last one. +

    + +The SVDRP functions are called from the separate "SVDRP server handler" thread. +Therefore the plugin needs to take care of proper locking if it accesses any +global data. +


    Loading plugins into VDR

    @@ -1877,7 +1883,7 @@ virtual bool SetPlayMode(ePlayMode PlayMode); virtual int64_t GetSTC(void); virtual bool IsPlayingVideo(void) const; virtual bool HasIBPTrickSpeed(void); -virtual void TrickSpeed(int Speed, bool Forward); +virtual void TrickSpeed(int Speed, bool Forward); virtual void Clear(void); virtual void Play(void); virtual void Freeze(void); @@ -2026,7 +2032,6 @@ new cMyDeviceHook; and shall not delete this object. It will be automatically deleted when the program ends. -

    Positioners

    Now you see me - now you don't!

    @@ -2065,7 +2070,6 @@ You should create your derived positioner object in the Note that the object has to be created on the heap (using new), and you shall not delete it at any point (it will be deleted automatically when the program ends). -


    Audio

    @@ -2301,7 +2305,6 @@ to signal VDR that no other EPG handlers shall be queried after this one.

    See VDR/epg.h for details. -


    The video directory

    Bits and pieces...

    @@ -2335,7 +2338,6 @@ You should create your derived video directory object in the Note that the object has to be created on the heap (using new), and you shall not delete it at any point (it will be deleted automatically when the program ends). -

    diff --git a/PLUGINS/src/dvbhddevice/COPYING b/PLUGINS/src/dvbhddevice/COPYING deleted file mode 100644 index f90922ee..00000000 --- a/PLUGINS/src/dvbhddevice/COPYING +++ /dev/null @@ -1,340 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/PLUGINS/src/dvbhddevice/HISTORY b/PLUGINS/src/dvbhddevice/HISTORY deleted file mode 100644 index 532b5e91..00000000 --- a/PLUGINS/src/dvbhddevice/HISTORY +++ /dev/null @@ -1,108 +0,0 @@ -VDR Plugin 'dvbhddevice' Revision History ------------------------------------------ - -2009-12-29: Version 0.0.1 - -- Initial revision. - -2010-01-04: Version 0.0.2 - -- Calling the MakePrimaryDevice() function of the base class to allow - the cDevice to stop displaying subtitles. -- Added support for DVB cards with multiple frontends. - -2011-04-17: Version 0.0.3 - -- Improved trickmodes -- No transfer mode needed for dolby digital -- Clear audio and video PID when Clear() ist called to stop audio decoding when jumping to cutting marks -- Support still frames in H264 format -- Remote control setup -- Added analogue video setting, support volume control. -- Support setting of audio delay. -- Support setting of audio channel (Stereo, Left, Right) -- Support setting of audio downmix mode. -- Playback of PES data is working now. -- Fall back to MPEG2 stream type when no PMT is available. -- Added support for PES PCM playback -- Support HDMI-CEC. (One-Touch Play) -- Added low level OSD implementation. -- Added option to select between high level and low level OSD. -- high level OSD: Implement SaveRegion and RestoreRegion. -- Fix not working video playback when PCR PID is different from video PID. -- Fix not working pause when playing H.264 video -- Improvements in transfer mode, fix audio dropouts or no audio at all -- Add implementation of CanHandleAreas method to support VDR 1.7.17 -- in cHdffOsdRaw::Flush fix reusing of loop variable i in subloop that lead to OSD update problems -- Specify container format when starting audio decoding to support PES-DVD containers -- Added support for True Color OSD -- Allow to disable true color OSD support via setup option - -2011-04-xx: Version 0.0.4 - -- locally define DVB OSD API extensions to support compiling with original DVB headers -- Return correct pixel aspect ratio in GetOsdSize -- Adapt Makefile to changes introduced in recent VDR versions - -2012-12-27: Version 0.0.5 - -- Adapted Makefile to changes introduced in recent VDR versions. - -2013-01-12: Version 0.0.6 - -- Adapted Makefile to changes introduced in recent VDR versions. - -2013-01-24: Version 0.0.7 - -- Fixed cHdffOsd::SetAreas() (didn't clear the OSD). - -2013-02-16: Version 0.0.8 - -- Added missing $(LDFLAGS) to the Makefile (thanks to Ville Skytt). - -2013-02-24: Version 0.0.9 - -- Updated the Finnish OSD texts (thanks to Rolf Ahrenberg). -- Updated the Italian OSD texts (thanks to Diego Pierotto). -- Fixed flashing OSD in "high level OSD" mode in case a menu is open while subtitles - are being displayed. -- Fixed immediately disappearing subtitle track menu when selecting "No subtitles". - -2013-03-31: Version 2.0.0 - -- Official release. - -2013-04-11: Version 2.0.1 - -- Fixed aspect ratio and position of scaled video. - -2013-08-23: Version 2.1.1 - -- Fixed aspect ratio and position of scaled video. -- Added yellow button in main menu to send CEC TV-Off command. - -2013-08-26: Version 2.1.2 - -- Updated the Finnish OSD texts (thanks to Rolf Ahrenberg). - -2014-01-01: Version 2.1.3 - -- Avoiding unnecessary pkg-config warnings in plugin Makefiles. -- cDevice::TrickSpeed() now has an additional parameter named Forward. - -2014-01-17: Version 2.1.4 - -- Using PCR based clock recovery in transfer mode. - -2014-03-15: Version 2.1.6 - -- The function cDevice::GetVideoSystem() has been deprecated. -- Removed old-style video format setting functions. - -2015-02-11: Version 2.1.7 - -- Adapted to the new return value of cOsd::RenderPixmaps(). - -2015-02-19: Version 2.2.0 - -- Official release. diff --git a/PLUGINS/src/dvbhddevice/Makefile b/PLUGINS/src/dvbhddevice/Makefile deleted file mode 100644 index a994e2e8..00000000 --- a/PLUGINS/src/dvbhddevice/Makefile +++ /dev/null @@ -1,123 +0,0 @@ -# -# Makefile for a Video Disk Recorder plugin -# - -# The official name of this plugin. -# This name will be used in the '-P...' option of VDR to load the plugin. -# By default the main source file also carries this name. - -PLUGIN = dvbhddevice - -### The version number of this plugin (taken from the main source file): - -VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g') - -### The directory environment: - -# Use package data if installed...otherwise assume we're under the VDR source directory: -PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell PKG_CONFIG_PATH="$$PKG_CONFIG_PATH:../../.." pkg-config --variable=$(1) vdr)) -LIBDIR = $(call PKGCFG,libdir) -LOCDIR = $(call PKGCFG,locdir) -PLGCFG = $(call PKGCFG,plgcfg) -# -TMPDIR ?= /tmp - -### The compiler options: - -export CFLAGS = $(call PKGCFG,cflags) -export CXXFLAGS = $(call PKGCFG,cxxflags) - -### The version number of VDR's plugin API: - -APIVERSION = $(call PKGCFG,apiversion) - -### Allow user defined options to overwrite defaults: - --include $(PLGCFG) - -### The name of the distribution archive: - -ARCHIVE = $(PLUGIN)-$(VERSION) -PACKAGE = vdr-$(ARCHIVE) - -### The name of the shared object file: - -SOFILE = libvdr-$(PLUGIN).so - -### Includes and Defines (add further entries here): - -INCLUDES += - -DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' - -### The object files (add further files here): - -OBJS = $(PLUGIN).o dvbhdffdevice.o hdffcmd.o hdffosd.o menu.o setup.o - -### The main target: - -all: $(SOFILE) i18n - -### Implicit rules: - -%.o: %.c - $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $< - -### Dependencies: - -MAKEDEP = $(CXX) -MM -MG -DEPFILE = .dependencies -$(DEPFILE): Makefile - @$(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@ - --include $(DEPFILE) - -### Internationalization (I18N): - -PODIR = po -I18Npo = $(wildcard $(PODIR)/*.po) -I18Nmo = $(addsuffix .mo, $(foreach file, $(I18Npo), $(basename $(file)))) -I18Nmsgs = $(addprefix $(DESTDIR)$(LOCDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file)))))) -I18Npot = $(PODIR)/$(PLUGIN).pot - -%.mo: %.po - msgfmt -c -o $@ $< - -$(I18Npot): $(wildcard *.c) - xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --package-name=vdr-$(PLUGIN) --package-version=$(VERSION) --msgid-bugs-address='' -o $@ `ls $^` - -%.po: $(I18Npot) - msgmerge -U --no-wrap --no-location --backup=none -q -N $@ $< - @touch $@ - -$(I18Nmsgs): $(DESTDIR)$(LOCDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo - install -D -m644 $< $@ - -.PHONY: i18n -i18n: $(I18Nmo) $(I18Npot) - -install-i18n: $(I18Nmsgs) - -### Targets: - -$(SOFILE): $(OBJS) libhdffcmd - @$(MAKE) --no-print-directory -C libhdffcmd all - $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) libhdffcmd/libhdffcmd.a -o $@ - -install-lib: $(SOFILE) - install -D $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION) - -install: install-lib install-i18n - -dist: $(I18Npo) clean - @-rm -rf $(TMPDIR)/$(ARCHIVE) - @mkdir $(TMPDIR)/$(ARCHIVE) - @cp -a * $(TMPDIR)/$(ARCHIVE) - @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE) - @-rm -rf $(TMPDIR)/$(ARCHIVE) - @echo Distribution package created as $(PACKAGE).tgz - -clean: - @-rm -f $(PODIR)/*.mo $(PODIR)/*.pot - @-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~ - @-$(MAKE) --no-print-directory -C libhdffcmd clean diff --git a/PLUGINS/src/dvbhddevice/README b/PLUGINS/src/dvbhddevice/README deleted file mode 100644 index 5697dea7..00000000 --- a/PLUGINS/src/dvbhddevice/README +++ /dev/null @@ -1,18 +0,0 @@ -This is a "plugin" for the Video Disk Recorder (VDR). - -Written by: Andreas Regel - -Project's homepage: http://powarman.dyndns.org/hg/dvbhddevice - -Latest version available at: http://powarman.dyndns.org/hg/dvbhddevice - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. -See the file COPYING for more information. - -Description: - -The 'dvbhddevice' plugin implements the output device for the -"Full Featured TechnoTrend S2-6400" DVB cards. diff --git a/PLUGINS/src/dvbhddevice/dvbhddevice.c b/PLUGINS/src/dvbhddevice/dvbhddevice.c deleted file mode 100644 index 3ee74475..00000000 --- a/PLUGINS/src/dvbhddevice/dvbhddevice.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * dvbhddevice.c: A plugin for the Video Disk Recorder - * - * See the README file for copyright information and how to reach the author. - */ - -#include -#include -#include "dvbhdffdevice.h" -#include "menu.h" -#include "setup.h" - -static const char *VERSION = "2.2.0"; -static const char *DESCRIPTION = trNOOP("HD Full Featured DVB device"); -static const char *MAINMENUENTRY = "dvbhddevice"; - -class cPluginDvbhddevice : public cPlugin { -private: - cDvbHdFfDeviceProbe *probe; - bool mIsUserInactive; -public: - cPluginDvbhddevice(void); - virtual ~cPluginDvbhddevice(); - virtual const char *Version(void) { return VERSION; } - virtual const char *Description(void) { return tr(DESCRIPTION); } - virtual void MainThreadHook(void); - virtual void Stop(void); - virtual const char *MainMenuEntry(void); - virtual cOsdObject *MainMenuAction(void); - virtual cMenuSetupPage *SetupMenu(void); - virtual bool SetupParse(const char *Name, const char *Value); - }; - -cPluginDvbhddevice::cPluginDvbhddevice(void) -: mIsUserInactive(true) -{ - probe = new cDvbHdFfDeviceProbe; -} - -cPluginDvbhddevice::~cPluginDvbhddevice() -{ - delete probe; -} - -void cPluginDvbhddevice::MainThreadHook(void) -{ - bool isUserInactive = ShutdownHandler.IsUserInactive(); - if (isUserInactive != mIsUserInactive) - { - mIsUserInactive = isUserInactive; - if (gHdffSetup.CecEnabled && gHdffSetup.CecTvOn) - { - HDFF::cHdffCmdIf * hdffCmdIf = cDvbHdFfDevice::GetHdffCmdHandler(); - if (hdffCmdIf && !mIsUserInactive) - { - hdffCmdIf->CmdHdmiSendCecCommand(HDFF_CEC_COMMAND_TV_ON); - } - } - } -} - -void cPluginDvbhddevice::Stop(void) -{ - if (gHdffSetup.CecEnabled && gHdffSetup.CecTvOff) - { - HDFF::cHdffCmdIf * hdffCmdIf = cDvbHdFfDevice::GetHdffCmdHandler(); - if (hdffCmdIf) - { - hdffCmdIf->CmdHdmiSendCecCommand(HDFF_CEC_COMMAND_TV_OFF); - isyslog("HDFF_CEC_COMMAND_TV_OFF"); - } - } -} - -const char *cPluginDvbhddevice::MainMenuEntry(void) -{ - return gHdffSetup.HideMainMenu ? NULL : MAINMENUENTRY; -} - -cOsdObject *cPluginDvbhddevice::MainMenuAction(void) -{ - HDFF::cHdffCmdIf * hdffCmdIf = cDvbHdFfDevice::GetHdffCmdHandler(); - return hdffCmdIf ? new cHdffMenu(hdffCmdIf) : NULL; -} - -cMenuSetupPage *cPluginDvbhddevice::SetupMenu(void) -{ - HDFF::cHdffCmdIf * hdffCmdIf = cDvbHdFfDevice::GetHdffCmdHandler(); - return hdffCmdIf ? new cHdffSetupPage(hdffCmdIf) : NULL; -} - -bool cPluginDvbhddevice::SetupParse(const char *Name, const char *Value) -{ - return gHdffSetup.SetupParse(Name, Value); -} - -VDRPLUGINCREATOR(cPluginDvbhddevice); // Don't touch this! diff --git a/PLUGINS/src/dvbhddevice/dvbhdffdevice.c b/PLUGINS/src/dvbhddevice/dvbhdffdevice.c deleted file mode 100644 index 8ee80754..00000000 --- a/PLUGINS/src/dvbhddevice/dvbhdffdevice.c +++ /dev/null @@ -1,1104 +0,0 @@ -/* - * dvbhdffdevice.c: The DVB HD Full Featured device interface - * - * See the README file for copyright information and how to reach the author. - */ - -#include - -#include "dvbhdffdevice.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "hdffosd.h" -#include "setup.h" - - -static uchar *YuvToJpeg(uchar *Mem, int Width, int Height, int &Size, int Quality); - - -// --- cDvbHdFfDevice -------------------------------------------------------- - -int cDvbHdFfDevice::devHdffOffset = -1; - -cDvbHdFfDevice::cDvbHdFfDevice(int Adapter, int Frontend) -:cDvbDevice(Adapter, Frontend) -{ - spuDecoder = NULL; - audioChannel = 0; - playMode = pmNone; - mHdffCmdIf = NULL; - - // Devices that are only present on cards with decoders: - - fd_osd = DvbOpen(DEV_DVB_OSD, adapter, frontend, O_RDWR); - fd_video = DvbOpen(DEV_DVB_VIDEO, adapter, frontend, O_RDWR | O_NONBLOCK); - fd_audio = DvbOpen(DEV_DVB_AUDIO, adapter, frontend, O_RDWR | O_NONBLOCK); - - //TODO missing /dev/video offset calculation - - isHdffPrimary = false; - if (devHdffOffset < 0) { - devHdffOffset = adapter; - isHdffPrimary = true; - mHdffCmdIf = new HDFF::cHdffCmdIf(fd_osd); - - uint32_t firmwareVersion = mHdffCmdIf->CmdGetFirmwareVersion(NULL, 0); - if (firmwareVersion < 0x401) - supportsPcrInTransferMode = false; - else - supportsPcrInTransferMode = true; - - /* reset some stuff in case the VDR was killed before and had no chance - to clean up. */ - mHdffCmdIf->CmdOsdReset(); - - mHdffCmdIf->CmdAvSetVideoSpeed(0, 100); - mHdffCmdIf->CmdAvSetAudioSpeed(0, 100); - - mHdffCmdIf->CmdAvEnableVideoAfterStop(0, false); - mHdffCmdIf->CmdAvSetPcrPid(0, 0); - mHdffCmdIf->CmdAvSetVideoPid(0, 0, HDFF_VIDEO_STREAM_MPEG1); - mHdffCmdIf->CmdAvSetAudioPid(0, 0, HDFF_AUDIO_STREAM_MPEG1); - - ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX); - mHdffCmdIf->CmdAvSetDecoderInput(0, 0); - mHdffCmdIf->CmdAvEnableSync(0, true); - mHdffCmdIf->CmdAvSetPlayMode(0, true); - /* reset done */ - - mHdffCmdIf->CmdAvSetAudioDelay(gHdffSetup.AudioDelay); - mHdffCmdIf->CmdAvSetAudioDownmix((HdffAudioDownmixMode_t) gHdffSetup.AudioDownmix); - mHdffCmdIf->CmdAvSetSyncShift(gHdffSetup.AvSyncShift); - mHdffCmdIf->CmdMuxSetVideoOut((HdffVideoOut_t) gHdffSetup.AnalogueVideo); - mHdffCmdIf->CmdHdmiSetVideoMode(gHdffSetup.GetVideoMode()); - - HdffHdmiConfig_t hdmiConfig; - memset(&hdmiConfig, 0, sizeof(hdmiConfig)); - hdmiConfig.TransmitAudio = true; - hdmiConfig.ForceDviMode = false; - hdmiConfig.CecEnabled = gHdffSetup.CecEnabled; - strcpy(hdmiConfig.CecDeviceName, "VDR"); - hdmiConfig.VideoModeAdaption = (HdffVideoModeAdaption_t) gHdffSetup.VideoModeAdaption; - mHdffCmdIf->CmdHdmiConfigure(&hdmiConfig); - - mHdffCmdIf->CmdRemoteSetProtocol((HdffRemoteProtocol_t) gHdffSetup.RemoteProtocol); - mHdffCmdIf->CmdRemoteSetAddressFilter(gHdffSetup.RemoteAddress >= 0, gHdffSetup.RemoteAddress); - } -} - -cDvbHdFfDevice::~cDvbHdFfDevice() -{ - delete spuDecoder; - if (isHdffPrimary) - { - delete mHdffCmdIf; - } - // We're not explicitly closing any device files here, since this sometimes - // caused segfaults. Besides, the program is about to terminate anyway... -} - -void cDvbHdFfDevice::MakePrimaryDevice(bool On) -{ - if (On) { - new cHdffOsdProvider(mHdffCmdIf); - - gHdffSetup.SetVideoFormat(mHdffCmdIf); - } - cDvbDevice::MakePrimaryDevice(On); -} - -bool cDvbHdFfDevice::HasDecoder(void) const -{ - return isHdffPrimary; -} - -cSpuDecoder *cDvbHdFfDevice::GetSpuDecoder(void) -{ - if (!spuDecoder && IsPrimaryDevice()) - spuDecoder = new cDvbSpuDecoder(); - return spuDecoder; -} - -uchar *cDvbHdFfDevice::GrabImage(int &Size, bool Jpeg, int Quality, int SizeX, int SizeY) -{ - #define BUFFER_SIZE (sizeof(struct v4l2_pix_format) + 1920 * 1080 * 2) - int fd; - uint8_t * buffer; - uint8_t * result = NULL; - - fd = DvbOpen(DEV_DVB_VIDEO, adapter, frontend, O_RDONLY); - if (fd < 0) { - esyslog("GrabImage: failed open DVB video device"); - return NULL; - } - - buffer = (uint8_t *) malloc(BUFFER_SIZE); - if (buffer) - { - int readBytes; - - readBytes = read(fd, buffer, BUFFER_SIZE); - if (readBytes < (int) sizeof(struct v4l2_pix_format)) - esyslog("GrabImage: failed reading from DVB video device"); - else { - struct v4l2_pix_format * pixfmt; - int dataSize; - - pixfmt = (struct v4l2_pix_format *) buffer; - dsyslog("GrabImage: Read image of size %d x %d", - pixfmt->width, pixfmt->height); - dataSize = readBytes - sizeof(struct v4l2_pix_format); - if (dataSize < (int) pixfmt->sizeimage) - esyslog("GrabImage: image is not complete"); - else { - if (Jpeg) { - uint8_t * temp; - temp = (uint8_t *) malloc(pixfmt->width * 3 * pixfmt->height); - if (temp) { - int numPixels = pixfmt->width * pixfmt->height; - uint8_t * destData = temp; - uint8_t * srcData = buffer + sizeof(struct v4l2_pix_format); - while (numPixels > 0) - { - destData[0] = srcData[1]; - destData[1] = srcData[0]; - destData[2] = srcData[2]; - destData[3] = srcData[3]; - destData[4] = srcData[0]; - destData[5] = srcData[2]; - srcData += 4; - destData += 6; - numPixels -= 2; - } - if (Quality < 0) - Quality = 100; - result = YuvToJpeg(temp, pixfmt->width, pixfmt->height, Size, Quality); - free(temp); - } - } - else { - // convert to PNM: - char buf[32]; - snprintf(buf, sizeof(buf), "P6\n%d\n%d\n255\n", - pixfmt->width, pixfmt->height); - int l = strlen(buf); - Size = l + pixfmt->width * 3 * pixfmt->height; - result = (uint8_t *) malloc(Size); - if (result) - { - memcpy(result, buf, l); - uint8_t * destData = result + l; - uint8_t * srcData = buffer + sizeof(struct v4l2_pix_format); - int numPixels = pixfmt->width * pixfmt->height; - while (numPixels > 0) - { - int cb = srcData[0] - 128; - int y1 = srcData[1]; - int cr = srcData[2] - 128; - int y2 = srcData[3]; - int r; - int g; - int b; - - r = y1 + (int) (1.402f * cr); - g = y1 - (int) (0.344f * cb + 0.714f * cr); - b = y1 + (int) (1.772f * cb); - destData[0] = r > 255 ? 255 : r < 0 ? 0 : r; - destData[1] = g > 255 ? 255 : g < 0 ? 0 : g; - destData[2] = b > 255 ? 255 : b < 0 ? 0 : b; - r = y2 + (int) (1.402f * cr); - g = y2 - (int) (0.344f * cb + 0.714f * cr); - b = y2 + (int) (1.772f * cb); - destData[3] = r > 255 ? 255 : r < 0 ? 0 : r; - destData[4] = g > 255 ? 255 : g < 0 ? 0 : g; - destData[5] = b > 255 ? 255 : b < 0 ? 0 : b; - - srcData += 4; - destData += 6; - numPixels -= 2; - } - } - } - } - } - free(buffer); - } - - close(fd); - - return result; -} - -void cDvbHdFfDevice::SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat) -{ - if (gHdffSetup.TvFormat == HDFF_TV_FORMAT_4_BY_3) - { - switch (VideoDisplayFormat) - { - case vdfPanAndScan: - case vdfCenterCutOut: - gHdffSetup.VideoConversion = HDFF_VIDEO_CONVERSION_CENTRE_CUT_OUT; - break; - - case vdfLetterBox: - gHdffSetup.VideoConversion = HDFF_VIDEO_CONVERSION_LETTERBOX_16_BY_9; - break; - } - gHdffSetup.SetVideoFormat(mHdffCmdIf); - } - cDevice::SetVideoDisplayFormat(VideoDisplayFormat); -} - -void cDvbHdFfDevice::GetVideoSize(int &Width, int &Height, double &VideoAspect) -{ - if (fd_video >= 0) { - video_size_t vs; - if (ioctl(fd_video, VIDEO_GET_SIZE, &vs) == 0) { - Width = vs.w; - Height = vs.h; - switch (vs.aspect_ratio) { - default: - case VIDEO_FORMAT_4_3: VideoAspect = 4.0 / 3.0; break; - case VIDEO_FORMAT_16_9: VideoAspect = 16.0 / 9.0; break; - case VIDEO_FORMAT_221_1: VideoAspect = 2.21; break; - } - return; - } - else - LOG_ERROR; - } - cDevice::GetVideoSize(Width, Height, VideoAspect); -} - -void cDvbHdFfDevice::GetOsdSize(int &Width, int &Height, double &PixelAspect) -{ - gHdffSetup.GetOsdSize(Width, Height, PixelAspect); -} - -bool cDvbHdFfDevice::SetPid(cPidHandle *Handle, int Type, bool On) -{ - //printf("SetPid Type %d, On %d, PID %5d, streamtype %d, handle %d, used %d\n", Type, On, Handle->pid, Handle->streamType, Handle->handle, Handle->used); - if (Handle->pid) { - dmx_pes_filter_params pesFilterParams; - memset(&pesFilterParams, 0, sizeof(pesFilterParams)); - if (On) { - if (Handle->handle < 0) { - Handle->handle = DvbOpen(DEV_DVB_DEMUX, adapter, frontend, O_RDWR | O_NONBLOCK, true); - if (Handle->handle < 0) { - LOG_ERROR; - return false; - } - } - if (Type == ptPcr) - mHdffCmdIf->CmdAvSetPcrPid(0, Handle->pid); - else if (Type == ptVideo) { - if (Handle->streamType == 0x1B) - mHdffCmdIf->CmdAvSetVideoPid(0, Handle->pid, HDFF_VIDEO_STREAM_H264); - else - mHdffCmdIf->CmdAvSetVideoPid(0, Handle->pid, HDFF_VIDEO_STREAM_MPEG2); - } - else if (Type == ptAudio) { - if (Handle->streamType == 0x03) - mHdffCmdIf->CmdAvSetAudioPid(0, Handle->pid, HDFF_AUDIO_STREAM_MPEG1); - else if (Handle->streamType == 0x04) - mHdffCmdIf->CmdAvSetAudioPid(0, Handle->pid, HDFF_AUDIO_STREAM_MPEG2); - else if (Handle->streamType == SI::AC3DescriptorTag) - mHdffCmdIf->CmdAvSetAudioPid(0, Handle->pid, HDFF_AUDIO_STREAM_AC3); - else if (Handle->streamType == SI::EnhancedAC3DescriptorTag) - mHdffCmdIf->CmdAvSetAudioPid(0, Handle->pid, HDFF_AUDIO_STREAM_EAC3); - else if (Handle->streamType == 0x0F) - mHdffCmdIf->CmdAvSetAudioPid(0, Handle->pid, HDFF_AUDIO_STREAM_AAC); - else if (Handle->streamType == 0x11) - mHdffCmdIf->CmdAvSetAudioPid(0, Handle->pid, HDFF_AUDIO_STREAM_HE_AAC); - else - mHdffCmdIf->CmdAvSetAudioPid(0, Handle->pid, HDFF_AUDIO_STREAM_MPEG1); - } - if (!(Type <= ptDolby && Handle->used <= 1)) { - pesFilterParams.pid = Handle->pid; - pesFilterParams.input = DMX_IN_FRONTEND; - pesFilterParams.output = DMX_OUT_TS_TAP; - pesFilterParams.pes_type= DMX_PES_OTHER; - pesFilterParams.flags = DMX_IMMEDIATE_START; - if (ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams) < 0) { - LOG_ERROR; - return false; - } - } - } - else if (!Handle->used) { - CHECK(ioctl(Handle->handle, DMX_STOP)); - if (Type == ptPcr) - mHdffCmdIf->CmdAvSetPcrPid(0, 0); - else if (Type == ptVideo) - mHdffCmdIf->CmdAvSetVideoPid(0, 0, HDFF_VIDEO_STREAM_MPEG1); - else if (Type == ptAudio) - mHdffCmdIf->CmdAvSetAudioPid(0, 0, HDFF_AUDIO_STREAM_MPEG1); - else if (Type == ptDolby) - mHdffCmdIf->CmdAvSetAudioPid(0, 0, HDFF_AUDIO_STREAM_AC3); - //TODO missing setting to 0x1FFF??? see cDvbDevice::SetPid() - close(Handle->handle); - Handle->handle = -1; - } - } - return true; -} - -void cDvbHdFfDevice::TurnOffLiveMode(bool LiveView) -{ - // Turn off live PIDs: - - DetachAll(pidHandles[ptAudio].pid); - DetachAll(pidHandles[ptVideo].pid); - DetachAll(pidHandles[ptPcr].pid); - DetachAll(pidHandles[ptTeletext].pid); - DelPid(pidHandles[ptAudio].pid); - DelPid(pidHandles[ptVideo].pid); - DelPid(pidHandles[ptPcr].pid, ptPcr); - DelPid(pidHandles[ptTeletext].pid); - DelPid(pidHandles[ptDolby].pid); -} - -bool cDvbHdFfDevice::SetChannelDevice(const cChannel *Channel, bool LiveView) -{ - int apid = Channel->Apid(0); - int vpid = Channel->Vpid(); - int dpid = Channel->Dpid(0); - - bool DoTune = !IsTunedToTransponder(Channel); - - bool pidHandlesVideo = vpid && pidHandles[ptVideo].pid == vpid; - bool pidHandlesAudio = apid && pidHandles[ptAudio].pid == apid; - - bool TurnOffLivePIDs = DoTune - || !IsPrimaryDevice() - || LiveView // for a new live view the old PIDs need to be turned off - || pidHandlesVideo // for recording the PIDs must be shifted from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER - ; - - bool StartTransferMode = IsPrimaryDevice() && !DoTune - && (LiveView && HasPid(vpid ? vpid : apid) && (!pidHandlesVideo || (!pidHandlesAudio && (dpid ? pidHandles[ptAudio].pid != dpid : true)))// the PID is already set as DMX_PES_OTHER - || !LiveView && (pidHandlesVideo || pidHandlesAudio) // a recording is going to shift the PIDs from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER - ); - if (CamSlot() && !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), CamSlot()->SlotNumber())) - StartTransferMode |= LiveView && IsPrimaryDevice() && Channel->Ca() >= CA_ENCRYPTED_MIN; - - //printf("SetChannelDevice Transfer %d, Live %d\n", StartTransferMode, LiveView); - - bool TurnOnLivePIDs = !StartTransferMode && LiveView; - - // Turn off live PIDs if necessary: - - if (TurnOffLivePIDs) - TurnOffLiveMode(LiveView); - - // Set the tuner: - - if (!cDvbDevice::SetChannelDevice(Channel, LiveView)) - return false; - - // PID settings: - - if (TurnOnLivePIDs) { - if (!(AddPid(Channel->Ppid(), ptPcr) && AddPid(vpid, ptVideo, Channel->Vtype()) && AddPid(apid ? apid : dpid, ptAudio, apid ? 0 : Channel->Dtype(0)))) { - esyslog("ERROR: failed to set PIDs for channel %d on device %d", Channel->Number(), CardIndex() + 1); - return false; - } - } - else if (StartTransferMode) - cControl::Launch(new cTransferControl(this, Channel)); - - return true; -} - -int cDvbHdFfDevice::GetAudioChannelDevice(void) -{ - return audioChannel; -} - -void cDvbHdFfDevice::SetAudioChannelDevice(int AudioChannel) -{ - mHdffCmdIf->CmdAvSetAudioChannel(AudioChannel); - audioChannel = AudioChannel; -} - -void cDvbHdFfDevice::SetVolumeDevice(int Volume) -{ - mHdffCmdIf->CmdMuxSetVolume(Volume * 100 / 255); -} - -void cDvbHdFfDevice::SetAudioTrackDevice(eTrackType Type) -{ - //printf("SetAudioTrackDevice %d\n", Type); - const tTrackId *TrackId = GetTrack(Type); - if (TrackId && TrackId->id) { - int streamType = 0; - cChannel * channel = Channels.GetByNumber(CurrentChannel()); - if (channel) { - if (IS_AUDIO_TRACK(Type)) - streamType = channel->Atype(Type - ttAudioFirst); - else if (IS_DOLBY_TRACK(Type)) - streamType = channel->Dtype(Type - ttDolbyFirst); - } - //printf("SetAudioTrackDevice new %d %d, current %d\n", TrackId->id, streamType, pidHandles[ptAudio].pid); - if (pidHandles[ptAudio].pid && pidHandles[ptAudio].pid != TrackId->id) { - DetachAll(pidHandles[ptAudio].pid); - if (CamSlot()) - CamSlot()->SetPid(pidHandles[ptAudio].pid, false); - pidHandles[ptAudio].pid = TrackId->id; - pidHandles[ptAudio].streamType = streamType; - SetPid(&pidHandles[ptAudio], ptAudio, true); - if (CamSlot()) { - CamSlot()->SetPid(pidHandles[ptAudio].pid, true); - CamSlot()->StartDecrypting(); - } - } - } -} - -bool cDvbHdFfDevice::CanReplay(void) const -{ - return cDevice::CanReplay(); -} - -bool cDvbHdFfDevice::SetPlayMode(ePlayMode PlayMode) -{ - if (PlayMode == pmNone) { - if (fd_video == -1) - fd_video = DvbOpen(DEV_DVB_VIDEO, adapter, frontend, O_RDWR | O_NONBLOCK); - if (fd_audio == -1) - fd_audio = DvbOpen(DEV_DVB_AUDIO, adapter, frontend, O_RDWR | O_NONBLOCK); - - mHdffCmdIf->CmdAvSetVideoSpeed(0, 100); - mHdffCmdIf->CmdAvSetAudioSpeed(0, 100); - - mHdffCmdIf->CmdAvEnableVideoAfterStop(0, false); - mHdffCmdIf->CmdAvSetPcrPid(0, 0); - mHdffCmdIf->CmdAvSetVideoPid(0, 0, HDFF_VIDEO_STREAM_MPEG1); - mHdffCmdIf->CmdAvSetAudioPid(0, 0, HDFF_AUDIO_STREAM_MPEG1); - - ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX); - mHdffCmdIf->CmdAvSetDecoderInput(0, 0); - mHdffCmdIf->CmdAvEnableSync(0, true); - mHdffCmdIf->CmdAvSetPlayMode(0, true); - mHdffCmdIf->CmdAvMuteAudio(0, false); - } - else { - if (playMode == pmNone) - TurnOffLiveMode(true); - - if (PlayMode == pmExtern_THIS_SHOULD_BE_AVOIDED) - { - close(fd_video); - fd_video = -1; - close(fd_audio); - fd_audio = -1; - } - else - { - isTransferMode = Transferring() || (cTransferControl::ReceiverDevice() == this); - mHdffCmdIf->CmdAvSetPlayMode(1, isTransferMode); - mHdffCmdIf->CmdAvSetStc(0, 100000); - mHdffCmdIf->CmdAvEnableSync(0, false); - mHdffCmdIf->CmdAvEnableVideoAfterStop(0, true); - - playVideoPid = -1; - playAudioPid = -1; - playPcrPid = -1; - audioCounter = 0; - videoCounter = 0; - freezed = false; - trickMode = false; - isPlayingVideo = false; - - mHdffCmdIf->CmdAvSetDecoderInput(0, 2); - ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY); - } - } - playMode = PlayMode; - return true; -} - -int64_t cDvbHdFfDevice::GetSTC(void) -{ - if (isPlayingVideo) - { - if (fd_video >= 0) { - uint64_t pts; - if (ioctl(fd_video, VIDEO_GET_PTS, &pts) == -1) { - esyslog("ERROR: pts %d: %m", CardIndex() + 1); - return -1; - } - //printf("video PTS %lld\n", pts); - return pts; - } - } - else - { - if (fd_audio >= 0) { - uint64_t pts; - if (ioctl(fd_audio, AUDIO_GET_PTS, &pts) == -1) { - esyslog("ERROR: pts %d: %m", CardIndex() + 1); - return -1; - } - //printf("audio PTS %lld\n", pts); - return pts; - } - } - return -1; -} - -cRect cDvbHdFfDevice::CanScaleVideo(const cRect &Rect, int Alignment) -{ - return Rect; -} - -void cDvbHdFfDevice::ScaleVideo(const cRect &Rect) -{ - if (Rect == cRect::Null) - { - mHdffCmdIf->CmdAvSetVideoWindow(0, false, 0, 0, 0, 0); - } - else - { - //printf("ScaleVideo: Rect = %d %d %d %d\n", Rect.X(), Rect.Y(), Rect.Width(), Rect.Height()); - - int osdWidth; - int osdHeight; - double osdPixelAspect; - - GetOsdSize(osdWidth, osdHeight, osdPixelAspect); - //printf("ScaleVideo: OsdSize = %d %d %g\n", osdWidth, osdHeight, osdPixelAspect); - - // Convert the video window coordinates in 1/10 percent of the display - // resolution. - int x = (Rect.X() * 1000 + osdWidth / 2) / osdWidth; - int y = (Rect.Y() * 1000 + osdHeight / 2) / osdHeight; - int w = (Rect.Width() * 1000 + osdWidth / 2) / osdWidth; - int h = (Rect.Height() * 1000 + osdHeight / 2) / osdHeight; - //printf("ScaleVideo: Win1 = %d %d %d %d\n", x, y, w, h); - - // fix aspect ratio, reposition video - if (w > h) { - x += (w - h) / 2; - w = h; - } - else if (w < h) { - y += (h - w) / 2; - h = w; - } - - //printf("ScaleVideo: Win2 = %d %d %d %d\n", x, y, w, h); - mHdffCmdIf->CmdAvSetVideoWindow(0, true, x, y, w, h); - } -} - -#if (APIVERSNUM >= 20103) -void cDvbHdFfDevice::TrickSpeed(int Speed, bool Forward) -#else -void cDvbHdFfDevice::TrickSpeed(int Speed) -#endif -{ - freezed = false; - mHdffCmdIf->CmdAvEnableSync(0, false); - mHdffCmdIf->CmdAvSetAudioPid(0, 0, HDFF_AUDIO_STREAM_MPEG1); - playAudioPid = -1; - if (Speed > 0) - mHdffCmdIf->CmdAvSetVideoSpeed(0, 100 / Speed); - trickMode = true; -} - -void cDvbHdFfDevice::Clear(void) -{ - CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER)); - mHdffCmdIf->CmdAvSetVideoPid(0, 0, HDFF_VIDEO_STREAM_MPEG1); - mHdffCmdIf->CmdAvSetAudioPid(0, 0, HDFF_AUDIO_STREAM_MPEG1); - playVideoPid = -1; - playAudioPid = -1; - cDevice::Clear(); -} - -void cDvbHdFfDevice::Play(void) -{ - freezed = false; - trickMode = false; - if (isPlayingVideo) - mHdffCmdIf->CmdAvEnableSync(0, true); - mHdffCmdIf->CmdAvSetVideoSpeed(0, 100); - mHdffCmdIf->CmdAvSetAudioSpeed(0, 100); - mHdffCmdIf->CmdAvMuteAudio(0, false); - cDevice::Play(); -} - -void cDvbHdFfDevice::Freeze(void) -{ - freezed = true; - mHdffCmdIf->CmdAvSetVideoSpeed(0, 0); - mHdffCmdIf->CmdAvSetAudioSpeed(0, 0); - cDevice::Freeze(); -} - -void cDvbHdFfDevice::Mute(void) -{ - mHdffCmdIf->CmdAvMuteAudio(0, true); - cDevice::Mute(); -} - -static HdffVideoStreamType_t MapVideoStreamTypes(int Vtype) -{ - switch (Vtype) { - case 0x01: return HDFF_VIDEO_STREAM_MPEG1; - case 0x02: return HDFF_VIDEO_STREAM_MPEG2; - case 0x1B: return HDFF_VIDEO_STREAM_H264; - default: return HDFF_VIDEO_STREAM_MPEG2; // fallback to MPEG2 - } -} - -void cDvbHdFfDevice::StillPicture(const uchar *Data, int Length) -{ - if (!Data || Length < TS_SIZE) - return; - if (Data[0] == 0x47) { - // TS data - cDevice::StillPicture(Data, Length); - } - else if (Data[0] == 0x00 && Data[1] == 0x00 && Data[2] == 0x01 && (Data[3] & 0xF0) == 0xE0) { - // PES data - char *buf = MALLOC(char, Length); - if (!buf) - return; - int i = 0; - int blen = 0; - while (i < Length - 6) { - if (Data[i] == 0x00 && Data[i + 1] == 0x00 && Data[i + 2] == 0x01) { - int len = Data[i + 4] * 256 + Data[i + 5]; - if ((Data[i + 3] & 0xF0) == 0xE0) { // video packet - // skip PES header - int offs = i + 6; - // skip header extension - if ((Data[i + 6] & 0xC0) == 0x80) { - // MPEG-2 PES header - if (Data[i + 8] >= Length) - break; - offs += 3; - offs += Data[i + 8]; - len -= 3; - len -= Data[i + 8]; - if (len < 0 || offs + len > Length) - break; - } - else { - // MPEG-1 PES header - while (offs < Length && len > 0 && Data[offs] == 0xFF) { - offs++; - len--; - } - if (offs <= Length - 2 && len >= 2 && (Data[offs] & 0xC0) == 0x40) { - offs += 2; - len -= 2; - } - if (offs <= Length - 5 && len >= 5 && (Data[offs] & 0xF0) == 0x20) { - offs += 5; - len -= 5; - } - else if (offs <= Length - 10 && len >= 10 && (Data[offs] & 0xF0) == 0x30) { - offs += 10; - len -= 10; - } - else if (offs < Length && len > 0) { - offs++; - len--; - } - } - if (blen + len > Length) // invalid PES length field - break; - memcpy(&buf[blen], &Data[offs], len); - i = offs + len; - blen += len; - } - else if (Data[i + 3] >= 0xBD && Data[i + 3] <= 0xDF) // other PES packets - i += len + 6; - else - i++; - } - else - i++; - } - mHdffCmdIf->CmdAvShowStillImage(0, (uint8_t *)buf, blen, MapVideoStreamTypes(PatPmtParser()->Vtype())); - free(buf); - } - else { - // non-PES data - mHdffCmdIf->CmdAvShowStillImage(0, Data, Length, MapVideoStreamTypes(PatPmtParser()->Vtype())); - } -} - -bool cDvbHdFfDevice::Poll(cPoller &Poller, int TimeoutMs) -{ - Poller.Add(fd_video, true); - return Poller.Poll(TimeoutMs); -} - -bool cDvbHdFfDevice::Flush(int TimeoutMs) -{ - //TODO actually this function should wait until all buffered data has been processed by the card, but how? - return true; -} - -void cDvbHdFfDevice::BuildTsPacket(uint8_t * TsBuffer, bool PusiSet, uint16_t Pid, uint8_t Counter, const uint8_t * Data, uint32_t Length) -{ - TsBuffer[0] = 0x47; - TsBuffer[1] = PusiSet ? 0x40 : 0x00; - TsBuffer[1] |= Pid >> 8; - TsBuffer[2] = Pid & 0xFF; - if (Length >= 184) - { - TsBuffer[3] = 0x10 | Counter; - memcpy(TsBuffer + 4, Data, 184); - } - else - { - uint8_t adaptationLength; - - TsBuffer[3] = 0x30 | Counter; - adaptationLength = 183 - Length; - TsBuffer[4] = adaptationLength; - if (adaptationLength > 0) - { - TsBuffer[5] = 0x00; - memset(TsBuffer + 6, 0xFF, adaptationLength - 1); - } - memcpy(TsBuffer + 5 + adaptationLength, Data, Length); - } -} - -uint32_t cDvbHdFfDevice::PesToTs(uint8_t * TsBuffer, uint16_t Pid, uint8_t & Counter, const uint8_t * Data, uint32_t Length) -{ - uint32_t tsOffset; - uint32_t i; - - tsOffset = 0; - i = 0; - while (Length > 0) - { - BuildTsPacket(TsBuffer + tsOffset, i == 0, Pid, Counter, Data + i * 184, Length); - if (Length >= 184) - Length -= 184; - else - Length = 0; - Counter = (Counter + 1) & 15; - tsOffset += 188; - i++; - } - return tsOffset; -} - -int cDvbHdFfDevice::PlayVideo(const uchar *Data, int Length) -{ - if (freezed) - return -1; - if (!isPlayingVideo) - { - mHdffCmdIf->CmdAvEnableSync(0, true); - isPlayingVideo = true; - } - - // ignore padding PES packets - if (Data[3] == 0xBE) - return Length; - - //TODO: support greater Length - uint8_t tsBuffer[188 * 16]; - uint32_t tsLength; - int pid = 100; - - tsLength = PesToTs(tsBuffer, pid, videoCounter, Data, Length); - - if (pid != playVideoPid) { - playVideoPid = pid; - mHdffCmdIf->CmdAvSetVideoPid(0, playVideoPid, HDFF_VIDEO_STREAM_MPEG2, true); - } - if (WriteAllOrNothing(fd_video, tsBuffer, tsLength, 1000, 10) <= 0) - Length = 0; - return Length; -} - -int cDvbHdFfDevice::PlayAudio(const uchar *Data, int Length, uchar Id) -{ - if (freezed) - return -1; - uint8_t streamId; - uint8_t tsBuffer[188 * 16]; - uint32_t tsLength; - HdffAudioStreamType_t streamType = HDFF_AUDIO_STREAM_MPEG1; - HdffAvContainerType_t containerType = HDFF_AV_CONTAINER_PES; - int pid; - - streamId = Data[3]; - if (streamId >= 0xC0 && streamId <= 0xDF) - { - streamType = HDFF_AUDIO_STREAM_MPEG1; - } - else if (streamId == 0xBD) - { - const uint8_t * payload = Data + 9 + Data[8]; - if ((payload[0] & 0xF8) == 0xA0) - { - containerType = HDFF_AV_CONTAINER_PES_DVD; - streamType = HDFF_AUDIO_STREAM_PCM; - } - else if ((payload[0] & 0xF8) == 0x88) - { - containerType = HDFF_AV_CONTAINER_PES_DVD; - streamType = HDFF_AUDIO_STREAM_DTS; - } - else if ((payload[0] & 0xF8) == 0x80) - { - containerType = HDFF_AV_CONTAINER_PES_DVD; - streamType = HDFF_AUDIO_STREAM_AC3; - } - else - { - streamType = HDFF_AUDIO_STREAM_AC3; - } - } - pid = 200 + (int) streamType; - tsLength = PesToTs(tsBuffer, pid, audioCounter, Data, Length); - - if (pid != playAudioPid) { - playAudioPid = pid; - mHdffCmdIf->CmdAvSetAudioPid(0, playAudioPid, streamType, containerType); - } - if (WriteAllOrNothing(fd_video, tsBuffer, tsLength, 1000, 10) <= 0) - Length = 0; - return Length; -} - -int cDvbHdFfDevice::PlayTsVideo(const uchar *Data, int Length) -{ - if (freezed) - return -1; - if (!isPlayingVideo) - { - mHdffCmdIf->CmdAvEnableSync(0, true); - isPlayingVideo = true; - } - - int pid = TsPid(Data); - if (pid != playVideoPid) { - PatPmtParser(); - if (pid == PatPmtParser()->Vpid()) { - playVideoPid = pid; - mHdffCmdIf->CmdAvSetVideoPid(0, playVideoPid, MapVideoStreamTypes(PatPmtParser()->Vtype()), true); - } - } - if (isTransferMode && supportsPcrInTransferMode) { - if (pid != playPcrPid) { - if (pid == PatPmtParser()->Ppid()) { - playPcrPid = pid; - mHdffCmdIf->CmdAvSetPcrPid(0, playPcrPid); - } - } - } - return WriteAllOrNothing(fd_video, Data, Length, 1000, 10); -} - -static HdffAudioStreamType_t MapAudioStreamTypes(int Atype) -{ - switch (Atype) { - case 0x03: return HDFF_AUDIO_STREAM_MPEG1; - case 0x04: return HDFF_AUDIO_STREAM_MPEG2; - case SI::AC3DescriptorTag: return HDFF_AUDIO_STREAM_AC3; - case SI::EnhancedAC3DescriptorTag: return HDFF_AUDIO_STREAM_EAC3; - case 0x0F: return HDFF_AUDIO_STREAM_AAC; - case 0x11: return HDFF_AUDIO_STREAM_HE_AAC; - default: return HDFF_AUDIO_STREAM_MPEG1; - } -} - -int cDvbHdFfDevice::PlayTsAudio(const uchar *Data, int Length) -{ - if (freezed) - return -1; - int pid = TsPid(Data); - if (pid != playAudioPid) { - playAudioPid = pid; - int AudioStreamType = -1; - for (int i = 0; PatPmtParser()->Apid(i); i++) { - if (playAudioPid == PatPmtParser()->Apid(i)) { - AudioStreamType = PatPmtParser()->Atype(i); - break; - } - } - if (AudioStreamType < 0) { - for (int i = 0; PatPmtParser()->Dpid(i); i++) { - if (playAudioPid == PatPmtParser()->Dpid(i)) { - AudioStreamType = PatPmtParser()->Dtype(i); - break; - } - } - } - mHdffCmdIf->CmdAvSetAudioPid(0, playAudioPid, MapAudioStreamTypes(AudioStreamType)); - } - return WriteAllOrNothing(fd_video, Data, Length, 1000, 10); -} - -HDFF::cHdffCmdIf *cDvbHdFfDevice::GetHdffCmdHandler(void) -{ - //TODO why not just keep a pointer? - if (devHdffOffset >= 0) { - cDvbHdFfDevice *device = (cDvbHdFfDevice *)GetDevice(devHdffOffset); - if (device) - return device->mHdffCmdIf; - } - return NULL; -} - -// --- cDvbHdFfDeviceProbe --------------------------------------------------- - -bool cDvbHdFfDeviceProbe::Probe(int Adapter, int Frontend) -{ - static uint32_t SubsystemIds[] = { - 0x13C23009, // Technotrend S2-6400 HDFF development samples - 0x13C2300A, // Technotrend S2-6400 HDFF production version - 0x00000000 - }; - cString FileName; - cReadLine ReadLine; - FILE *f = NULL; - uint32_t SubsystemId = 0; - FileName = cString::sprintf("/sys/class/dvb/dvb%d.frontend%d/device/subsystem_vendor", Adapter, Frontend); - if ((f = fopen(FileName, "r")) != NULL) { - if (char *s = ReadLine.Read(f)) - SubsystemId = strtoul(s, NULL, 0) << 16; - fclose(f); - } - FileName = cString::sprintf("/sys/class/dvb/dvb%d.frontend%d/device/subsystem_device", Adapter, Frontend); - if ((f = fopen(FileName, "r")) != NULL) { - if (char *s = ReadLine.Read(f)) - SubsystemId |= strtoul(s, NULL, 0); - fclose(f); - } - for (uint32_t *sid = SubsystemIds; *sid; sid++) { - if (*sid == SubsystemId) { - FileName = cString::sprintf("/dev/dvb/adapter%d/osd0", Adapter); - int fd = open(FileName, O_RDWR); - if (fd != -1) { //TODO treat the second path of the S2-6400 as a budget device - close(fd); - dsyslog("creating cDvbHdFfDevice"); - new cDvbHdFfDevice(Adapter, Frontend); - return true; - } - } - } - return false; -} - - -// --- YuvToJpeg ------------------------------------------------------------- - -#include - -#define JPEGCOMPRESSMEM 4000000 - -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; - int NewSize = jcd->size + JPEGCOMPRESSMEM; - if (uchar *NewBuffer = (uchar *)realloc(jcd->mem, NewSize)) { - jcd->size = NewSize; - jcd->mem = NewBuffer; - } - else { - esyslog("ERROR: out of memory"); - return false; - } - 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) { - if (uchar *NewBuffer = (uchar *)realloc(jcd->mem, Used)) { - jcd->size = Used; - jcd->mem = NewBuffer; - } - else - esyslog("ERROR: out of memory"); - } - } -} - -static uchar *YuvToJpeg(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_YCbCr; - - 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; -} diff --git a/PLUGINS/src/dvbhddevice/dvbhdffdevice.h b/PLUGINS/src/dvbhddevice/dvbhdffdevice.h deleted file mode 100644 index 580bf9e4..00000000 --- a/PLUGINS/src/dvbhddevice/dvbhdffdevice.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * dvbhdffdevice.h: The DVB HD Full Featured device interface - * - * See the README file for copyright information and how to reach the author. - */ - -#ifndef __DVBHDFFDEVICE_H -#define __DVBHDFFDEVICE_H - -#include "hdffcmd.h" -#include -#include - -/// The cDvbHdFfDevice implements a DVB device which can be accessed through the Linux DVB driver API. - -class cDvbHdFfDevice : public cDvbDevice { -private: - int fd_osd, fd_audio, fd_video; -protected: - virtual void MakePrimaryDevice(bool On); -public: - static bool Probe(int Adapter, int Frontend); - cDvbHdFfDevice(int Adapter, int Frontend); - virtual ~cDvbHdFfDevice(); - virtual bool HasDecoder(void) const; - -// SPU facilities - -private: - cDvbSpuDecoder *spuDecoder; -public: - virtual cSpuDecoder *GetSpuDecoder(void); - -// Channel facilities - -private: - void TurnOffLiveMode(bool LiveView); -protected: - virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView); - -// PID handle facilities - -protected: - virtual bool SetPid(cPidHandle *Handle, int Type, bool On); - -// Image Grab facilities - -public: - virtual uchar *GrabImage(int &Size, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1); - -// Video format facilities - -public: - virtual void SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat); - virtual void GetVideoSize(int &Width, int &Height, double &VideoAspect); - virtual void GetOsdSize(int &Width, int &Height, double &PixelAspect); - -// Track facilities - -protected: - virtual void SetAudioTrackDevice(eTrackType Type); - -// Audio facilities - -private: - int audioChannel; -protected: - virtual int GetAudioChannelDevice(void); - virtual void SetAudioChannelDevice(int AudioChannel); - virtual void SetVolumeDevice(int Volume); - -// Player facilities - -private: - int playVideoPid; - int playAudioPid; - int playPcrPid; - bool freezed; - bool trickMode; - bool isPlayingVideo; - bool isTransferMode; - bool supportsPcrInTransferMode; - - // Pes2Ts conversion stuff - uint8_t videoCounter; - uint8_t audioCounter; - void BuildTsPacket(uint8_t * TsBuffer, bool PusiSet, uint16_t Pid, uint8_t Counter, const uint8_t * Data, uint32_t Length); - uint32_t PesToTs(uint8_t * TsBuffer, uint16_t Pid, uint8_t & Counter, const uint8_t * Data, uint32_t Length); - -protected: - ePlayMode playMode; - virtual bool CanReplay(void) const; - virtual bool SetPlayMode(ePlayMode PlayMode); - virtual int PlayVideo(const uchar *Data, int Length); - virtual int PlayAudio(const uchar *Data, int Length, uchar Id); - virtual int PlayTsVideo(const uchar *Data, int Length); - virtual int PlayTsAudio(const uchar *Data, int Length); -public: - virtual int64_t GetSTC(void); - virtual cRect CanScaleVideo(const cRect &Rect, int Alignment = taCenter); - virtual void ScaleVideo(const cRect &Rect = cRect::Null); -#if (APIVERSNUM >= 20103) - virtual void TrickSpeed(int Speed, bool Forward); -#else - virtual void TrickSpeed(int Speed); -#endif - virtual void Clear(void); - virtual void Play(void); - virtual void Freeze(void); - virtual void Mute(void); - virtual void StillPicture(const uchar *Data, int Length); - virtual bool Poll(cPoller &Poller, int TimeoutMs = 0); - virtual bool Flush(int TimeoutMs = 0); - -// HDFF specific things - -public: - static HDFF::cHdffCmdIf *GetHdffCmdHandler(void); -private: - static int devHdffOffset;//TODO - bool isHdffPrimary;//TODO implicit! - HDFF::cHdffCmdIf *mHdffCmdIf; -}; - -class cDvbHdFfDeviceProbe : public cDvbDeviceProbe { -public: - virtual bool Probe(int Adapter, int Frontend); - }; - -#endif //__DVBHDFFDEVICE_H diff --git a/PLUGINS/src/dvbhddevice/hdffcmd.c b/PLUGINS/src/dvbhddevice/hdffcmd.c deleted file mode 100644 index fd2d598a..00000000 --- a/PLUGINS/src/dvbhddevice/hdffcmd.c +++ /dev/null @@ -1,401 +0,0 @@ -/* - * hdffcmd.c: TODO(short description) - * - * See the README file for copyright information and how to reach the author. - */ - -#include - -#include "hdffcmd.h" -#include "libhdffcmd/hdffcmd.h" -#include -#include -#include - - -namespace HDFF -{ - -cHdffCmdIf::cHdffCmdIf(int OsdDev) -{ - mOsdDev = OsdDev; - if (mOsdDev < 0) - { - //printf("ERROR: invalid OSD device handle (%d)!\n", mOsdDev); - } -} - -cHdffCmdIf::~cHdffCmdIf(void) -{ -} - - -uint32_t cHdffCmdIf::CmdGetFirmwareVersion(char * pString, uint32_t MaxLength) -{ - uint32_t version; - int err; - - err = HdffCmdGetFirmwareVersion(mOsdDev, &version, pString, MaxLength); - if (err == 0) - return version; - return 0; -} - -uint32_t cHdffCmdIf::CmdGetInterfaceVersion(char * pString, uint32_t MaxLength) -{ - uint32_t version; - int err; - - err = HdffCmdGetInterfaceVersion(mOsdDev, &version, pString, MaxLength); - if (err == 0) - return version; - return 0; -} - -uint32_t cHdffCmdIf::CmdGetCopyrights(uint8_t Index, char * pString, uint32_t MaxLength) -{ - int err; - - err = HdffCmdGetCopyrights(mOsdDev, Index, pString, MaxLength); - if (err == 0) - return strlen(pString); - return 0; -} - - -void cHdffCmdIf::CmdAvSetPlayMode(uint8_t PlayMode, bool Realtime) -{ - HdffCmdAvSetPlayMode(mOsdDev, PlayMode, Realtime); -} - -void cHdffCmdIf::CmdAvSetVideoPid(uint8_t DecoderIndex, uint16_t VideoPid, HdffVideoStreamType_t StreamType, bool PlaybackMode) -{ - //printf("SetVideoPid %d %d\n", VideoPid, StreamType); - HdffCmdAvSetVideoPid(mOsdDev, DecoderIndex, VideoPid, StreamType); -} - -void cHdffCmdIf::CmdAvSetAudioPid(uint8_t DecoderIndex, uint16_t AudioPid, HdffAudioStreamType_t StreamType, HdffAvContainerType_t ContainerType) -{ - //printf("SetAudioPid %d %d %d\n", AudioPid, StreamType, ContainerType); - HdffCmdAvSetAudioPid(mOsdDev, DecoderIndex, AudioPid, StreamType, - ContainerType); -} - -void cHdffCmdIf::CmdAvSetPcrPid(uint8_t DecoderIndex, uint16_t PcrPid) -{ - //printf("SetPcrPid %d\n", PcrPid); - HdffCmdAvSetPcrPid(mOsdDev, DecoderIndex, PcrPid); -} - -void cHdffCmdIf::CmdAvSetTeletextPid(uint8_t DecoderIndex, uint16_t TeletextPid) -{ - HdffCmdAvSetTeletextPid(mOsdDev, DecoderIndex, TeletextPid); -} - -void cHdffCmdIf::CmdAvSetVideoWindow(uint8_t DecoderIndex, bool Enable, uint16_t X, uint16_t Y, uint16_t Width, uint16_t Height) -{ - HdffCmdAvSetVideoWindow(mOsdDev, DecoderIndex, Enable, X, Y, Width, Height); -} - -void cHdffCmdIf::CmdAvShowStillImage(uint8_t DecoderIndex, const uint8_t * pStillImage, int Size, HdffVideoStreamType_t StreamType) -{ - HdffCmdAvShowStillImage(mOsdDev, DecoderIndex, pStillImage, Size, - StreamType); -} - -void cHdffCmdIf::CmdAvSetDecoderInput(uint8_t DecoderIndex, uint8_t DemultiplexerIndex) -{ - HdffCmdAvSetDecoderInput(mOsdDev, DecoderIndex, DemultiplexerIndex); -} - -void cHdffCmdIf::CmdAvSetDemultiplexerInput(uint8_t DemultiplexerIndex, uint8_t TsInputIndex) -{ - HdffCmdAvSetDemultiplexerInput(mOsdDev, DemultiplexerIndex, TsInputIndex); -} - -void cHdffCmdIf::CmdAvSetVideoFormat(uint8_t DecoderIndex, const HdffVideoFormat_t * pVideoFormat) -{ - HdffCmdAvSetVideoFormat(mOsdDev, DecoderIndex, pVideoFormat); -} - -void cHdffCmdIf::CmdAvSetVideoOutputMode(uint8_t DecoderIndex, HdffVideoOutputMode_t OutputMode) -{ - HdffCmdAvSetVideoOutputMode(mOsdDev, DecoderIndex, OutputMode); -} - -void cHdffCmdIf::CmdAvSetStc(uint8_t DecoderIndex, uint64_t Stc) -{ - HdffCmdAvSetStc(mOsdDev, DecoderIndex, Stc); -} - -void cHdffCmdIf::CmdAvFlushBuffer(uint8_t DecoderIndex, bool FlushAudio, bool FlushVideo) -{ - HdffCmdAvFlushBuffer(mOsdDev, DecoderIndex, FlushAudio, FlushVideo); -} - -void cHdffCmdIf::CmdAvEnableSync(uint8_t DecoderIndex, bool EnableSync) -{ - HdffCmdAvEnableSync(mOsdDev, DecoderIndex, EnableSync, EnableSync); -} - -void cHdffCmdIf::CmdAvSetVideoSpeed(uint8_t DecoderIndex, int32_t Speed) -{ - HdffCmdAvSetVideoSpeed(mOsdDev, DecoderIndex, Speed); -} - -void cHdffCmdIf::CmdAvSetAudioSpeed(uint8_t DecoderIndex, int32_t Speed) -{ - HdffCmdAvSetAudioSpeed(mOsdDev, DecoderIndex, Speed); -} - -void cHdffCmdIf::CmdAvEnableVideoAfterStop(uint8_t DecoderIndex, bool EnableVideoAfterStop) -{ - HdffCmdAvEnableVideoAfterStop(mOsdDev, DecoderIndex, EnableVideoAfterStop); -} - -void cHdffCmdIf::CmdAvSetAudioDelay(int16_t Delay) -{ - HdffCmdAvSetAudioDelay(mOsdDev, Delay); -} - -void cHdffCmdIf::CmdAvSetAudioDownmix(HdffAudioDownmixMode_t DownmixMode) -{ - HdffCmdAvSetAudioDownmix(mOsdDev, DownmixMode); -} - -void cHdffCmdIf::CmdAvSetAudioChannel(uint8_t AudioChannel) -{ - HdffCmdAvSetAudioChannel(mOsdDev, AudioChannel); -} - -void cHdffCmdIf::CmdAvSetSyncShift(int16_t SyncShift) -{ - HdffCmdAvSetSyncShift(mOsdDev, SyncShift); -} - -void cHdffCmdIf::CmdAvMuteAudio(uint8_t DecoderIndex, bool Mute) -{ - HdffCmdAvMuteAudio(mOsdDev, DecoderIndex, Mute); -} - -void cHdffCmdIf::CmdOsdConfigure(const HdffOsdConfig_t * pConfig) -{ - HdffCmdOsdConfigure(mOsdDev, pConfig); -} - -void cHdffCmdIf::CmdOsdReset(void) -{ - HdffCmdOsdReset(mOsdDev); -} - -uint32_t cHdffCmdIf::CmdOsdCreateDisplay(uint32_t Width, uint32_t Height, HdffColorType_t ColorType) -{ - //printf("CreateDisplay %d %d %d\n", Width, Height, ColorType); - uint32_t newDisplay; - - if (HdffCmdOsdCreateDisplay(mOsdDev, Width, Height, ColorType, &newDisplay) == 0) - return newDisplay; - LOG_ERROR_STR("Error creating display"); - return HDFF_INVALID_HANDLE; -} - -void cHdffCmdIf::CmdOsdDeleteDisplay(uint32_t hDisplay) -{ - //printf("DeleteDisplay\n"); - HdffCmdOsdDeleteDisplay(mOsdDev, hDisplay); -} - -void cHdffCmdIf::CmdOsdEnableDisplay(uint32_t hDisplay, bool Enable) -{ - //printf("EnableDisplay\n"); - HdffCmdOsdEnableDisplay(mOsdDev, hDisplay, Enable); -} - -void cHdffCmdIf::CmdOsdSetDisplayOutputRectangle(uint32_t hDisplay, uint32_t X, uint32_t Y, uint32_t Width, uint32_t Height) -{ - //printf("SetOutputRect %d %d %d %d %d\n", hDisplay, X, Y, Width, Height); - HdffCmdOsdSetDisplayOutputRectangle(mOsdDev, hDisplay, X, Y, Width, Height); -} - -void cHdffCmdIf::CmdOsdSetDisplayClippingArea(uint32_t hDisplay, bool Enable, uint32_t X, uint32_t Y, uint32_t Width, uint32_t Height) -{ - //printf("SetClippingArea %d %d %d %d %d %d\n", hDisplay, Enable, X, Y, Width, Height); - HdffCmdOsdSetDisplayClippingArea(mOsdDev, hDisplay, Enable, X, Y, Width, Height); -} - -void cHdffCmdIf::CmdOsdRenderDisplay(uint32_t hDisplay) -{ - //printf("Render %08X\n", hDisplay); - HdffCmdOsdRenderDisplay(mOsdDev, hDisplay); -} - -uint32_t cHdffCmdIf::CmdOsdCreatePalette(HdffColorType_t ColorType, HdffColorFormat_t ColorFormat, - uint32_t NumColors, const uint32_t * pColors) -{ - uint32_t newPalette; - int err; - - err = HdffCmdOsdCreatePalette(mOsdDev, ColorType, ColorFormat, NumColors, - pColors, &newPalette); - if (err == 0) - return newPalette; - LOG_ERROR_STR("Error creating palette"); - return HDFF_INVALID_HANDLE; -} - -void cHdffCmdIf::CmdOsdDeletePalette(uint32_t hPalette) -{ - HdffCmdOsdDeletePalette(mOsdDev, hPalette); -} - -void cHdffCmdIf::CmdOsdSetDisplayPalette(uint32_t hDisplay, uint32_t hPalette) -{ - HdffCmdOsdSetDisplayPalette(mOsdDev, hDisplay, hPalette); -} - -void cHdffCmdIf::CmdOsdSetPaletteColors(uint32_t hPalette, HdffColorFormat_t ColorFormat, - uint8_t StartColor, uint32_t NumColors, const uint32_t * pColors) -{ - HdffCmdOsdSetPaletteColors(mOsdDev, hPalette, ColorFormat, StartColor, - NumColors, pColors); -} - -uint32_t cHdffCmdIf::CmdOsdCreateFontFace(const uint8_t * pFontData, uint32_t DataSize) -{ - //printf("CreateFontFace %d\n", DataSize); - uint32_t newFontFace; - int err; - - err = HdffCmdOsdCreateFontFace(mOsdDev, pFontData, DataSize, &newFontFace); - if (err == 0) - return newFontFace; - LOG_ERROR_STR("Error creating font face"); - return HDFF_INVALID_HANDLE; -} - -void cHdffCmdIf::CmdOsdDeleteFontFace(uint32_t hFontFace) -{ - //printf("DeleteFontFace %08X\n", hFontFace); - HdffCmdOsdDeleteFontFace(mOsdDev, hFontFace); -} - -uint32_t cHdffCmdIf::CmdOsdCreateFont(uint32_t hFontFace, uint32_t Size) -{ - //printf("CreateFont %d\n", Size); - uint32_t newFont; - int err; - - err = HdffCmdOsdCreateFont(mOsdDev, hFontFace, Size, &newFont); - if (err == 0) - return newFont; - LOG_ERROR_STR("Error creating font"); - return HDFF_INVALID_HANDLE; -} - -void cHdffCmdIf::CmdOsdDeleteFont(uint32_t hFont) -{ - //printf("DeleteFont %08X\n", hFont); - HdffCmdOsdDeleteFont(mOsdDev, hFont); -} - -void cHdffCmdIf::CmdOsdDrawRectangle(uint32_t hDisplay, int X, int Y, int Width, int Height, uint32_t Color) -{ - //printf("Rect (%d,%d) %d x %d, %08X\n", X, Y, Width, Height, Color); - HdffCmdOsdDrawRectangle(mOsdDev, hDisplay, X, Y, Width, Height, Color); -} - -void cHdffCmdIf::CmdOsdDrawEllipse(uint32_t hDisplay, int CX, int CY, int RadiusX, int RadiusY, - uint32_t Color, uint32_t Flags) -{ - //printf("Ellipse (%d,%d) %d x %d, %08X, %d\n", CX, CY, RadiusX, RadiusY, Color, Flags); - HdffCmdOsdDrawEllipse(mOsdDev, hDisplay, CX, CY, RadiusX, RadiusY, Color, Flags); -} - -void cHdffCmdIf::CmdOsdDrawSlope(uint32_t hDisplay, int X, int Y, int Width, int Height, - uint32_t Color, uint32_t Type) -{ - //printf("Slope (%d,%d) %d x %d, %08X, %X\n", X, Y, Width, Height, Color, Type); - HdffCmdOsdDrawSlope(mOsdDev, hDisplay, X, Y, Width, Height, Color, Type); -} - -void cHdffCmdIf::CmdOsdDrawText(uint32_t hDisplay, uint32_t hFont, int X, int Y, const char * pText, uint32_t Color) -{ - //printf("Text %08X (%d,%d), %s, %08X\n", hFont, X, Y, pText, Color); - HdffCmdOsdDrawText(mOsdDev, hDisplay, hFont, X, Y, pText, Color); -} - -void cHdffCmdIf::CmdOsdDrawUtf8Text(uint32_t hDisplay, uint32_t hFont, int X, int Y, const char * pText, uint32_t Color) -{ - //printf("Text(UTF8) %08X (%d,%d), %s, %08X\n", hFont, X, Y, pText, Color); - HdffCmdOsdDrawUtf8Text(mOsdDev, hDisplay, hFont, X, Y, pText, Color); -} - -void cHdffCmdIf::CmdOsdDrawTextW(uint32_t hDisplay, uint32_t hFont, int X, int Y, const uint16_t * pText, uint32_t Color) -{ - //printf("TextW %08X (%d,%d), %08X\n", hFont, X, Y, Color); - HdffCmdOsdDrawWideText(mOsdDev, hDisplay, hFont, X, Y, pText, Color); -} - -void cHdffCmdIf::CmdOsdDrawBitmap(uint32_t hDisplay, int X, int Y, const uint8_t * pBitmap, - int BmpWidth, int BmpHeight, int BmpSize, - HdffColorType_t ColorType, uint32_t hPalette) -{ - //printf("Bitmap %08X (%d,%d) %d x %d, %08X\n", hDisplay, X, Y, BmpWidth, BmpHeight, hPalette); - HdffCmdOsdDrawBitmap(mOsdDev, hDisplay, X, Y, pBitmap, BmpWidth, BmpHeight, - BmpSize, ColorType, hPalette); -} - -void cHdffCmdIf::CmdOsdSaveRegion(uint32_t hDisplay, int X, int Y, int Width, int Height) -{ - HdffCmdOsdSaveRegion(mOsdDev, hDisplay, X, Y, Width, Height); -} - -void cHdffCmdIf::CmdOsdRestoreRegion(uint32_t hDisplay) -{ - HdffCmdOsdRestoreRegion(mOsdDev, hDisplay); -} - -void cHdffCmdIf::CmdMuxSetVideoOut(HdffVideoOut_t VideoOut) -{ - HdffCmdMuxSetVideoOut(mOsdDev, VideoOut); -} - -void cHdffCmdIf::CmdMuxSetVolume(uint8_t Volume) -{ - HdffCmdMuxSetVolume(mOsdDev, Volume); -} - -void cHdffCmdIf::CmdMuxMuteAudio(bool Mute) -{ - HdffCmdMuxMuteAudio(mOsdDev, Mute); -} - -void cHdffCmdIf::CmdHdmiSetVideoMode(HdffVideoMode_t VideoMode) -{ - //printf("HdmiSetVideoMode %d\n", VideoMode); - HdffCmdHdmiSetVideoMode(mOsdDev, VideoMode); -} - -void cHdffCmdIf::CmdHdmiConfigure(const HdffHdmiConfig_t * pConfig) -{ - HdffCmdHdmiConfigure(mOsdDev, pConfig); -} - -void cHdffCmdIf::CmdHdmiSendCecCommand(HdffCecCommand_t Command) -{ - HdffCmdHdmiSendCecCommand(mOsdDev, Command); -} - -void cHdffCmdIf::CmdRemoteSetProtocol(HdffRemoteProtocol_t Protocol) -{ - //printf("%s %d\n", __func__, Protocol); - HdffCmdRemoteSetProtocol(mOsdDev, Protocol); -} - -void cHdffCmdIf::CmdRemoteSetAddressFilter(bool Enable, uint32_t Address) -{ - //printf("%s %d %d\n", __func__, Enable, Address); - HdffCmdRemoteSetAddressFilter(mOsdDev, Enable, Address); -} - -} // end of namespace diff --git a/PLUGINS/src/dvbhddevice/hdffcmd.h b/PLUGINS/src/dvbhddevice/hdffcmd.h deleted file mode 100644 index 891bc7e8..00000000 --- a/PLUGINS/src/dvbhddevice/hdffcmd.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * hdffcmd.h: TODO(short description) - * - * See the README file for copyright information and how to reach the author. - */ - -#ifndef _HDFF_CMD_H_ -#define _HDFF_CMD_H_ - -#include "libhdffcmd/hdffcmd.h" - -namespace HDFF -{ - -class cHdffCmdIf -{ -private: - int mOsdDev; - -public: - cHdffCmdIf(int OsdDev); - ~cHdffCmdIf(void); - - uint32_t CmdGetFirmwareVersion(char * pString, uint32_t MaxLength); - uint32_t CmdGetInterfaceVersion(char * pString, uint32_t MaxLength); - uint32_t CmdGetCopyrights(uint8_t Index, char * pString, uint32_t MaxLength); - - void CmdAvSetPlayMode(uint8_t PlayMode, bool Realtime); - void CmdAvSetVideoPid(uint8_t DecoderIndex, uint16_t VideoPid, HdffVideoStreamType_t StreamType, bool PlaybackMode = false); - void CmdAvSetAudioPid(uint8_t DecoderIndex, uint16_t AudioPid, HdffAudioStreamType_t StreamType, HdffAvContainerType_t ContainerType = HDFF_AV_CONTAINER_PES); - void CmdAvSetPcrPid(uint8_t DecoderIndex, uint16_t PcrPid); - void CmdAvSetTeletextPid(uint8_t DecoderIndex, uint16_t TeletextPid); - void CmdAvSetVideoWindow(uint8_t DecoderIndex, bool Enable, uint16_t X, uint16_t Y, uint16_t Width, uint16_t Height); - void CmdAvShowStillImage(uint8_t DecoderIndex, const uint8_t * pStillImage, int Size, HdffVideoStreamType_t StreamType); - void CmdAvSetDecoderInput(uint8_t DecoderIndex, uint8_t DemultiplexerIndex); - void CmdAvSetDemultiplexerInput(uint8_t DemultiplexerIndex, uint8_t TsInputIndex); - void CmdAvSetVideoFormat(uint8_t DecoderIndex, const HdffVideoFormat_t * pVideoFormat); - void CmdAvSetVideoOutputMode(uint8_t DecoderIndex, HdffVideoOutputMode_t OutputMode); - void CmdAvSetStc(uint8_t DecoderIndex, uint64_t Stc); - void CmdAvFlushBuffer(uint8_t DecoderIndex, bool FlushAudio, bool FlushVideo); - void CmdAvEnableSync(uint8_t DecoderIndex, bool EnableSync); - void CmdAvSetVideoSpeed(uint8_t DecoderIndex, int32_t Speed); - void CmdAvSetAudioSpeed(uint8_t DecoderIndex, int32_t Speed); - void CmdAvEnableVideoAfterStop(uint8_t DecoderIndex, bool EnableVideoAfterStop); - void CmdAvSetAudioDelay(int16_t Delay); - void CmdAvSetAudioDownmix(HdffAudioDownmixMode_t DownmixMode); - void CmdAvSetAudioChannel(uint8_t AudioChannel); - void CmdAvSetSyncShift(int16_t SyncShift); - void CmdAvMuteAudio(uint8_t DecoderIndex, bool Mute); - - void CmdOsdConfigure(const HdffOsdConfig_t * pConfig); - void CmdOsdReset(void); - - uint32_t CmdOsdCreateDisplay(uint32_t Width, uint32_t Height, HdffColorType_t ColorType); - void CmdOsdDeleteDisplay(uint32_t hDisplay); - void CmdOsdEnableDisplay(uint32_t hDisplay, bool Enable); - void CmdOsdSetDisplayOutputRectangle(uint32_t hDisplay, uint32_t X, uint32_t Y, uint32_t Width, uint32_t Height); - void CmdOsdSetDisplayClippingArea(uint32_t hDisplay, bool Enable, uint32_t X, uint32_t Y, uint32_t Width, uint32_t Height); - void CmdOsdRenderDisplay(uint32_t hDisplay); - - uint32_t CmdOsdCreatePalette(HdffColorType_t ColorType, HdffColorFormat_t ColorFormat, - uint32_t NumColors, const uint32_t * pColors); - void CmdOsdDeletePalette(uint32_t hPalette); - void CmdOsdSetDisplayPalette(uint32_t hDisplay, uint32_t hPalette); - void CmdOsdSetPaletteColors(uint32_t hPalette, HdffColorFormat_t ColorFormat, - uint8_t StartColor, uint32_t NumColors, const uint32_t * pColors); - - uint32_t CmdOsdCreateFontFace(const uint8_t * pFontData, uint32_t DataSize); - void CmdOsdDeleteFontFace(uint32_t hFontFace); - uint32_t CmdOsdCreateFont(uint32_t hFontFace, uint32_t Size); - void CmdOsdDeleteFont(uint32_t hFont); - - void CmdOsdDrawRectangle(uint32_t hDisplay, int X, int Y, int Width, int Height, uint32_t Color); - void CmdOsdDrawEllipse(uint32_t hDisplay, int CX, int CY, int RadiusX, int RadiusY, - uint32_t Color, uint32_t Flags); - void CmdOsdDrawSlope(uint32_t hDisplay, int X, int Y, int Width, int Height, uint32_t Color, uint32_t Type); - void CmdOsdDrawText(uint32_t hDisplay, uint32_t hFont, int X, int Y, const char * pText, uint32_t Color); - void CmdOsdDrawUtf8Text(uint32_t hDisplay, uint32_t hFont, int X, int Y, const char * pText, uint32_t Color); - void CmdOsdDrawTextW(uint32_t hDisplay, uint32_t hFont, int X, int Y, const uint16_t * pText, uint32_t Color); - void CmdOsdDrawBitmap(uint32_t hDisplay, int X, int Y, const uint8_t * pBitmap, - int BmpWidth, int BmpHeight, int BmpSize, - HdffColorType_t ColorType, uint32_t hPalette); - void CmdOsdSaveRegion(uint32_t hDisplay, int X, int Y, int Width, int Height); - void CmdOsdRestoreRegion(uint32_t hDisplay); - - void CmdMuxSetVideoOut(HdffVideoOut_t VideoOut); - void CmdMuxSetVolume(uint8_t Volume); - void CmdMuxMuteAudio(bool Mute); - - void CmdHdmiSetVideoMode(HdffVideoMode_t VideoMode); - void CmdHdmiConfigure(const HdffHdmiConfig_t * pConfig); - void CmdHdmiSendCecCommand(HdffCecCommand_t Command); - - void CmdRemoteSetProtocol(HdffRemoteProtocol_t Protocol); - void CmdRemoteSetAddressFilter(bool Enable, uint32_t Address); -}; - -} // end of namespace - -#endif diff --git a/PLUGINS/src/dvbhddevice/hdffosd.c b/PLUGINS/src/dvbhddevice/hdffosd.c deleted file mode 100644 index 3ce28bfa..00000000 --- a/PLUGINS/src/dvbhddevice/hdffosd.c +++ /dev/null @@ -1,836 +0,0 @@ -/* - * hdffosd.c: Implementation of the DVB HD Full Featured On Screen Display - * - * See the README file for copyright information and how to reach the author. - */ - -#include "hdffosd.h" -#include -#include -#include -#include "hdffcmd.h" -#include "setup.h" - -#define MAX_NUM_FONTFACES 8 -#define MAX_NUM_FONTS 8 -#define MAX_BITMAP_SIZE (1024*1024) - -typedef struct _tFontFace -{ - cString Name; - uint32_t Handle; -} tFontFace; - -typedef struct _tFont -{ - uint32_t hFontFace; - int Size; - uint32_t Handle; -} tFont; - -class cHdffOsd : public cOsd -{ -private: - HDFF::cHdffCmdIf * mHdffCmdIf; - int mLeft; - int mTop; - int mDispWidth; - int mDispHeight; - bool mChanged; - uint32_t mDisplay; - tFontFace mFontFaces[MAX_NUM_FONTFACES]; - tFont mFonts[MAX_NUM_FONTS]; - uint32_t mBitmapPalette; - uint32_t mBitmapColors[256]; - - bool mSupportsUtf8Text; - -protected: - virtual void SetActive(bool On); -public: - cHdffOsd(int Left, int Top, HDFF::cHdffCmdIf * pHdffCmdIf, uint Level); - virtual ~cHdffOsd(); - virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas); - virtual eOsdError SetAreas(const tArea *Areas, int NumAreas); - virtual void SaveRegion(int x1, int y1, int x2, int y2); - virtual void RestoreRegion(void); - virtual void DrawPixel(int x, int y, tColor Color); - virtual void DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg = 0, tColor ColorBg = 0, bool ReplacePalette = false, bool Overlay = false); - virtual void DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width = 0, int Height = 0, int Alignment = taDefault); - virtual void DrawRectangle(int x1, int y1, int x2, int y2, tColor Color); - virtual void DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants = 0); - virtual void DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type); - virtual void Flush(void); -}; - -cHdffOsd::cHdffOsd(int Left, int Top, HDFF::cHdffCmdIf * pHdffCmdIf, uint Level) -: cOsd(Left, Top, Level) -{ - double pixelAspect; - HdffOsdConfig_t config; - - //printf("cHdffOsd %d, %d, %d\n", Left, Top, Level); - mHdffCmdIf = pHdffCmdIf; - mLeft = Left; - mTop = Top; - mChanged = false; - mBitmapPalette = HDFF_INVALID_HANDLE; - - mSupportsUtf8Text = false; - if (mHdffCmdIf->CmdGetFirmwareVersion(NULL, 0) >= 0x309) - mSupportsUtf8Text = true; - - memset(&config, 0, sizeof(config)); - config.FontKerning = true; - config.FontAntialiasing = Setup.AntiAlias ? true : false; - mHdffCmdIf->CmdOsdConfigure(&config); - - gHdffSetup.GetOsdSize(mDispWidth, mDispHeight, pixelAspect); - mDisplay = mHdffCmdIf->CmdOsdCreateDisplay(mDispWidth, mDispHeight, HDFF_COLOR_TYPE_ARGB8888); - mHdffCmdIf->CmdOsdSetDisplayOutputRectangle(mDisplay, 0, 0, HDFF_SIZE_FULL_SCREEN, HDFF_SIZE_FULL_SCREEN); - for (int i = 0; i < MAX_NUM_FONTFACES; i++) - { - mFontFaces[i].Name = ""; - mFontFaces[i].Handle = HDFF_INVALID_HANDLE; - } - for (int i = 0; i < MAX_NUM_FONTS; i++) - { - mFonts[i].hFontFace = HDFF_INVALID_HANDLE; - mFonts[i].Size = 0; - mFonts[i].Handle = HDFF_INVALID_HANDLE; - } -} - -cHdffOsd::~cHdffOsd() -{ - //printf("~cHdffOsd %d %d\n", mLeft, mTop); - if (Active()) { - mHdffCmdIf->CmdOsdDrawRectangle(mDisplay, 0, 0, mDispWidth, mDispHeight, 0); - mHdffCmdIf->CmdOsdRenderDisplay(mDisplay); - } - SetActive(false); - - for (int i = 0; i < MAX_NUM_FONTS; i++) - { - if (mFonts[i].Handle == HDFF_INVALID_HANDLE) - break; - mHdffCmdIf->CmdOsdDeleteFont(mFonts[i].Handle); - } - for (int i = 0; i < MAX_NUM_FONTFACES; i++) - { - if (mFontFaces[i].Handle == HDFF_INVALID_HANDLE) - break; - mHdffCmdIf->CmdOsdDeleteFontFace(mFontFaces[i].Handle); - } - - if (mBitmapPalette != HDFF_INVALID_HANDLE) - mHdffCmdIf->CmdOsdDeletePalette(mBitmapPalette); - mHdffCmdIf->CmdOsdDeleteDisplay(mDisplay); -} - -eOsdError cHdffOsd::CanHandleAreas(const tArea *Areas, int NumAreas) -{ - eOsdError Result = cOsd::CanHandleAreas(Areas, NumAreas); - if (Result == oeOk) - { - for (int i = 0; i < NumAreas; i++) - { - if (Areas[i].bpp != 1 && Areas[i].bpp != 2 && Areas[i].bpp != 4 && Areas[i].bpp != 8) - return oeBppNotSupported; - } - } - return Result; -} - -eOsdError cHdffOsd::SetAreas(const tArea *Areas, int NumAreas) -{ - eOsdError error; - cBitmap * bitmap; - - for (int i = 0; i < NumAreas; i++) - { - //printf("SetAreas %d: %d %d %d %d %d\n", i, Areas[i].x1, Areas[i].y1, Areas[i].x2, Areas[i].y2, Areas[i].bpp); - } - if (Active() && mDisplay != HDFF_INVALID_HANDLE) - { - mHdffCmdIf->CmdOsdDrawRectangle(mDisplay, 0, 0, mDispWidth, mDispHeight, 0); - mHdffCmdIf->CmdOsdRenderDisplay(mDisplay); - } - error = cOsd::SetAreas(Areas, NumAreas); - - for (int i = 0; (bitmap = GetBitmap(i)) != NULL; i++) - { - bitmap->Clean(); - } - - return error; -} - -void cHdffOsd::SetActive(bool On) -{ - if (On != Active()) - { - cOsd::SetActive(On); - if (On) - { - if (GetBitmap(0)) // only flush here if there are already bitmaps - Flush(); - } - else if (mDisplay != HDFF_INVALID_HANDLE) - { - mHdffCmdIf->CmdOsdDrawRectangle(mDisplay, 0, 0, mDispWidth, mDispHeight, 0); - mHdffCmdIf->CmdOsdRenderDisplay(mDisplay); - } - } -} - -void cHdffOsd::SaveRegion(int x1, int y1, int x2, int y2) -{ - mHdffCmdIf->CmdOsdSaveRegion(mDisplay, mLeft + x1, mTop + y1, x2 - x1 + 1, y2 - y1 + 1); - mChanged = true; -} - -void cHdffOsd::RestoreRegion(void) -{ - mHdffCmdIf->CmdOsdRestoreRegion(mDisplay); - mChanged = true; -} - -void cHdffOsd::DrawPixel(int x, int y, tColor Color) -{ - //printf("DrawPixel\n"); -} - -void cHdffOsd::DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg, bool ReplacePalette, bool Overlay) -{ - //printf("DrawBitmap %d %d %d x %d\n", x, y, Bitmap.Width(), Bitmap.Height()); - int i; - int numColors; - const tColor * colors = Bitmap.Colors(numColors); - - for (i = 0; i < numColors; i++) - { - mBitmapColors[i] = colors[i]; - if (ColorFg || ColorBg) - { - if (i == 0) - mBitmapColors[i] = ColorBg; - else if (i == 1) - mBitmapColors[i] = ColorFg; - } - } - if (mBitmapPalette == HDFF_INVALID_HANDLE) - { - mBitmapPalette = mHdffCmdIf->CmdOsdCreatePalette(HDFF_COLOR_TYPE_CLUT8, - HDFF_COLOR_FORMAT_ARGB, numColors, mBitmapColors); - } - else - { - mHdffCmdIf->CmdOsdSetPaletteColors(mBitmapPalette, - HDFF_COLOR_FORMAT_ARGB, 0, numColors, mBitmapColors); - } - int width = Bitmap.Width(); - int height = Bitmap.Height(); - int chunk = MAX_BITMAP_SIZE / width; - if (chunk > height) - chunk = height; - for (int yc = 0; yc < height; yc += chunk) - { - int hc = chunk; - if (yc + hc > height) - hc = height - yc; - mHdffCmdIf->CmdOsdDrawBitmap(mDisplay, mLeft + x, mTop + y + yc, - (uint8_t *) Bitmap.Data(0, yc), width, hc, - width * hc, HDFF_COLOR_TYPE_CLUT8, mBitmapPalette); - } - mChanged = true; -} - -void cHdffOsd::DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment) -{ - int w = Font->Width(s); - int h = Font->Height(); - int cw = Width ? Width : w; - int ch = Height ? Height : h; - int i; - int size = Font->Size(); - tFontFace * pFontFace; - tFont * pFont; - - if (ColorBg != clrTransparent) - mHdffCmdIf->CmdOsdDrawRectangle(mDisplay, mLeft + x, mTop + y, cw, ch, ColorBg); - - if (s == NULL) - return; - - pFontFace = NULL; - for (i = 0; i < MAX_NUM_FONTFACES; i++) - { - if (mFontFaces[i].Handle == HDFF_INVALID_HANDLE) - break; - - if (strcmp(mFontFaces[i].Name, Font->FontName()) == 0) - { - pFontFace = &mFontFaces[i]; - break; - } - } - if (pFontFace == NULL) - { - if (i < MAX_NUM_FONTFACES) - { - cString fontFileName = Font->FontName(); - FILE * fp = fopen(fontFileName, "rb"); - if (fp) - { - fseek(fp, 0, SEEK_END); - long fileSize = ftell(fp); - fseek(fp, 0, SEEK_SET); - if (fileSize > 0) - { - uint8_t * buffer = new uint8_t[fileSize]; - if (buffer) - { - if (fread(buffer, fileSize, 1, fp) == 1) - { - mFontFaces[i].Handle = mHdffCmdIf->CmdOsdCreateFontFace(buffer, fileSize); - if (mFontFaces[i].Handle != HDFF_INVALID_HANDLE) - { - mFontFaces[i].Name = Font->FontName(); - pFontFace = &mFontFaces[i]; - } - } - delete[] buffer; - } - } - fclose(fp); - } - } - } - if (pFontFace == NULL) - return; - - pFont = NULL; - for (i = 0; i < MAX_NUM_FONTS; i++) - { - if (mFonts[i].Handle == HDFF_INVALID_HANDLE) - break; - - if (mFonts[i].hFontFace == pFontFace->Handle - && mFonts[i].Size == size) - { - pFont = &mFonts[i]; - break; - } - } - if (pFont == NULL) - { - if (i < MAX_NUM_FONTS) - { - mFonts[i].Handle = mHdffCmdIf->CmdOsdCreateFont(pFontFace->Handle, size); - if (mFonts[i].Handle != HDFF_INVALID_HANDLE) - { - mFonts[i].hFontFace = pFontFace->Handle; - mFonts[i].Size = size; - pFont = &mFonts[i]; - } - } - } - if (pFont == NULL) - return; - - mHdffCmdIf->CmdOsdSetDisplayClippingArea(mDisplay, true, mLeft + x, mTop + y, cw, ch); - - if (Width || Height) - { - if (Width) - { - if ((Alignment & taLeft) != 0) - { -#if (APIVERSNUM >= 10728) - if ((Alignment & taBorder) != 0) - x += max(h / TEXT_ALIGN_BORDER, 1); -#endif - } - else if ((Alignment & taRight) != 0) - { - if (w < Width) - x += Width - w; -#if (APIVERSNUM >= 10728) - if ((Alignment & taBorder) != 0) - x -= max(h / TEXT_ALIGN_BORDER, 1); -#endif - } - else - { // taCentered - if (w < Width) - x += (Width - w) / 2; - } - } - if (Height) - { - if ((Alignment & taTop) != 0) - ; - else if ((Alignment & taBottom) != 0) - { - if (h < Height) - y += Height - h; - } - else - { // taCentered - if (h < Height) - y += (Height - h) / 2; - } - } - } -#if 0 - if (mSupportsUtf8Text) - { - mHdffCmdIf->CmdOsdDrawUtf8Text(mDisplay, pFont->Handle, x + mLeft, y + mTop + h, s, ColorFg); - } - else -#endif - { - uint16_t tmp[1000]; - uint16_t len = 0; - while (*s && (len < (sizeof(tmp) - 1))) - { - int sl = Utf8CharLen(s); - uint sym = Utf8CharGet(s, sl); - s += sl; - tmp[len] = sym; - len++; - } - tmp[len] = 0; - mHdffCmdIf->CmdOsdDrawTextW(mDisplay, pFont->Handle, x + mLeft, y + mTop + h, tmp, ColorFg); - } - mHdffCmdIf->CmdOsdSetDisplayClippingArea(mDisplay, false, 0, 0, 0, 0); - mChanged = true; -} - -void cHdffOsd::DrawRectangle(int x1, int y1, int x2, int y2, tColor Color) -{ - mHdffCmdIf->CmdOsdDrawRectangle(mDisplay, mLeft + x1, mTop + y1, x2 - x1 + 1, y2 - y1 + 1, Color); - mChanged = true; -} - -void cHdffOsd::DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants) -{ - uint32_t flags; - int cx; - int cy; - int rx; - int ry; - - switch (abs(Quadrants)) - { - case 1: - if (Quadrants > 0) - flags = HDFF_DRAW_QUARTER_TOP_RIGHT; - else - flags = HDFF_DRAW_QUARTER_TOP_RIGHT_INVERTED; - cx = x1; - cy = y2; - rx = x2 - x1; - ry = y2 - y1; - break; - case 2: - if (Quadrants > 0) - flags = HDFF_DRAW_QUARTER_TOP_LEFT; - else - flags = HDFF_DRAW_QUARTER_TOP_LEFT_INVERTED; - cx = x2; - cy = y2; - rx = x2 - x1; - ry = y2 - y1; - break; - case 3: - if (Quadrants > 0) - flags = HDFF_DRAW_QUARTER_BOTTOM_LEFT; - else - flags = HDFF_DRAW_QUARTER_BOTTOM_LEFT_INVERTED; - cx = x2; - cy = y1; - rx = x2 - x1; - ry = y2 - y1; - break; - case 4: - if (Quadrants > 0) - flags = HDFF_DRAW_QUARTER_BOTTOM_RIGHT; - else - flags = HDFF_DRAW_QUARTER_BOTTOM_RIGHT_INVERTED; - cx = x1; - cy = y1; - rx = x2 - x1; - ry = y2 - y1; - break; - case 5: - flags = HDFF_DRAW_HALF_RIGHT; - cx = x1; - cy = (y1 + y2) / 2; - rx = x2 - x1; - ry = (y2 - y1) / 2; - break; - case 6: - flags = HDFF_DRAW_HALF_TOP; - cx = (x1 + x2) / 2; - cy = y2; - rx = (x2 - x1) / 2; - ry = y2 - y1; - break; - case 7: - flags = HDFF_DRAW_HALF_LEFT; - cx = x2; - cy = (y1 + y2) / 2; - rx = x2 - x1; - ry = (y2 - y1) / 2; - break; - case 8: - flags = HDFF_DRAW_HALF_BOTTOM; - cx = (x1 + x2) / 2; - cy = y1; - rx = (x2 - x1) / 2; - ry = y2 - y1; - break; - default: - flags = HDFF_DRAW_FULL; - cx = (x1 + x2) / 2; - cy = (y1 + y2) / 2; - rx = (x2 - x1) / 2; - ry = (y2 - y1) / 2; - break; - } - mHdffCmdIf->CmdOsdDrawEllipse(mDisplay, mLeft + cx, mTop + cy, rx, ry, Color, flags); - mChanged = true; -} - -void cHdffOsd::DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type) -{ - //printf("DrawSlope\n"); - mHdffCmdIf->CmdOsdDrawSlope(mDisplay, mLeft + x1, mTop + y1, - x2 - x1 + 1, y2 - y1 + 1, Color, Type); - mChanged = true; -} - -void cHdffOsd::Flush(void) -{ - if (!Active()) - return; - - //printf("Flush\n"); - cBitmap * Bitmap; - - for (int i = 0; (Bitmap = GetBitmap(i)) != NULL; i++) - { - int x1; - int y1; - int x2; - int y2; - - if (Bitmap->Dirty(x1, y1, x2, y2)) - { - //printf("dirty %d %d, %d %d\n", x1, y1, x2, y2); - DrawBitmap(0, 0, *Bitmap); - Bitmap->Clean(); - } - } - - if (!mChanged) - return; - - mHdffCmdIf->CmdOsdRenderDisplay(mDisplay); - - mChanged = false; -} - - -class cHdffOsdRaw : public cOsd -{ -private: - HDFF::cHdffCmdIf * mHdffCmdIf; - int mDispWidth; - int mDispHeight; - bool refresh; - uint32_t mDisplay; - uint32_t mBitmapPalette; - uint32_t mBitmapColors[256]; - -protected: - virtual void SetActive(bool On); -public: - cHdffOsdRaw(int Left, int Top, HDFF::cHdffCmdIf * pHdffCmdIf, uint Level); - virtual ~cHdffOsdRaw(); - virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas); - virtual eOsdError SetAreas(const tArea *Areas, int NumAreas); - virtual void Flush(void); -}; - -cHdffOsdRaw::cHdffOsdRaw(int Left, int Top, HDFF::cHdffCmdIf * pHdffCmdIf, uint Level) -: cOsd(Left, Top, Level) -{ - double pixelAspect; - - //printf("cHdffOsdRaw %d, %d, %d\n", Left, Top, Level); - mHdffCmdIf = pHdffCmdIf; - refresh = true; - mBitmapPalette = HDFF_INVALID_HANDLE; - mDisplay = HDFF_INVALID_HANDLE; - - gHdffSetup.GetOsdSize(mDispWidth, mDispHeight, pixelAspect); -} - -cHdffOsdRaw::~cHdffOsdRaw() -{ - //printf("~cHdffOsdRaw %d %d\n", Left(), Top()); - if (mDisplay != HDFF_INVALID_HANDLE) - { - mHdffCmdIf->CmdOsdDrawRectangle(mDisplay, 0, 0, mDispWidth, mDispHeight, 0); - mHdffCmdIf->CmdOsdRenderDisplay(mDisplay); - } - if (mBitmapPalette != HDFF_INVALID_HANDLE) - mHdffCmdIf->CmdOsdDeletePalette(mBitmapPalette); - mBitmapPalette = HDFF_INVALID_HANDLE; - if (mDisplay != HDFF_INVALID_HANDLE) - mHdffCmdIf->CmdOsdDeleteDisplay(mDisplay); - mDisplay = HDFF_INVALID_HANDLE; -} - -void cHdffOsdRaw::SetActive(bool On) -{ - if (On != Active()) - { - cOsd::SetActive(On); - if (On) - { - if (mDisplay == HDFF_INVALID_HANDLE) - { - mDisplay = mHdffCmdIf->CmdOsdCreateDisplay(mDispWidth, mDispHeight, HDFF_COLOR_TYPE_ARGB8888); - if (mDisplay != HDFF_INVALID_HANDLE) - mHdffCmdIf->CmdOsdSetDisplayOutputRectangle(mDisplay, 0, 0, HDFF_SIZE_FULL_SCREEN, HDFF_SIZE_FULL_SCREEN); - } - refresh = true; - if (GetBitmap(0)) // only flush here if there are already bitmaps - Flush(); - } - else - { - if (mDisplay != HDFF_INVALID_HANDLE) - { - mHdffCmdIf->CmdOsdDrawRectangle(mDisplay, 0, 0, mDispWidth, mDispHeight, 0); - mHdffCmdIf->CmdOsdRenderDisplay(mDisplay); - } - if (mBitmapPalette != HDFF_INVALID_HANDLE) - mHdffCmdIf->CmdOsdDeletePalette(mBitmapPalette); - mBitmapPalette = HDFF_INVALID_HANDLE; - if (mDisplay != HDFF_INVALID_HANDLE) - mHdffCmdIf->CmdOsdDeleteDisplay(mDisplay); - mDisplay = HDFF_INVALID_HANDLE; - } - } -} - -eOsdError cHdffOsdRaw::CanHandleAreas(const tArea *Areas, int NumAreas) -{ - eOsdError Result = cOsd::CanHandleAreas(Areas, NumAreas); - if (Result == oeOk) - { - for (int i = 0; i < NumAreas; i++) - { - if (Areas[i].bpp != 1 && Areas[i].bpp != 2 && Areas[i].bpp != 4 && Areas[i].bpp != 8 - && (Areas[i].bpp != 32 || !gHdffSetup.TrueColorOsd)) - return oeBppNotSupported; - } - } - return Result; -} - -eOsdError cHdffOsdRaw::SetAreas(const tArea *Areas, int NumAreas) -{ - for (int i = 0; i < NumAreas; i++) - { - //printf("SetAreas %d: %d %d %d %d %d\n", i, Areas[i].x1, Areas[i].y1, Areas[i].x2, Areas[i].y2, Areas[i].bpp); - } - if (mDisplay != HDFF_INVALID_HANDLE) - { - mHdffCmdIf->CmdOsdDrawRectangle(mDisplay, 0, 0, mDispWidth, mDispHeight, 0); - mHdffCmdIf->CmdOsdRenderDisplay(mDisplay); - refresh = true; - } - return cOsd::SetAreas(Areas, NumAreas); -} - -void cHdffOsdRaw::Flush(void) -{ - if (!Active() || (mDisplay == HDFF_INVALID_HANDLE)) - return; -#ifdef MEASURE_OSD_TIME - struct timeval start; - struct timeval end; - struct timezone timeZone; - gettimeofday(&start, &timeZone); -#endif - - bool render = false; - if (IsTrueColor()) - { - uint8_t * buffer = 0; - if (gHdffSetup.TrueColorFormat != 0) - { - buffer = new uint8_t[MAX_BITMAP_SIZE]; - if (!buffer) - return; - } - LOCK_PIXMAPS; - while (cPixmapMemory *pm = dynamic_cast(RenderPixmaps())) - { - int w = pm->ViewPort().Width(); - int h = pm->ViewPort().Height(); - int d = w * sizeof(tColor); - int Chunk = MAX_BITMAP_SIZE / w / sizeof(tColor); - if (Chunk > h) - Chunk = h; - for (int y = 0; y < h; y += Chunk) - { - int hc = Chunk; - if (y + hc > h) - hc = h - y; - if (gHdffSetup.TrueColorFormat == 0) // ARGB8888 (32 bit) - { - mHdffCmdIf->CmdOsdDrawBitmap(mDisplay, - Left() + pm->ViewPort().X(), Top() + pm->ViewPort().Y() + y, - pm->Data() + y * d, w, hc, hc * d, - HDFF_COLOR_TYPE_ARGB8888, HDFF_INVALID_HANDLE); - } - else if (gHdffSetup.TrueColorFormat == 1) // ARGB8565 (24 bit) - { - const tColor * pixmapData = (const tColor *) (pm->Data() + y * d); - uint8_t * bitmapData = buffer; - for (int i = 0; i < hc * w; i++) - { - bitmapData[2] = (pixmapData[i] & 0xFF000000) >> 24; - bitmapData[1] = ((pixmapData[i] & 0x00F80000) >> 16) - | ((pixmapData[i] & 0x0000E000) >> 13); - bitmapData[0] = ((pixmapData[i] & 0x00001C00) >> 5) - | ((pixmapData[i] & 0x000000F8) >> 3); - bitmapData += 3; - } - mHdffCmdIf->CmdOsdDrawBitmap(mDisplay, - Left() + pm->ViewPort().X(), Top() + pm->ViewPort().Y() + y, - buffer, w, hc, hc * w * 3, - HDFF_COLOR_TYPE_ARGB8565, HDFF_INVALID_HANDLE); - } - else if (gHdffSetup.TrueColorFormat == 2) // ARGB4444 (16 bit) - { - const tColor * pixmapData = (const tColor *) (pm->Data() + y * d); - uint16_t * bitmapData = (uint16_t *) buffer; - for (int i = 0; i < hc * w; i++) - { - bitmapData[i] = ((pixmapData[i] & 0xF0000000) >> 16) - | ((pixmapData[i] & 0x00F00000) >> 12) - | ((pixmapData[i] & 0x0000F000) >> 8) - | ((pixmapData[i] & 0x000000F0) >> 4); - } - mHdffCmdIf->CmdOsdDrawBitmap(mDisplay, - Left() + pm->ViewPort().X(), Top() + pm->ViewPort().Y() + y, - buffer, w, hc, hc * w * 2, - HDFF_COLOR_TYPE_ARGB4444, HDFF_INVALID_HANDLE); - } - } - DestroyPixmap(pm); - render = true; - } - if (buffer) - delete[] buffer; - } - else - { - uint8_t * buffer = new uint8_t[MAX_BITMAP_SIZE]; - if (!buffer) - return; - cBitmap * bitmap; - for (int i = 0; (bitmap = GetBitmap(i)) != NULL; i++) - { - int x1 = 0, y1 = 0, x2 = 0, y2 = 0; - if (refresh || bitmap->Dirty(x1, y1, x2, y2)) - { - if (refresh) - { - x2 = bitmap->Width() - 1; - y2 = bitmap->Height() - 1; - } - // commit colors: - int numColors; - const tColor * colors = bitmap->Colors(numColors); - if (colors) - { - for (int c = 0; c < numColors; c++) - mBitmapColors[c] = colors[c]; - if (mBitmapPalette == HDFF_INVALID_HANDLE) - { - mBitmapPalette = mHdffCmdIf->CmdOsdCreatePalette(HDFF_COLOR_TYPE_CLUT8, - HDFF_COLOR_FORMAT_ARGB, numColors, mBitmapColors); - } - else - { - mHdffCmdIf->CmdOsdSetPaletteColors(mBitmapPalette, - HDFF_COLOR_FORMAT_ARGB, 0, numColors, mBitmapColors); - } - } - // commit modified data: - int width = x2 - x1 + 1; - int height = y2 - y1 + 1; - int chunk = MAX_BITMAP_SIZE / width; - if (chunk > height) - chunk = height; - for (int y = 0; y < height; y += chunk) - { - int hc = chunk; - if (y + hc > height) - hc = height - y; - for (int r = 0; r < hc; r++) - memcpy(buffer + r * width, bitmap->Data(x1, y1 + y + r), width); - mHdffCmdIf->CmdOsdDrawBitmap(mDisplay, - Left() + bitmap->X0() + x1, Top() + bitmap->Y0() + y1 + y, - buffer, width, hc, hc * width, - HDFF_COLOR_TYPE_CLUT8, mBitmapPalette); - } - render = true; - } - bitmap->Clean(); - } - delete[] buffer; - } - if (render) - { - mHdffCmdIf->CmdOsdRenderDisplay(mDisplay); -#ifdef MEASURE_OSD_TIME - gettimeofday(&end, &timeZone); - int timeNeeded = end.tv_usec - start.tv_usec; - timeNeeded += (end.tv_sec - start.tv_sec) * 1000000; - printf("time = %d\n", timeNeeded); -#endif - } - refresh = false; -} - - - - -cHdffOsdProvider::cHdffOsdProvider(HDFF::cHdffCmdIf * HdffCmdIf) -{ - mHdffCmdIf = HdffCmdIf; -} - -cOsd *cHdffOsdProvider::CreateOsd(int Left, int Top, uint Level) -{ - //printf("CreateOsd %d %d %d\n", Left, Top, Level); - if (gHdffSetup.HighLevelOsd) - return new cHdffOsd(Left, Top, mHdffCmdIf, Level); - else - return new cHdffOsdRaw(Left, Top, mHdffCmdIf, Level); -} - -bool cHdffOsdProvider::ProvidesTrueColor(void) -{ - return gHdffSetup.TrueColorOsd && !gHdffSetup.HighLevelOsd; -} diff --git a/PLUGINS/src/dvbhddevice/hdffosd.h b/PLUGINS/src/dvbhddevice/hdffosd.h deleted file mode 100644 index c8eaf460..00000000 --- a/PLUGINS/src/dvbhddevice/hdffosd.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * hdffosd.h: Implementation of the DVB HD Full Featured On Screen Display - * - * See the README file for copyright information and how to reach the author. - */ - -#ifndef _HDFF_OSD_H_ -#define _HDFF_OSD_H_ - -#include - -#include "hdffcmd.h" - -class cHdffOsdProvider : public cOsdProvider -{ -private: - HDFF::cHdffCmdIf * mHdffCmdIf; -public: - cHdffOsdProvider(HDFF::cHdffCmdIf * pHdffCmdIf); - virtual cOsd *CreateOsd(int Left, int Top, uint Level); - virtual bool ProvidesTrueColor(void); -}; - -#endif diff --git a/PLUGINS/src/dvbhddevice/libhdffcmd/Makefile b/PLUGINS/src/dvbhddevice/libhdffcmd/Makefile deleted file mode 100644 index 216cdbad..00000000 --- a/PLUGINS/src/dvbhddevice/libhdffcmd/Makefile +++ /dev/null @@ -1,68 +0,0 @@ -# -# Makefile for the HDFF firmware command interface library -# - -VERSION = 0.1.0 - -INSTALL_PATH ?= /usr/local -INSTALL_LIB_PATH ?= $(INSTALL_PATH)/lib -INSTALL_INCLUDE_PATH ?= $(INSTALL_PATH)/include - -LIB_NAME = libhdffcmd - -LIB_OBJS = bitbuffer.o hdffcmd_av.o hdffcmd_base.o hdffcmd_generic.o \ - hdffcmd_hdmi.o hdffcmd_mux.o hdffcmd_osd.o hdffcmd_remote.o - -LIB_HEADERS = hdffcmd.h hdffcmd_av.h hdffcmd_generic.h hdffcmd_hdmi.h \ - hdffcmd_mux.h hdffcmd_osd.h hdffcmd_remote.h - -LIB_STATIC = $(LIB_NAME).a -LIB_SHARED = $(LIB_NAME)-$(VERSION).so - -CC ?= gcc -CFLAGS ?= -g -O2 -fPIC -Wall -AR ?= ar -ARFLAGS ?= r - -### Implicit rules: - -%.o: %.c - $(CC) $(CFLAGS) -c $(DEFINES) $(INCLUDES) $< - -### Dependencies: - -MAKEDEP = $(CC) -MM -MG -DEPFILE = .dependencies -$(DEPFILE): Makefile - @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(LIB_OBJS:%.o=%.c) > $@ - --include $(DEPFILE) - -### Targets: - -all: $(LIB_STATIC) $(LIB_SHARED) - -$(LIB_STATIC): $(LIB_OBJS) - $(AR) $(ARFLAGS) $(LIB_STATIC) $(LIB_OBJS) - -$(LIB_SHARED): $(LIB_OBJS) - $(CC) $(CFLAGS) $(LDFLAGS) -fPIC -shared -o $(LIB_SHARED) $(LIB_OBJS) - ln -sf $(LIB_SHARED) $(LIB_NAME).so - -clean: - @-rm -f $(LIB_OBJS) $(DEPFILE) $(LIB_STATIC) $(LIB_NAME)*.so - -install: $(LIB_SHARED) - chown root $(LIB_SHARED) - chgrp root $(LIB_SHARED) - chmod 0755 $(LIB_SHARED) - cp -f $(LIB_SHARED) $(INSTALL_LIB_PATH)/ - ln -sf $(LIB_SHARED) $(INSTALL_LIB_PATH)/$(LIB_NAME).so - mkdir -p $(INSTALL_INCLUDE_PATH)/libhdffcmd - list='$(LIB_HEADERS)'; \ - for headerfile in $$list; do \ - cp -f $$headerfile $(INSTALL_INCLUDE_PATH)/libhdffcmd/ ; \ - chown root $(INSTALL_INCLUDE_PATH)/libhdffcmd/$$headerfile ; \ - chgrp root $(INSTALL_INCLUDE_PATH)/libhdffcmd/$$headerfile ; \ - chmod 0644 $(INSTALL_INCLUDE_PATH)/libhdffcmd/$$headerfile ; \ - done diff --git a/PLUGINS/src/dvbhddevice/libhdffcmd/bitbuffer.c b/PLUGINS/src/dvbhddevice/libhdffcmd/bitbuffer.c deleted file mode 100644 index b85990d6..00000000 --- a/PLUGINS/src/dvbhddevice/libhdffcmd/bitbuffer.c +++ /dev/null @@ -1,79 +0,0 @@ -/********************************************************************** - * - * HDFF firmware command interface library - * - * Copyright (C) 2011 Andreas Regel - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - *********************************************************************/ - -#include - -#include "bitbuffer.h" - -void BitBuffer_Init(BitBuffer_t * BitBuffer, - uint8_t * Data, uint32_t MaxLength) -{ - memset(Data, 0, MaxLength); - BitBuffer->Data = Data; - BitBuffer->MaxLength = MaxLength * 8; - BitBuffer->BitPos = 0; -} - -void BitBuffer_SetBits(BitBuffer_t * BitBuffer, int NumBits, uint32_t Data) -{ - uint32_t nextBitPos; - uint32_t bytePos; - uint32_t bitsInByte; - int shift; - - if (NumBits <= 0 || NumBits > 32) - return; - - nextBitPos = BitBuffer->BitPos + NumBits; - - if (nextBitPos > BitBuffer->MaxLength) - return; - - bytePos = BitBuffer->BitPos / 8; - bitsInByte = BitBuffer->BitPos % 8; - - BitBuffer->Data[bytePos] &= (uint8_t) (0xFF << (8 - bitsInByte)); - shift = NumBits - (8 - bitsInByte); - if (shift > 0) - BitBuffer->Data[bytePos] |= (uint8_t) (Data >> shift); - else - BitBuffer->Data[bytePos] |= (uint8_t) (Data << (-shift)); - NumBits -= 8 - bitsInByte; - bytePos++; - while (NumBits > 0) - { - shift = NumBits - 8; - if (shift > 0) - BitBuffer->Data[bytePos] = (uint8_t) (Data >> shift); - else - BitBuffer->Data[bytePos] = (uint8_t) (Data << (-shift)); - NumBits -= 8; - bytePos++; - } - BitBuffer->BitPos = nextBitPos; -} - -uint32_t BitBuffer_GetByteLength(BitBuffer_t * BitBuffer) -{ - return (BitBuffer->BitPos + 7) / 8; -} diff --git a/PLUGINS/src/dvbhddevice/libhdffcmd/bitbuffer.h b/PLUGINS/src/dvbhddevice/libhdffcmd/bitbuffer.h deleted file mode 100644 index 5bdc23b2..00000000 --- a/PLUGINS/src/dvbhddevice/libhdffcmd/bitbuffer.h +++ /dev/null @@ -1,43 +0,0 @@ -/********************************************************************** - * - * HDFF firmware command interface library - * - * Copyright (C) 2011 Andreas Regel - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - *********************************************************************/ - -#ifndef BITBUFFER_H -#define BITBUFFER_H - -#include - -typedef struct BitBuffer_t -{ - uint8_t * Data; - uint32_t MaxLength; - uint32_t BitPos; -} BitBuffer_t; - -void BitBuffer_Init(BitBuffer_t * BitBuffer, - uint8_t * Data, uint32_t MaxLength); - -void BitBuffer_SetBits(BitBuffer_t * BitBuffer, int NumBits, uint32_t Data); - -uint32_t BitBuffer_GetByteLength(BitBuffer_t * BitBuffer); - -#endif /* BITBUFFER_H */ diff --git a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd.h b/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd.h deleted file mode 100644 index 8d057824..00000000 --- a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd.h +++ /dev/null @@ -1,42 +0,0 @@ -/********************************************************************** - * - * HDFF firmware command interface library - * - * Copyright (C) 2011 Andreas Regel - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - *********************************************************************/ - -#ifndef HDFFCMD_H -#define HDFFCMD_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "hdffcmd_av.h" -#include "hdffcmd_generic.h" -#include "hdffcmd_hdmi.h" -#include "hdffcmd_mux.h" -#include "hdffcmd_osd.h" -#include "hdffcmd_remote.h" - -#ifdef __cplusplus -} -#endif - -#endif /* HDFFCMD_H */ diff --git a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_av.c b/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_av.c deleted file mode 100644 index ea17ceb4..00000000 --- a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_av.c +++ /dev/null @@ -1,506 +0,0 @@ -/********************************************************************** - * - * HDFF firmware command interface library - * - * Copyright (C) 2011 Andreas Regel - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - *********************************************************************/ - -#include -#include -#include - -#include "hdffcmd.h" -#include "hdffcmd_base.h" -#include "hdffcmd_defs.h" - - -int HdffCmdAvSetPlayMode(int OsdDevice, uint8_t PlayMode, int Realtime) -{ - uint8_t cmdData[8]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_AV_DECODER, - HDFF_MSG_AV_SET_PLAY_MODE); - BitBuffer_SetBits(&cmdBuf, 1, Realtime ? 1 : 0); - BitBuffer_SetBits(&cmdBuf, 7, PlayMode); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdAvSetVideoPid(int OsdDevice, uint8_t DecoderIndex, uint16_t Pid, - HdffVideoStreamType_t StreamType) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_AV_DECODER, - HDFF_MSG_AV_SET_VIDEO_PID); - BitBuffer_SetBits(&cmdBuf, 4, DecoderIndex); - BitBuffer_SetBits(&cmdBuf, 4, StreamType); - BitBuffer_SetBits(&cmdBuf, 3, 0); // reserved - BitBuffer_SetBits(&cmdBuf, 13, Pid); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdAvSetAudioPid(int OsdDevice, uint8_t DecoderIndex, uint16_t Pid, - HdffAudioStreamType_t StreamType, - HdffAvContainerType_t ContainerType) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_AV_DECODER, - HDFF_MSG_AV_SET_AUDIO_PID); - BitBuffer_SetBits(&cmdBuf, 4, DecoderIndex); - BitBuffer_SetBits(&cmdBuf, 4, StreamType); - BitBuffer_SetBits(&cmdBuf, 2, 0); // reserved - BitBuffer_SetBits(&cmdBuf, 1, ContainerType); - BitBuffer_SetBits(&cmdBuf, 13, Pid); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdAvSetPcrPid(int OsdDevice, uint8_t DecoderIndex, uint16_t Pid) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_AV_DECODER, - HDFF_MSG_AV_SET_PCR_PID); - BitBuffer_SetBits(&cmdBuf, 4, DecoderIndex); - BitBuffer_SetBits(&cmdBuf, 4, 0); // reserved - BitBuffer_SetBits(&cmdBuf, 3, 0); // reserved - BitBuffer_SetBits(&cmdBuf, 13, Pid); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdAvSetTeletextPid(int OsdDevice, uint8_t DecoderIndex, uint16_t Pid) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_AV_DECODER, - HDFF_MSG_AV_SET_TELETEXT_PID); - BitBuffer_SetBits(&cmdBuf, 4, DecoderIndex); - BitBuffer_SetBits(&cmdBuf, 4, 0); // reserved - BitBuffer_SetBits(&cmdBuf, 3, 0); // reserved - BitBuffer_SetBits(&cmdBuf, 13, Pid); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdAvSetVideoWindow(int OsdDevice, uint8_t DecoderIndex, int Enable, - uint16_t X, uint16_t Y, uint16_t Width, - uint16_t Height) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_AV_DECODER, - HDFF_MSG_AV_SET_VIDEO_WINDOW); - BitBuffer_SetBits(&cmdBuf, 4, DecoderIndex); - BitBuffer_SetBits(&cmdBuf, 3, 0); // reserved - if (Enable) - BitBuffer_SetBits(&cmdBuf, 1, 1); - else - BitBuffer_SetBits(&cmdBuf, 1, 0); - BitBuffer_SetBits(&cmdBuf, 16, X); - BitBuffer_SetBits(&cmdBuf, 16, Y); - BitBuffer_SetBits(&cmdBuf, 16, Width); - BitBuffer_SetBits(&cmdBuf, 16, Height); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdAvShowStillImage(int OsdDevice, uint8_t DecoderIndex, - const uint8_t * StillImage, int Size, - HdffVideoStreamType_t StreamType) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - osd_raw_data_t osd_data; - int err; - - memset(&osd_data, 0, sizeof(osd_raw_data_t)); - osd_data.data_buffer = StillImage; - osd_data.data_length = Size; - err = ioctl(OsdDevice, OSD_RAW_DATA, &osd_data); - if (err != 0) - return err; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_AV_DECODER, - HDFF_MSG_AV_SHOW_STILL_IMAGE); - BitBuffer_SetBits(&cmdBuf, 4, DecoderIndex); - BitBuffer_SetBits(&cmdBuf, 4, StreamType); - BitBuffer_SetBits(&cmdBuf, 16, osd_data.data_handle); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdAvSetDecoderInput(int OsdDevice, uint8_t DecoderIndex, - uint8_t DemultiplexerIndex) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_AV_DECODER, - HDFF_MSG_AV_SET_DECODER_INPUT); - BitBuffer_SetBits(&cmdBuf, 4, DecoderIndex); - BitBuffer_SetBits(&cmdBuf, 4, DemultiplexerIndex); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdAvSetDemultiplexerInput(int OsdDevice, uint8_t DemultiplexerIndex, - uint8_t TsInputIndex) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_AV_DECODER, - HDFF_MSG_AV_SET_DEMULTIPLEXER_INPUT); - BitBuffer_SetBits(&cmdBuf, 4, DemultiplexerIndex); - BitBuffer_SetBits(&cmdBuf, 4, TsInputIndex); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdAvSetVideoFormat(int OsdDevice, uint8_t DecoderIndex, - const HdffVideoFormat_t * VideoFormat) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_AV_DECODER, - HDFF_MSG_AV_SET_VIDEO_FORMAT); - BitBuffer_SetBits(&cmdBuf, 4, DecoderIndex); - BitBuffer_SetBits(&cmdBuf, 1, VideoFormat->AutomaticEnabled ? 1 : 0); - BitBuffer_SetBits(&cmdBuf, 1, VideoFormat->AfdEnabled ? 1 : 0); - BitBuffer_SetBits(&cmdBuf, 2, VideoFormat->TvFormat); - BitBuffer_SetBits(&cmdBuf, 8, VideoFormat->VideoConversion); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdAvSetVideoOutputMode(int OsdDevice, uint8_t DecoderIndex, - HdffVideoOutputMode_t OutputMode) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_AV_DECODER, - HDFF_MSG_AV_SET_VIDEO_OUTPUT_MODE); - BitBuffer_SetBits(&cmdBuf, 8, OutputMode); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdAvSetStc(int OsdDevice, uint8_t DecoderIndex, uint64_t Stc) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_AV_DECODER, - HDFF_MSG_AV_SET_STC); - BitBuffer_SetBits(&cmdBuf, 4, DecoderIndex); - BitBuffer_SetBits(&cmdBuf, 3, 0); // reserved - BitBuffer_SetBits(&cmdBuf, 1, (uint32_t) (Stc >> 32)); - BitBuffer_SetBits(&cmdBuf, 32, (uint32_t) Stc); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdAvFlushBuffer(int OsdDevice, uint8_t DecoderIndex, int FlushAudio, - int FlushVideo) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_AV_DECODER, - HDFF_MSG_AV_FLUSH_BUFFER); - BitBuffer_SetBits(&cmdBuf, 4, DecoderIndex); - if (FlushAudio) - { - BitBuffer_SetBits(&cmdBuf, 1, 1); - } - else - { - BitBuffer_SetBits(&cmdBuf, 1, 0); - } - if (FlushVideo) - { - BitBuffer_SetBits(&cmdBuf, 1, 1); - } - else - { - BitBuffer_SetBits(&cmdBuf, 1, 0); - } - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdAvEnableSync(int OsdDevice, uint8_t DecoderIndex, int SyncAudio, - int SyncVideo) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_AV_DECODER, - HDFF_MSG_AV_ENABLE_SYNC); - BitBuffer_SetBits(&cmdBuf, 4, DecoderIndex); - BitBuffer_SetBits(&cmdBuf, 1, SyncAudio ? 1 : 0); - BitBuffer_SetBits(&cmdBuf, 1, SyncVideo ? 1 : 0); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdAvSetVideoSpeed(int OsdDevice, uint8_t DecoderIndex, int32_t Speed) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_AV_DECODER, - HDFF_MSG_AV_SET_VIDEO_SPEED); - BitBuffer_SetBits(&cmdBuf, 4, DecoderIndex); - BitBuffer_SetBits(&cmdBuf, 4, 0); - BitBuffer_SetBits(&cmdBuf, 32, Speed); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdAvSetAudioSpeed(int OsdDevice, uint8_t DecoderIndex, int32_t Speed) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_AV_DECODER, - HDFF_MSG_AV_SET_AUDIO_SPEED); - BitBuffer_SetBits(&cmdBuf, 4, DecoderIndex); - BitBuffer_SetBits(&cmdBuf, 4, 0); - BitBuffer_SetBits(&cmdBuf, 32, Speed); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdAvEnableVideoAfterStop(int OsdDevice, uint8_t DecoderIndex, - int EnableVideoAfterStop) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_AV_DECODER, - HDFF_MSG_AV_ENABLE_VIDEO_AFTER_STOP); - BitBuffer_SetBits(&cmdBuf, 4, DecoderIndex); - BitBuffer_SetBits(&cmdBuf, 1, EnableVideoAfterStop ? 1 : 0); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdAvSetAudioDelay(int OsdDevice, int16_t Delay) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_AV_DECODER, - HDFF_MSG_AV_SET_AUDIO_DELAY); - BitBuffer_SetBits(&cmdBuf, 16, Delay); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdAvSetAudioDownmix(int OsdDevice, HdffAudioDownmixMode_t DownmixMode) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_AV_DECODER, - HDFF_MSG_AV_SET_AUDIO_DOWNMIX); - BitBuffer_SetBits(&cmdBuf, 8, DownmixMode); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdAvSetAudioChannel(int OsdDevice, uint8_t AudioChannel) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_AV_DECODER, - HDFF_MSG_AV_SET_AUDIO_CHANNEL); - BitBuffer_SetBits(&cmdBuf, 8, AudioChannel); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdAvSetSyncShift(int OsdDevice, int16_t SyncShift) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_AV_DECODER, - HDFF_MSG_AV_SET_OPTIONS); - BitBuffer_SetBits(&cmdBuf, 1, 1); - BitBuffer_SetBits(&cmdBuf, 31, 0); // reserved - BitBuffer_SetBits(&cmdBuf, 16, SyncShift); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdAvMuteAudio(int OsdDevice, uint8_t DecoderIndex, int Mute) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_AV_DECODER, - HDFF_MSG_AV_MUTE_AUDIO); - BitBuffer_SetBits(&cmdBuf, 4, DecoderIndex); - BitBuffer_SetBits(&cmdBuf, 1, Mute ? 1 : 0); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdAvMuteVideo(int OsdDevice, uint8_t DecoderIndex, int Mute) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_AV_DECODER, - HDFF_MSG_AV_MUTE_VIDEO); - BitBuffer_SetBits(&cmdBuf, 4, DecoderIndex); - BitBuffer_SetBits(&cmdBuf, 1, Mute ? 1 : 0); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} diff --git a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_av.h b/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_av.h deleted file mode 100644 index 36381417..00000000 --- a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_av.h +++ /dev/null @@ -1,159 +0,0 @@ -/********************************************************************** - * - * HDFF firmware command interface library - * - * Copyright (C) 2011 Andreas Regel - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - *********************************************************************/ - -#ifndef HDFFCMD_AV_H -#define HDFFCMD_AV_H - -typedef enum HdffAvContainerType_t -{ - HDFF_AV_CONTAINER_PES, - HDFF_AV_CONTAINER_PES_DVD -} HdffAvContainerType_t; - -typedef enum HdffAudioStreamType_t -{ - HDFF_AUDIO_STREAM_INVALID = -1, - HDFF_AUDIO_STREAM_MPEG1 = 0, - HDFF_AUDIO_STREAM_MPEG2, - HDFF_AUDIO_STREAM_AC3, - HDFF_AUDIO_STREAM_AAC, - HDFF_AUDIO_STREAM_HE_AAC, - HDFF_AUDIO_STREAM_PCM, - HDFF_AUDIO_STREAM_EAC3, - HDFF_AUDIO_STREAM_DTS -} HdffAudioStreamType_t; - -typedef enum HdffVideoStreamType_t -{ - HDFF_VIDEO_STREAM_INVALID = -1, - HDFF_VIDEO_STREAM_MPEG1 = 0, - HDFF_VIDEO_STREAM_MPEG2, - HDFF_VIDEO_STREAM_H264, - HDFF_VIDEO_STREAM_MPEG4_ASP, - HDFF_VIDEO_STREAM_VC1 -} HdffVideoStreamType_t; - -typedef enum HdffTvFormat_t -{ - HDFF_TV_FORMAT_4_BY_3, - HDFF_TV_FORMAT_16_BY_9 -} HdffTvFormat_t; - -typedef enum HdffVideoConversion_t -{ - HDFF_VIDEO_CONVERSION_AUTOMATIC, - HDFF_VIDEO_CONVERSION_LETTERBOX_16_BY_9, - HDFF_VIDEO_CONVERSION_LETTERBOX_14_BY_9, - HDFF_VIDEO_CONVERSION_PILLARBOX, - HDFF_VIDEO_CONVERSION_CENTRE_CUT_OUT, - HDFF_VIDEO_CONVERSION_ALWAYS_16_BY_9, - HDFF_VIDEO_CONVERSION_ZOOM_16_BY_9 -} HdffVideoConversion_t; - -typedef struct HdffVideoFormat_t -{ - int AutomaticEnabled; - int AfdEnabled; - HdffTvFormat_t TvFormat; - HdffVideoConversion_t VideoConversion; -} HdffVideoFormat_t; - -typedef enum HdffVideoOutputMode_t -{ - HDFF_VIDEO_OUTPUT_CLONE, - HDFF_VIDEO_OUTPUT_HD_ONLY -} HdffVideoOutputMode_t; - -typedef enum HdffAudioDownmixMode_t -{ - HDFF_AUDIO_DOWNMIX_OFF, - HDFF_AUDIO_DOWNMIX_ANALOG, - HDFF_AUDIO_DOWNMIX_ALWAYS, - HDFF_AUDIO_DOWNMIX_AUTOMATIC, - HDFF_AUDIO_DOWNMIX_HDMI_ONLY -} HdffAudioDownmixMode_t; - - -int HdffCmdAvSetPlayMode(int OsdDevice, uint8_t PlayMode, int Realtime); - -int HdffCmdAvSetVideoPid(int OsdDevice, uint8_t DecoderIndex, uint16_t Pid, - HdffVideoStreamType_t StreamType); - -int HdffCmdAvSetAudioPid(int OsdDevice, uint8_t DecoderIndex, uint16_t Pid, - HdffAudioStreamType_t StreamType, - HdffAvContainerType_t ContainerType); - -int HdffCmdAvSetPcrPid(int OsdDevice, uint8_t DecoderIndex, uint16_t Pid); - -int HdffCmdAvSetTeletextPid(int OsdDevice, uint8_t DecoderIndex, uint16_t Pid); - -int HdffCmdAvSetVideoWindow(int OsdDevice, uint8_t DecoderIndex, int Enable, - uint16_t X, uint16_t Y, uint16_t Width, - uint16_t Height); - -int HdffCmdAvShowStillImage(int OsdDevice, uint8_t DecoderIndex, - const uint8_t * StillImage, int Size, - HdffVideoStreamType_t StreamType); - -int HdffCmdAvSetDecoderInput(int OsdDevice, uint8_t DecoderIndex, - uint8_t DemultiplexerIndex); - -int HdffCmdAvSetDemultiplexerInput(int OsdDevice, uint8_t DemultiplexerIndex, - uint8_t TsInputIndex); - -int HdffCmdAvSetVideoFormat(int OsdDevice, uint8_t DecoderIndex, - const HdffVideoFormat_t * VideoFormat); - -int HdffCmdAvSetVideoOutputMode(int OsdDevice, uint8_t DecoderIndex, - HdffVideoOutputMode_t OutputMode); - -int HdffCmdAvSetStc(int OsdDevice, uint8_t DecoderIndex, uint64_t Stc); - -int HdffCmdAvFlushBuffer(int OsdDevice, uint8_t DecoderIndex, int FlushAudio, - int FlushVideo); - -int HdffCmdAvEnableSync(int OsdDevice, uint8_t DecoderIndex, int SyncAudio, - int SyncVideo); - -int HdffCmdAvSetVideoSpeed(int OsdDevice, uint8_t DecoderIndex, int32_t Speed); - -int HdffCmdAvSetAudioSpeed(int OsdDevice, uint8_t DecoderIndex, int32_t Speed); - -int HdffCmdAvEnableVideoAfterStop(int OsdDevice, uint8_t DecoderIndex, - int EnableVideoAfterStop); - -int HdffCmdAvSetAudioDelay(int OsdDevice, int16_t Delay); - -int HdffCmdAvSetAudioDownmix(int OsdDevice, - HdffAudioDownmixMode_t DownmixMode); - -int HdffCmdAvSetAudioChannel(int OsdDevice, uint8_t AudioChannel); - -int HdffCmdAvSetSyncShift(int OsdDevice, int16_t SyncShift); - -int HdffCmdAvMuteAudio(int OsdDevice, uint8_t DecoderIndex, int Mute); - -int HdffCmdAvMuteVideo(int OsdDevice, uint8_t DecoderIndex, int Mute); - - -#endif /* HDFFCMD_AV_H */ diff --git a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_base.c b/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_base.c deleted file mode 100644 index ac1ab7e5..00000000 --- a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_base.c +++ /dev/null @@ -1,45 +0,0 @@ -/********************************************************************** - * - * HDFF firmware command interface library - * - * Copyright (C) 2011 Andreas Regel - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - *********************************************************************/ - -#include "hdffcmd_base.h" - -void HdffCmdBuildHeader(BitBuffer_t * MsgBuf, HdffMessageType_t MsgType, - HdffMessageGroup_t MsgGroup, HdffMessageId_t MsgId) -{ - BitBuffer_SetBits(MsgBuf, 16, 0); // length field will be set later - BitBuffer_SetBits(MsgBuf, 6, 0); // reserved - BitBuffer_SetBits(MsgBuf, 2, MsgType); - BitBuffer_SetBits(MsgBuf, 8, MsgGroup); - BitBuffer_SetBits(MsgBuf, 16, MsgId); -} - -uint32_t HdffCmdSetLength(BitBuffer_t * MsgBuf) -{ - uint32_t length; - - length = BitBuffer_GetByteLength(MsgBuf) - 2; - MsgBuf->Data[0] = (uint8_t) (length >> 8); - MsgBuf->Data[1] = (uint8_t) length; - - return length + 2; -} diff --git a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_base.h b/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_base.h deleted file mode 100644 index b6856aad..00000000 --- a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_base.h +++ /dev/null @@ -1,55 +0,0 @@ -/********************************************************************** - * - * HDFF firmware command interface library - * - * Copyright (C) 2011 Andreas Regel - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - *********************************************************************/ - -#ifndef HDFFCMD_BASE_H -#define HDFFCMD_BASE_H - -#include - -#if !defined OSD_RAW_CMD -typedef struct osd_raw_cmd_s { - const void *cmd_data; - int cmd_len; - void *result_data; - int result_len; -} osd_raw_cmd_t; - -typedef struct osd_raw_data_s { - const void *data_buffer; - int data_length; - int data_handle; -} osd_raw_data_t; - -#define OSD_RAW_CMD _IOWR('o', 162, osd_raw_cmd_t) -#define OSD_RAW_DATA _IOWR('o', 163, osd_raw_data_t) -#endif - -#include "bitbuffer.h" -#include "hdffcmd_defs.h" - -void HdffCmdBuildHeader(BitBuffer_t * MsgBuf, HdffMessageType_t MsgType, - HdffMessageGroup_t MsgGroup, HdffMessageId_t MsgId); - -uint32_t HdffCmdSetLength(BitBuffer_t * MsgBuf); - -#endif /* HDFFCMD_BASE_H */ diff --git a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_defs.h b/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_defs.h deleted file mode 100644 index cae2097b..00000000 --- a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_defs.h +++ /dev/null @@ -1,125 +0,0 @@ -/********************************************************************** - * - * HDFF firmware command interface library - * - * Copyright (C) 2011 Andreas Regel - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - *********************************************************************/ - -#ifndef HDFFCMD_DEFS_H -#define HDFFCMD_DEFS_H - -typedef enum HdffMessageType_t -{ - HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_TYPE_ANSWER, - HDFF_MSG_TYPE_RESULT, - HDFF_MSG_TYPE_EVENT -} HdffMessageType_t; - -typedef enum HdffMessageGroup_t -{ - HDFF_MSG_GROUP_GENERIC, - HDFF_MSG_GROUP_AV_DECODER, - HDFF_MSG_GROUP_AV_MUX, - HDFF_MSG_GROUP_FRONTEND, - HDFF_MSG_GROUP_OSD, - HDFF_MSG_GROUP_HDMI, - HDFF_MSG_GROUP_REMOTE_CONTROL -} HdffMessageGroup_t; - -typedef enum HdffMessageId_t -{ - HDFF_MSG_GEN_GET_FIRMWARE_VERSION = 0, - HDFF_MSG_GEN_GET_INTERFACE_VERSION, - HDFF_MSG_GEN_GET_COPYRIGHTS, - - HDFF_MSG_AV_SET_AUDIO_PID = 0, - HDFF_MSG_AV_SET_VIDEO_PID, - HDFF_MSG_AV_SET_PCR_PID, - HDFF_MSG_AV_SET_TELETEXT_PID, - HDFF_MSG_AV_SHOW_STILL_IMAGE, - HDFF_MSG_AV_SET_VIDEO_WINDOW, - HDFF_MSG_AV_SET_DECODER_INPUT, - HDFF_MSG_AV_SET_DEMULTIPLEXER_INPUT, - HDFF_MSG_AV_SET_VIDEO_FORMAT, - HDFF_MSG_AV_SET_VIDEO_OUTPUT_MODE, - HDFF_MSG_AV_SET_STC, - HDFF_MSG_AV_FLUSH_BUFFER, - HDFF_MSG_AV_ENABLE_SYNC, - HDFF_MSG_AV_SET_VIDEO_SPEED, - HDFF_MSG_AV_SET_AUDIO_SPEED, - HDFF_MSG_AV_ENABLE_VIDEO_AFTER_STOP, - HDFF_MSG_AV_GET_VIDEO_FORMAT_INFO, - HDFF_MSG_AV_SET_AUDIO_DELAY, - HDFF_MSG_AV_SET_AUDIO_DOWNMIX, - HDFF_MSG_AV_SET_AUDIO_CHANNEL, - HDFF_MSG_AV_SET_PLAY_MODE, - HDFF_MSG_AV_SET_OPTIONS, - HDFF_MSG_AV_MUTE_AUDIO, - HDFF_MSG_AV_MUTE_VIDEO, - - HDFF_MSG_MUX_SET_VIDEO_OUT = 0, - HDFF_MSG_MUX_SET_SLOW_BLANK, - HDFF_MSG_MUX_SET_FAST_BLANK, - HDFF_MSG_MUX_SET_VOLUME, - HDFF_MSG_MUX_SET_AUDIO_MUTE, - - HDFF_MSG_OSD_CONFIGURE = 0, - HDFF_MSG_OSD_RESET, - HDFF_MSG_OSD_CREATE_DISPLAY = 10, - HDFF_MSG_OSD_DELETE_DISPLAY, - HDFF_MSG_OSD_ENABLE_DISPLAY, - HDFF_MSG_OSD_SET_DISPLAY_OUTPUT_RECTANGLE, - HDFF_MSG_OSD_SET_DISPLAY_CLIPPLING_AREA, - HDFF_MSG_OSD_RENDER_DISPLAY, - HDFF_MSG_OSD_SAVE_REGION, - HDFF_MSG_OSD_RESTORE_REGION, - HDFF_MSG_OSD_CREATE_PALETTE = 30, - HDFF_MSG_OSD_DELETE_PALETTE, - HDFF_MSG_OSD_SET_DISPLAY_PALETTE, - HDFF_MSG_OSD_SET_PALETTE_COLORS, - HDFF_MSG_OSD_CREATE_FONT_FACE = 50, - HDFF_MSG_OSD_DELETE_FONT_FACE, - HDFF_MSG_OSD_CREATE_FONT, - HDFF_MSG_OSD_DELETE_FONT, - HDFF_MSG_OSD_DRAW_PIXEL = 70, - HDFF_MSG_OSD_DRAW_RECTANGLE, - HDFF_MSG_OSD_DRAW_CIRCLE, - HDFF_MSG_OSD_DRAW_ELLIPSE, - HDFF_MSG_OSD_DRAW_SLOPE, - HDFF_MSG_OSD_DRAW_TEXT, - HDFF_MSG_OSD_DRAW_WIDE_TEXT, - HDFF_MSG_OSD_DRAW_BITMAP, - HDFF_MSG_OSD_DRAW_UTF8_TEXT, - - HDFF_MSG_HDMI_ENABLE_OUTPUT = 0, - HDFF_MSG_HDMI_SET_VIDEO_MODE, - HDFF_MSG_HDMI_CONFIGURE, - HDFF_MSG_HDMI_IS_DISPLAY_CONNECTED, - HDFF_MSG_HDMI_GET_DISPLAY_INFO, - HDFF_MSG_HDMI_GET_VIDEO_MODE, - HDFF_MSG_HDMI_SEND_CEC_COMMAND, - HDFF_MSG_HDMI_SEND_RAW_CEC_COMMAND, - - HDFF_MSG_REMOTE_SET_PROTOCOL = 0, - HDFF_MSG_REMOTE_SET_ADDRESS_FILTER, - HDFF_MSG_REMOTE_KEY_EVENT -} HdffMessageId_t; - -#endif /* HDFFCMD_DEFS_H */ diff --git a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_generic.c b/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_generic.c deleted file mode 100644 index 797abe84..00000000 --- a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_generic.c +++ /dev/null @@ -1,165 +0,0 @@ -/********************************************************************** - * - * HDFF firmware command interface library - * - * Copyright (C) 2011 Andreas Regel - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - *********************************************************************/ - -#include -#include -#include -#include - -#include "hdffcmd.h" -#include "hdffcmd_base.h" -#include "hdffcmd_defs.h" - -int HdffCmdGetFirmwareVersion(int OsdDevice, uint32_t * Version, char * String, - uint32_t MaxLength) -{ - uint8_t cmdData[8]; - uint8_t resultData[64]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - int err; - - if (Version == NULL) - return -EINVAL; - - *Version = 0; - if (String) - String[0] = 0; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - osd_cmd.result_data = resultData; - osd_cmd.result_len = sizeof(resultData); - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_GENERIC, - HDFF_MSG_GEN_GET_FIRMWARE_VERSION); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - err = ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); - if (err == 0) - { - if (osd_cmd.result_len > 0) - { - if (String) - { - uint8_t textLength = resultData[9]; - if (textLength >= MaxLength) - textLength = MaxLength - 1; - memcpy(String, &resultData[10], textLength); - String[textLength] = 0; - } - *Version = (resultData[6] << 16) - | (resultData[7] << 8) - | resultData[8]; - } - } - return err; -} - -int HdffCmdGetInterfaceVersion(int OsdDevice, uint32_t * Version, char * String, - uint32_t MaxLength) -{ - uint8_t cmdData[8]; - uint8_t resultData[64]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - int err; - - if (Version == NULL) - return -EINVAL; - - *Version = 0; - if (String) - String[0] = 0; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - osd_cmd.result_data = resultData; - osd_cmd.result_len = sizeof(resultData); - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_GENERIC, - HDFF_MSG_GEN_GET_INTERFACE_VERSION); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - err = ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); - if (err == 0) - { - if (osd_cmd.result_len > 0) - { - if (String) - { - uint8_t textLength = resultData[9]; - if (textLength >= MaxLength) - textLength = MaxLength - 1; - memcpy(String, &resultData[10], textLength); - String[textLength] = 0; - } - *Version = (resultData[6] << 16) - | (resultData[7] << 8) - | resultData[8]; - } - } - return err; -} - -int HdffCmdGetCopyrights(int OsdDevice, uint8_t Index, char * String, - uint32_t MaxLength) -{ - uint8_t cmdData[8]; - uint8_t resultData[280]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - int err; - - if (String == NULL) - return -EINVAL; - - String[0] = 0; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - osd_cmd.result_data = resultData; - osd_cmd.result_len = sizeof(resultData); - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_GENERIC, - HDFF_MSG_GEN_GET_COPYRIGHTS); - BitBuffer_SetBits(&cmdBuf, 8, Index); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - err = ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); - if (err == 0) - { - if (osd_cmd.result_len > 0) - { - uint8_t index = resultData[6]; - uint8_t textLen = resultData[7]; - if (index == Index && textLen > 0) - { - if (textLen >= MaxLength) - { - textLen = MaxLength - 1; - } - memcpy(String, resultData + 8, textLen); - String[textLen] = 0; - } - } - } - return err; -} diff --git a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_generic.h b/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_generic.h deleted file mode 100644 index c12b2964..00000000 --- a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_generic.h +++ /dev/null @@ -1,36 +0,0 @@ -/********************************************************************** - * - * HDFF firmware command interface library - * - * Copyright (C) 2011 Andreas Regel - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - *********************************************************************/ - -#ifndef HDFFCMD_GENERIC_H -#define HDFFCMD_GENERIC_H - -int HdffCmdGetFirmwareVersion(int OsdDevice, uint32_t * Version, char * String, - uint32_t MaxLength); - -int HdffCmdGetInterfaceVersion(int OsdDevice, uint32_t * Version, char * String, - uint32_t MaxLength); - -int HdffCmdGetCopyrights(int OsdDevice, uint8_t Index, char * String, - uint32_t MaxLength); - -#endif /* HDFFCMD_GENERIC_H */ diff --git a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_hdmi.c b/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_hdmi.c deleted file mode 100644 index de315f83..00000000 --- a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_hdmi.c +++ /dev/null @@ -1,120 +0,0 @@ -/********************************************************************** - * - * HDFF firmware command interface library - * - * Copyright (C) 2011 Andreas Regel - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - *********************************************************************/ - -#include -#include -#include - -#include "hdffcmd.h" -#include "hdffcmd_base.h" -#include "hdffcmd_defs.h" - - -int HdffCmdHdmiSetVideoMode(int OsdDevice, HdffVideoMode_t VideoMode) -{ - uint8_t cmdData[8]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_HDMI, - HDFF_MSG_HDMI_SET_VIDEO_MODE); - BitBuffer_SetBits(&cmdBuf, 8, VideoMode); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdHdmiConfigure(int OsdDevice, const HdffHdmiConfig_t * Config) -{ - uint8_t cmdData[24]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - size_t nameLen; - int i; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_HDMI, - HDFF_MSG_HDMI_CONFIGURE); - BitBuffer_SetBits(&cmdBuf, 1, Config->TransmitAudio ? 1 : 0); - BitBuffer_SetBits(&cmdBuf, 1, Config->ForceDviMode ? 1 : 0); - BitBuffer_SetBits(&cmdBuf, 1, Config->CecEnabled ? 1 : 0); - BitBuffer_SetBits(&cmdBuf, 3, Config->VideoModeAdaption); - BitBuffer_SetBits(&cmdBuf, 6, 0); // reserved - nameLen = strlen(Config->CecDeviceName); - if (nameLen > 13) - nameLen = 13; - BitBuffer_SetBits(&cmdBuf, 4, nameLen); - for (i = 0; i < nameLen; i++) - BitBuffer_SetBits(&cmdBuf, 8, Config->CecDeviceName[i]); - - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdHdmiSendCecCommand(int OsdDevice, HdffCecCommand_t Command) -{ - uint8_t cmdData[8]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_HDMI, - HDFF_MSG_HDMI_SEND_CEC_COMMAND); - BitBuffer_SetBits(&cmdBuf, 8, Command); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdHdmiSendRawCecCommand(int OsdDevice, uint8_t Destination, - uint8_t Opcode, const uint8_t * Operand, - uint8_t OperandLength) -{ - uint8_t cmdData[24]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - int i; - - if (OperandLength > 14) - OperandLength = 14; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_HDMI, - HDFF_MSG_HDMI_SEND_RAW_CEC_COMMAND); - BitBuffer_SetBits(&cmdBuf, 4, 0); // reserved - BitBuffer_SetBits(&cmdBuf, 4, Destination); - BitBuffer_SetBits(&cmdBuf, 8, Opcode); - BitBuffer_SetBits(&cmdBuf, 8, OperandLength); - for (i = 0; i < OperandLength; i++) - BitBuffer_SetBits(&cmdBuf, 8, Operand[i]); - - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} diff --git a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_hdmi.h b/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_hdmi.h deleted file mode 100644 index fc79bd9b..00000000 --- a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_hdmi.h +++ /dev/null @@ -1,72 +0,0 @@ -/********************************************************************** - * - * HDFF firmware command interface library - * - * Copyright (C) 2011 Andreas Regel - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - *********************************************************************/ - -#ifndef HDFFCMD_HDMI_H -#define HDFFCMD_HDMI_H - - -typedef enum HdffVideoMode_t -{ - HDFF_VIDEO_MODE_576P50 = 18, - HDFF_VIDEO_MODE_720P50 = 19, - HDFF_VIDEO_MODE_1080I50 = 20, - HDFF_VIDEO_MODE_576I50 = 22 -} HdffVideoMode_t; - -typedef enum HdffVideoModeAdaption_t -{ - HDFF_VIDEO_MODE_ADAPT_OFF, - HDFF_VIDEO_MODE_ADAPT_FRAME_RATE, - HDFF_VIDEO_MODE_ADAPT_ONLY_FOR_HD, - HDFF_VIDEO_MODE_ADAPT_ALWAYS -} HdffVideoModeAdaption_t; - -typedef struct HdffHdmiConfig_t -{ - int TransmitAudio; - int ForceDviMode; - int CecEnabled; - HdffVideoModeAdaption_t VideoModeAdaption; - char CecDeviceName[14]; -} HdffHdmiConfig_t; - -typedef enum HdffCecCommand_t -{ - HDFF_CEC_COMMAND_TV_ON, - HDFF_CEC_COMMAND_TV_OFF, - HDFF_CEC_COMMAND_ACTIVE_SOURCE, - HDFF_CEC_COMMAND_INACTIVE_SOURCE -} HdffCecCommand_t; - - -int HdffCmdHdmiSetVideoMode(int OsdDevice, HdffVideoMode_t VideoMode); - -int HdffCmdHdmiConfigure(int OsdDevice, const HdffHdmiConfig_t * Config); - -int HdffCmdHdmiSendCecCommand(int OsdDevice, HdffCecCommand_t Command); - -int HdffCmdHdmiSendRawCecCommand(int OsdDevice, uint8_t Destination, - uint8_t Opcode, const uint8_t * Operand, - uint8_t OperandLength); - -#endif /* HDFFCMD_HDMI_H */ diff --git a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_mux.c b/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_mux.c deleted file mode 100644 index 3698b565..00000000 --- a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_mux.c +++ /dev/null @@ -1,81 +0,0 @@ -/********************************************************************** - * - * HDFF firmware command interface library - * - * Copyright (C) 2011 Andreas Regel - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - *********************************************************************/ - -#include -#include -#include - -#include "hdffcmd.h" -#include "hdffcmd_base.h" -#include "hdffcmd_defs.h" - - -int HdffCmdMuxSetVideoOut(int OsdDevice, HdffVideoOut_t VideoOut) -{ - uint8_t cmdData[8]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_AV_MUX, - HDFF_MSG_MUX_SET_VIDEO_OUT); - BitBuffer_SetBits(&cmdBuf, 4, VideoOut); - BitBuffer_SetBits(&cmdBuf, 4, 0); // reserved - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdMuxSetVolume(int OsdDevice, uint8_t Volume) -{ - uint8_t cmdData[8]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_AV_MUX, - HDFF_MSG_MUX_SET_VOLUME); - BitBuffer_SetBits(&cmdBuf, 8, Volume); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdMuxMuteAudio(int OsdDevice, int Mute) -{ - uint8_t cmdData[8]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_AV_MUX, - HDFF_MSG_MUX_SET_AUDIO_MUTE); - BitBuffer_SetBits(&cmdBuf, 1, Mute); - BitBuffer_SetBits(&cmdBuf, 7, 0); // reserved - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} diff --git a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_mux.h b/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_mux.h deleted file mode 100644 index 8821d5fb..00000000 --- a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_mux.h +++ /dev/null @@ -1,56 +0,0 @@ -/********************************************************************** - * - * HDFF firmware command interface library - * - * Copyright (C) 2011 Andreas Regel - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - *********************************************************************/ - -#ifndef HDFFCMD_MUX_H -#define HDFFCMD_MUX_H - - -typedef enum HdffVideoOut_t -{ - HDFF_VIDEO_OUT_DISABLED, - HDFF_VIDEO_OUT_CVBS_RGB, - HDFF_VIDEO_OUT_CVBS_YUV, - HDFF_VIDEO_OUT_YC -} HdffVideoOut_t; - -typedef enum HdffSlowBlank_t -{ - HDFF_SLOW_BLANK_OFF, - HDFF_SLOW_BLANK_16_BY_9, - HDFF_SLOW_BLANK_4_BY_3 -} HdffSlowBlank_t; - -typedef enum HdffFastBlank_t -{ - HDFF_FAST_BLANK_CVBS, - HDFF_FAST_BLANK_RGB -} HdffFastBlank_t; - - -int HdffCmdMuxSetVideoOut(int OsdDevice, HdffVideoOut_t VideoOut); - -int HdffCmdMuxSetVolume(int OsdDevice, uint8_t Volume); - -int HdffCmdMuxMuteAudio(int OsdDevice, int Mute); - -#endif /* HDFFCMD_MUX_H */ diff --git a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_osd.c b/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_osd.c deleted file mode 100644 index cf714a87..00000000 --- a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_osd.c +++ /dev/null @@ -1,720 +0,0 @@ -/********************************************************************** - * - * HDFF firmware command interface library - * - * Copyright (C) 2011 Andreas Regel - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - *********************************************************************/ - -#include -#include -#include - -#include "hdffcmd.h" -#include "hdffcmd_base.h" -#include "hdffcmd_defs.h" - - -int HdffCmdOsdConfigure(int OsdDevice, const HdffOsdConfig_t * Config) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_CONFIGURE); - if (Config->FontAntialiasing) - { - BitBuffer_SetBits(&cmdBuf, 1, 1); - } - else - { - BitBuffer_SetBits(&cmdBuf, 1, 0); - } - if (Config->FontKerning) - { - BitBuffer_SetBits(&cmdBuf, 1, 1); - } - else - { - BitBuffer_SetBits(&cmdBuf, 1, 0); - } - BitBuffer_SetBits(&cmdBuf, 6, 0); // reserved - BitBuffer_SetBits(&cmdBuf, 16, Config->FontDpi); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdOsdReset(int OsdDevice) -{ - uint8_t cmdData[8]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_RESET); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - - -int HdffCmdOsdCreateDisplay(int OsdDevice, uint16_t Width, uint16_t Height, - HdffColorType_t ColorType, uint32_t * NewDisplay) -{ - uint8_t cmdData[16]; - uint8_t resultData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - int err; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - osd_cmd.result_data = resultData; - osd_cmd.result_len = sizeof(resultData); - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_CREATE_DISPLAY); - BitBuffer_SetBits(&cmdBuf, 16, Width); - BitBuffer_SetBits(&cmdBuf, 16, Height); - BitBuffer_SetBits(&cmdBuf, 8, ColorType); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - err = ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); - *NewDisplay = HDFF_INVALID_HANDLE; - if (err == 0) - { - if (osd_cmd.result_len > 0) - { - if (resultData[2] == HDFF_MSG_TYPE_ANSWER) - { - *NewDisplay = (resultData[6] << 24) - | (resultData[7] << 16) - | (resultData[8] << 8) - | resultData[9]; - } - else - err = -1; - } - } - return err; -} - -int HdffCmdOsdDeleteDisplay(int OsdDevice, uint32_t Display) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_DELETE_DISPLAY); - BitBuffer_SetBits(&cmdBuf, 32, Display); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdOsdEnableDisplay(int OsdDevice, uint32_t Display, int Enable) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_ENABLE_DISPLAY); - BitBuffer_SetBits(&cmdBuf, 32, Display); - if (Enable) - { - BitBuffer_SetBits(&cmdBuf, 1, 1); - } - else - { - BitBuffer_SetBits(&cmdBuf, 1, 0); - } - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdOsdSetDisplayOutputRectangle(int OsdDevice, uint32_t Display, - uint16_t X, uint16_t Y, - uint16_t Width, uint16_t Height) -{ - uint8_t cmdData[20]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_SET_DISPLAY_OUTPUT_RECTANGLE); - BitBuffer_SetBits(&cmdBuf, 32, Display); - BitBuffer_SetBits(&cmdBuf, 16, X); - BitBuffer_SetBits(&cmdBuf, 16, Y); - BitBuffer_SetBits(&cmdBuf, 16, Width); - BitBuffer_SetBits(&cmdBuf, 16, Height); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdOsdSetDisplayClippingArea(int OsdDevice, uint32_t Display, - int Enable, uint16_t X, uint16_t Y, - uint16_t Width, uint16_t Height) -{ - uint8_t cmdData[20]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_SET_DISPLAY_CLIPPLING_AREA); - BitBuffer_SetBits(&cmdBuf, 32, Display); - if (Enable) - { - BitBuffer_SetBits(&cmdBuf, 1, 1); - } - else - { - BitBuffer_SetBits(&cmdBuf, 1, 0); - } - BitBuffer_SetBits(&cmdBuf, 7, 0); // reserved - BitBuffer_SetBits(&cmdBuf, 16, X); - BitBuffer_SetBits(&cmdBuf, 16, Y); - BitBuffer_SetBits(&cmdBuf, 16, Width); - BitBuffer_SetBits(&cmdBuf, 16, Height); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdOsdRenderDisplay(int OsdDevice, uint32_t Display) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_RENDER_DISPLAY); - BitBuffer_SetBits(&cmdBuf, 32, Display); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdOsdSaveRegion(int OsdDevice, uint32_t Display, - uint16_t X, uint16_t Y, - uint16_t Width, uint16_t Height) -{ - uint8_t cmdData[20]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_SAVE_REGION); - BitBuffer_SetBits(&cmdBuf, 32, Display); - BitBuffer_SetBits(&cmdBuf, 16, X); - BitBuffer_SetBits(&cmdBuf, 16, Y); - BitBuffer_SetBits(&cmdBuf, 16, Width); - BitBuffer_SetBits(&cmdBuf, 16, Height); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdOsdRestoreRegion(int OsdDevice, uint32_t Display) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_RESTORE_REGION); - BitBuffer_SetBits(&cmdBuf, 32, Display); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - - -int HdffCmdOsdCreatePalette(int OsdDevice, HdffColorType_t ColorType, - HdffColorFormat_t ColorFormat, - uint32_t NumColors, const uint32_t * Colors, - uint32_t * NewPalette) -{ - uint8_t cmdData[1060]; - uint8_t resultData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - int i; - int err; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - osd_cmd.result_data = resultData; - osd_cmd.result_len = sizeof(resultData); - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_CREATE_PALETTE); - BitBuffer_SetBits(&cmdBuf, 8, ColorType); - BitBuffer_SetBits(&cmdBuf, 8, ColorFormat); - if (NumColors > 256) - NumColors = 256; - BitBuffer_SetBits(&cmdBuf, 8, NumColors == 256 ? 0 : NumColors); - for (i = 0; i < NumColors; i++) - { - BitBuffer_SetBits(&cmdBuf, 32, Colors[i]); - } - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - err = ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); - *NewPalette = HDFF_INVALID_HANDLE; - if (err == 0) - { - if (osd_cmd.result_len > 0) - { - if (resultData[2] == HDFF_MSG_TYPE_ANSWER) - { - *NewPalette = (resultData[6] << 24) - | (resultData[7] << 16) - | (resultData[8] << 8) - | resultData[9]; - } - else - err = -1; - } - } - return err; -} - -int HdffCmdOsdDeletePalette(int OsdDevice, uint32_t Palette) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_DELETE_PALETTE); - BitBuffer_SetBits(&cmdBuf, 32, Palette); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdOsdSetDisplayPalette(int OsdDevice, uint32_t Display, - uint32_t Palette) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_SET_DISPLAY_PALETTE); - BitBuffer_SetBits(&cmdBuf, 32, Display); - BitBuffer_SetBits(&cmdBuf, 32, Palette); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdOsdSetPaletteColors(int OsdDevice, uint32_t Palette, - HdffColorFormat_t ColorFormat, - uint8_t StartColor, uint32_t NumColors, - const uint32_t * Colors) -{ - uint8_t cmdData[1060]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - int i; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_SET_PALETTE_COLORS); - BitBuffer_SetBits(&cmdBuf, 32, Palette); - BitBuffer_SetBits(&cmdBuf, 8, ColorFormat); - BitBuffer_SetBits(&cmdBuf, 8, StartColor); - if (NumColors > 256) - NumColors = 256; - BitBuffer_SetBits(&cmdBuf, 8, NumColors == 256 ? 0 : NumColors); - for (i = 0; i < NumColors; i++) - { - BitBuffer_SetBits(&cmdBuf, 32, Colors[i]); - } - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdOsdCreateFontFace(int OsdDevice, const uint8_t * FontData, - uint32_t DataSize, uint32_t * NewFontFace) -{ - uint8_t cmdData[16]; - uint8_t resultData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - osd_raw_data_t osd_data; - int err; - - *NewFontFace = HDFF_INVALID_HANDLE; - - memset(&osd_data, 0, sizeof(osd_raw_data_t)); - osd_data.data_buffer = FontData; - osd_data.data_length = DataSize; - err = ioctl(OsdDevice, OSD_RAW_DATA, &osd_data); - if (err != 0) - return err; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - osd_cmd.result_data = resultData; - osd_cmd.result_len = sizeof(resultData); - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_CREATE_FONT_FACE); - BitBuffer_SetBits(&cmdBuf, 16, osd_data.data_handle); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - err = ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); - if (err == 0) - { - if (osd_cmd.result_len > 0) - { - if (resultData[2] == HDFF_MSG_TYPE_ANSWER) - { - *NewFontFace = (resultData[6] << 24) - | (resultData[7] << 16) - | (resultData[8] << 8) - | resultData[9]; - } - else - err = -1; - } - } - return err; -} - -int HdffCmdOsdDeleteFontFace(int OsdDevice, uint32_t FontFace) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_DELETE_FONT_FACE); - BitBuffer_SetBits(&cmdBuf, 32, FontFace); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdOsdCreateFont(int OsdDevice, uint32_t FontFace, uint32_t Size, - uint32_t * NewFont) -{ - uint8_t cmdData[16]; - uint8_t resultData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - int err; - - *NewFont = HDFF_INVALID_HANDLE; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - osd_cmd.result_data = resultData; - osd_cmd.result_len = sizeof(resultData); - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_CREATE_FONT); - BitBuffer_SetBits(&cmdBuf, 32, FontFace); - BitBuffer_SetBits(&cmdBuf, 32, Size); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - err = ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); - if (err == 0) - { - if (osd_cmd.result_len > 0) - { - if (resultData[2] == HDFF_MSG_TYPE_ANSWER) - { - *NewFont = (resultData[6] << 24) - | (resultData[7] << 16) - | (resultData[8] << 8) - | resultData[9]; - } - else - err = -1; - } - } - return err; -} - -int HdffCmdOsdDeleteFont(int OsdDevice, uint32_t Font) -{ - uint8_t cmdData[16]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_DELETE_FONT); - BitBuffer_SetBits(&cmdBuf, 32, Font); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - - -int HdffCmdOsdDrawRectangle(int OsdDevice, uint32_t Display, uint16_t X, - uint16_t Y, uint16_t Width, uint16_t Height, - uint32_t Color) -{ - uint8_t cmdData[24]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_DRAW_RECTANGLE); - BitBuffer_SetBits(&cmdBuf, 32, Display); - BitBuffer_SetBits(&cmdBuf, 16, X); - BitBuffer_SetBits(&cmdBuf, 16, Y); - BitBuffer_SetBits(&cmdBuf, 16, Width); - BitBuffer_SetBits(&cmdBuf, 16, Height); - BitBuffer_SetBits(&cmdBuf, 32, Color); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdOsdDrawEllipse(int OsdDevice, uint32_t Display, uint16_t CX, - uint16_t CY, uint16_t RadiusX, uint16_t RadiusY, - uint32_t Color, uint32_t Flags) -{ - uint8_t cmdData[28]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_DRAW_ELLIPSE); - BitBuffer_SetBits(&cmdBuf, 32, Display); - BitBuffer_SetBits(&cmdBuf, 16, CX); - BitBuffer_SetBits(&cmdBuf, 16, CY); - BitBuffer_SetBits(&cmdBuf, 16, RadiusX); - BitBuffer_SetBits(&cmdBuf, 16, RadiusY); - BitBuffer_SetBits(&cmdBuf, 32, Color); - BitBuffer_SetBits(&cmdBuf, 32, Flags); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdOsdDrawSlope(int OsdDevice, uint32_t Display, uint16_t X, - uint16_t Y, uint16_t Width, uint16_t Height, - uint32_t Color, uint32_t Type) -{ - uint8_t cmdData[28]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_DRAW_SLOPE); - BitBuffer_SetBits(&cmdBuf, 32, Display); - BitBuffer_SetBits(&cmdBuf, 16, X); - BitBuffer_SetBits(&cmdBuf, 16, Y); - BitBuffer_SetBits(&cmdBuf, 16, Width); - BitBuffer_SetBits(&cmdBuf, 16, Height); - BitBuffer_SetBits(&cmdBuf, 32, Color); - BitBuffer_SetBits(&cmdBuf, 32, Type); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdOsdDrawText(int OsdDevice, uint32_t Display, uint32_t Font, - uint16_t X, uint16_t Y, const char * Text, - uint32_t Color) -{ - uint8_t cmdData[1060]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - int i; - int length; - - length = 0; - while (Text[length]) - { - length++; - } - if (length > 980) - length = 980; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_DRAW_TEXT); - BitBuffer_SetBits(&cmdBuf, 32, Display); - BitBuffer_SetBits(&cmdBuf, 32, Font); - BitBuffer_SetBits(&cmdBuf, 16, X); - BitBuffer_SetBits(&cmdBuf, 16, Y); - BitBuffer_SetBits(&cmdBuf, 32, Color); - BitBuffer_SetBits(&cmdBuf, 16, length); - for (i = 0; i < length; i++) - { - BitBuffer_SetBits(&cmdBuf, 8, Text[i]); - } - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdOsdDrawUtf8Text(int OsdDevice, uint32_t Display, uint32_t Font, - uint16_t X, uint16_t Y, const char * Text, - uint32_t Color) -{ - uint8_t cmdData[1060]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - int i; - int length; - - length = 0; - while (Text[length]) - { - length++; - } - if (length > 980) - length = 980; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_DRAW_UTF8_TEXT); - BitBuffer_SetBits(&cmdBuf, 32, Display); - BitBuffer_SetBits(&cmdBuf, 32, Font); - BitBuffer_SetBits(&cmdBuf, 16, X); - BitBuffer_SetBits(&cmdBuf, 16, Y); - BitBuffer_SetBits(&cmdBuf, 32, Color); - BitBuffer_SetBits(&cmdBuf, 16, length); - for (i = 0; i < length; i++) - { - BitBuffer_SetBits(&cmdBuf, 8, Text[i]); - } - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdOsdDrawWideText(int OsdDevice, uint32_t Display, uint32_t Font, - uint16_t X, uint16_t Y, const uint16_t * Text, - uint32_t Color) -{ - uint8_t cmdData[1060]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - int i; - int length; - - length = 0; - while (Text[length]) - { - length++; - } - if (length > 480) - length = 480; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_DRAW_WIDE_TEXT); - BitBuffer_SetBits(&cmdBuf, 32, Display); - BitBuffer_SetBits(&cmdBuf, 32, Font); - BitBuffer_SetBits(&cmdBuf, 16, X); - BitBuffer_SetBits(&cmdBuf, 16, Y); - BitBuffer_SetBits(&cmdBuf, 32, Color); - BitBuffer_SetBits(&cmdBuf, 16, length); - for (i = 0; i < length; i++) - { - BitBuffer_SetBits(&cmdBuf, 16, Text[i]); - } - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdOsdDrawBitmap(int OsdDevice, uint32_t Display, uint16_t X, - uint16_t Y, const uint8_t * Bitmap, uint16_t BmpWidth, - uint16_t BmpHeight, uint32_t BmpSize, - HdffColorType_t ColorType, uint32_t Palette) -{ - uint8_t cmdData[32]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - osd_raw_data_t osd_data; - int err; - - memset(&osd_data, 0, sizeof(osd_raw_data_t)); - osd_data.data_buffer = Bitmap; - osd_data.data_length = BmpSize; - err = ioctl(OsdDevice, OSD_RAW_DATA, &osd_data); - if (err != 0) - return err; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, HDFF_MSG_GROUP_OSD, - HDFF_MSG_OSD_DRAW_BITMAP); - BitBuffer_SetBits(&cmdBuf, 32, Display); - BitBuffer_SetBits(&cmdBuf, 16, X); - BitBuffer_SetBits(&cmdBuf, 16, Y); - BitBuffer_SetBits(&cmdBuf, 16, BmpWidth); - BitBuffer_SetBits(&cmdBuf, 16, BmpHeight); - BitBuffer_SetBits(&cmdBuf, 8, ColorType); - BitBuffer_SetBits(&cmdBuf, 6, 0); // reserved - BitBuffer_SetBits(&cmdBuf, 2, 0); // uncompressed - BitBuffer_SetBits(&cmdBuf, 32, Palette); - BitBuffer_SetBits(&cmdBuf, 16, osd_data.data_handle); - BitBuffer_SetBits(&cmdBuf, 32, 0); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} diff --git a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_osd.h b/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_osd.h deleted file mode 100644 index 079b754f..00000000 --- a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_osd.h +++ /dev/null @@ -1,170 +0,0 @@ -/********************************************************************** - * - * HDFF firmware command interface library - * - * Copyright (C) 2011 Andreas Regel - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - *********************************************************************/ - -#ifndef HDFFCMD_OSD_H -#define HDFFCMD_OSD_H - - -#define HDFF_INVALID_HANDLE 0xFFFFFFFF -#define HDFF_SCREEN_DISPLAY_HANDLE 0xFFFFFFFE - -#define HDFF_POSITION_SCREEN_CENTERED 0xFFFF - -#define HDFF_SIZE_FULL_SCREEN 0xFFFF -#define HDFF_SIZE_SAME_AS_SOURCE 0xFFFE - -#define HDFF_FONT_FACE_TIRESIAS 0x00000000 - - -typedef struct HdffOsdConfig_t -{ - int FontAntialiasing; - int FontKerning; - uint16_t FontDpi; -} HdffOsdConfig_t; - -typedef enum HdffColorType_t -{ - HDFF_COLOR_TYPE_CLUT1, - HDFF_COLOR_TYPE_CLUT2, - HDFF_COLOR_TYPE_CLUT4, - HDFF_COLOR_TYPE_CLUT8, - HDFF_COLOR_TYPE_ARGB8888, - HDFF_COLOR_TYPE_ARGB8565, - HDFF_COLOR_TYPE_ARGB4444, - HDFF_COLOR_TYPE_ARGB1555, - HDFF_COLOR_TYPE_RGB888, - HDFF_COLOR_TYPE_RGB565 -} HdffColorType_t; - -typedef enum HdffColorFormat_t -{ - HDFF_COLOR_FORMAT_ARGB, - HDFF_COLOR_FORMAT_ACBYCR -} HdffColorFormat_t; - -typedef enum HdffDrawingFlags_t -{ - HDFF_DRAW_FULL, - HDFF_DRAW_HALF_TOP, - HDFF_DRAW_HALF_LEFT, - HDFF_DRAW_HALF_BOTTOM, - HDFF_DRAW_HALF_RIGHT, - HDFF_DRAW_QUARTER_TOP_LEFT, - HDFF_DRAW_QUARTER_TOP_RIGHT, - HDFF_DRAW_QUARTER_BOTTOM_LEFT, - HDFF_DRAW_QUARTER_BOTTOM_RIGHT, - HDFF_DRAW_QUARTER_TOP_LEFT_INVERTED, - HDFF_DRAW_QUARTER_TOP_RIGHT_INVERTED, - HDFF_DRAW_QUARTER_BOTTOM_LEFT_INVERTED, - HDFF_DRAW_QUARTER_BOTTOM_RIGHT_INVERTED -} HdffDrawingFlags_t; - - -int HdffCmdOsdConfigure(int OsdDevice, const HdffOsdConfig_t * Config); - -int HdffCmdOsdReset(int OsdDevice); - - -int HdffCmdOsdCreateDisplay(int OsdDevice, uint16_t Width, uint16_t Height, - HdffColorType_t ColorType, uint32_t * NewDisplay); - -int HdffCmdOsdDeleteDisplay(int OsdDevice, uint32_t Display); - -int HdffCmdOsdEnableDisplay(int OsdDevice, uint32_t Display, int Enable); - -int HdffCmdOsdSetDisplayOutputRectangle(int OsdDevice, uint32_t Display, - uint16_t X, uint16_t Y, - uint16_t Width, uint16_t Height); - -int HdffCmdOsdSetDisplayClippingArea(int OsdDevice, uint32_t Display, - int Enable, uint16_t X, uint16_t Y, - uint16_t Width, uint16_t Height); - -int HdffCmdOsdRenderDisplay(int OsdDevice, uint32_t Display); - -int HdffCmdOsdSaveRegion(int OsdDevice, uint32_t Display, - uint16_t X, uint16_t Y, - uint16_t Width, uint16_t Height); - -int HdffCmdOsdRestoreRegion(int OsdDevice, uint32_t Display); - - -int HdffCmdOsdCreatePalette(int OsdDevice, HdffColorType_t ColorType, - HdffColorFormat_t ColorFormat, - uint32_t NumColors, const uint32_t * Colors, - uint32_t * NewPalette); - -int HdffCmdOsdDeletePalette(int OsdDevice, uint32_t Palette); - -int HdffCmdOsdSetDisplayPalette(int OsdDevice, uint32_t Display, - uint32_t Palette); - -int HdffCmdOsdSetPaletteColors(int OsdDevice, uint32_t Palette, - HdffColorFormat_t ColorFormat, - uint8_t StartColor, uint32_t NumColors, - const uint32_t * Colors); - - -int HdffCmdOsdCreateFontFace(int OsdDevice, const uint8_t * FontData, - uint32_t DataSize, uint32_t * NewFontFace); - -int HdffCmdOsdDeleteFontFace(int OsdDevice, uint32_t FontFace); - -int HdffCmdOsdCreateFont(int OsdDevice, uint32_t FontFace, uint32_t Size, - uint32_t * NewFont); - -int HdffCmdOsdDeleteFont(int OsdDevice, uint32_t Font); - - -int HdffCmdOsdDrawRectangle(int OsdDevice, uint32_t Display, uint16_t X, - uint16_t Y, uint16_t Width, uint16_t Height, - uint32_t Color); - -int HdffCmdOsdDrawEllipse(int OsdDevice, uint32_t Display, uint16_t CX, - uint16_t CY, uint16_t RadiusX, uint16_t RadiusY, - uint32_t Color, uint32_t Flags); - -int HdffCmdOsdDrawSlope(int OsdDevice, uint32_t Display, uint16_t X, - uint16_t Y, uint16_t Width, uint16_t Height, - uint32_t Color, uint32_t Type); - -int HdffCmdOsdDrawText(int OsdDevice, uint32_t Display, uint32_t Font, - uint16_t X, uint16_t Y, const char * Text, - uint32_t Color); - -int HdffCmdOsdDrawUtf8Text(int OsdDevice, uint32_t Display, uint32_t Font, - uint16_t X, uint16_t Y, const char * Text, - uint32_t Color); - -int HdffCmdOsdDrawWideText(int OsdDevice, uint32_t Display, uint32_t Font, - uint16_t X, uint16_t Y, const uint16_t * Text, - uint32_t Color); - -int HdffCmdOsdDrawBitmap(int OsdDevice, uint32_t Display, uint16_t X, - uint16_t Y, const uint8_t * Bitmap, uint16_t BmpWidth, - uint16_t BmpHeight, uint32_t BmpSize, - HdffColorType_t ColorType, uint32_t Palette); - - -#endif /* HDFFCMD_OSD_H */ diff --git a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_remote.c b/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_remote.c deleted file mode 100644 index d9bbe456..00000000 --- a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_remote.c +++ /dev/null @@ -1,67 +0,0 @@ -/********************************************************************** - * - * HDFF firmware command interface library - * - * Copyright (C) 2011 Andreas Regel - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - *********************************************************************/ - -#include -#include -#include - -#include "hdffcmd.h" -#include "hdffcmd_base.h" -#include "hdffcmd_defs.h" - - -int HdffCmdRemoteSetProtocol(int OsdDevice, HdffRemoteProtocol_t Protocol) -{ - uint8_t cmdData[8]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_REMOTE_CONTROL, - HDFF_MSG_REMOTE_SET_PROTOCOL); - BitBuffer_SetBits(&cmdBuf, 8, Protocol); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} - -int HdffCmdRemoteSetAddressFilter(int OsdDevice, int Enable, uint32_t Address) -{ - uint8_t cmdData[12]; - BitBuffer_t cmdBuf; - osd_raw_cmd_t osd_cmd; - - BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); - memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); - osd_cmd.cmd_data = cmdData; - HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, - HDFF_MSG_GROUP_REMOTE_CONTROL, - HDFF_MSG_REMOTE_SET_ADDRESS_FILTER); - BitBuffer_SetBits(&cmdBuf, 1, Enable); - BitBuffer_SetBits(&cmdBuf, 7, 0); // reserved - BitBuffer_SetBits(&cmdBuf, 32, Address); - osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); - return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); -} diff --git a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_remote.h b/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_remote.h deleted file mode 100644 index f2e6b8af..00000000 --- a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_remote.h +++ /dev/null @@ -1,39 +0,0 @@ -/********************************************************************** - * - * HDFF firmware command interface library - * - * Copyright (C) 2011 Andreas Regel - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - *********************************************************************/ - -#ifndef HDFFCMD_REMOTE_H -#define HDFFCMD_REMOTE_H - -typedef enum HdffRemoteProtocol_t -{ - HDFF_REMOTE_PROTOCOL_NONE, - HDFF_REMOTE_PROTOCOL_RC5, - HDFF_REMOTE_PROTOCOL_RC6 -} HdffRemoteProtocol_t; - - -int HdffCmdRemoteSetProtocol(int OsdDevice, HdffRemoteProtocol_t Protocol); - -int HdffCmdRemoteSetAddressFilter(int OsdDevice, int Enable, uint32_t Address); - -#endif /* HDFFCMD_REMOTE_H */ diff --git a/PLUGINS/src/dvbhddevice/menu.c b/PLUGINS/src/dvbhddevice/menu.c deleted file mode 100644 index fa953fbc..00000000 --- a/PLUGINS/src/dvbhddevice/menu.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * menu.c: The DVB HD Full Featured device main menu - * - * See the README file for copyright information and how to reach the author. - */ - -#include "menu.h" -#include "setup.h" - -cHdffMenu::cHdffMenu(HDFF::cHdffCmdIf * pHdffCmdIf) -: cOsdMenu("dvbhddevice"), - mHdffCmdIf(pHdffCmdIf) -{ - mVideoConversionItem = new cOsdItem("", osUnknown, false); - Add(mVideoConversionItem); - SetHelp(tr("Video Conversion"), tr("TV on"), tr("TV off")); - SetVideoConversion(); -} - -cHdffMenu::~cHdffMenu() -{ -} - -eOSState cHdffMenu::ProcessKey(eKeys key) -{ - eOSState state = cOsdMenu::ProcessKey(key); - if (state == osUnknown) - { - switch (key) - { - case kRed: - gHdffSetup.SetNextVideoConversion(); - SetVideoConversion(); - break; - - case kGreen: - mHdffCmdIf->CmdHdmiSendCecCommand(HDFF_CEC_COMMAND_TV_ON); - state = osEnd; - break; - - case kYellow: - mHdffCmdIf->CmdHdmiSendCecCommand(HDFF_CEC_COMMAND_TV_OFF); - state = osEnd; - break; - - case kOk: - state = osEnd; - break; - - default: - break; - } - } - return state; -} - -void cHdffMenu::SetVideoConversion(void) -{ - gHdffSetup.SetVideoFormat(mHdffCmdIf); - - char str[128]; - sprintf(str, "%s: %s", tr("Video Conversion"), gHdffSetup.GetVideoConversionString()); - mVideoConversionItem->SetText(str); - Display(); -} diff --git a/PLUGINS/src/dvbhddevice/menu.h b/PLUGINS/src/dvbhddevice/menu.h deleted file mode 100644 index 18b3d997..00000000 --- a/PLUGINS/src/dvbhddevice/menu.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * menu.h: The DVB HD Full Featured device main menu - * - * See the README file for copyright information and how to reach the author. - */ - -#ifndef _HDFF_MENU_H_ -#define _HDFF_MENU_H_ - -#include -#include - -#include "hdffcmd.h" - -class cHdffMenu : public cOsdMenu -{ -private: - HDFF::cHdffCmdIf * mHdffCmdIf; - - cOsdItem * mVideoConversionItem; - - void SetVideoConversion(void); -public: - cHdffMenu(HDFF::cHdffCmdIf * pHdffCmdIf); - virtual ~cHdffMenu(); - virtual eOSState ProcessKey(eKeys Key); -}; - -#endif diff --git a/PLUGINS/src/dvbhddevice/po/de_DE.po b/PLUGINS/src/dvbhddevice/po/de_DE.po deleted file mode 100644 index 2bee15e0..00000000 --- a/PLUGINS/src/dvbhddevice/po/de_DE.po +++ /dev/null @@ -1,128 +0,0 @@ -# VDR plugin language source file -# Copyright (C) 2015 Andreas Regel -# This file is distributed under the same license as the dvbhddevice package. -# Christoph Haubrich , 2011 -# -msgid "" -msgstr "" -"Project-Id-Version: vdr-dvbhddevice 2.2.0\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-09-21 14:01+0200\n" -"PO-Revision-Date: 2011-04-25 21:44+0200\n" -"Last-Translator: Christoph Haubrich\n" -"Language-Team: \n" -"Language: de\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" - -msgid "HD Full Featured DVB device" -msgstr "HD Full Featured DVB device" - -msgid "Video Conversion" -msgstr "Bildanpassung" - -msgid "TV on" -msgstr "TV ein" - -msgid "TV off" -msgstr "TV aus" - -msgid "Automatic" -msgstr "automatisch" - -msgid "Letterbox 16/9" -msgstr "Letterbox 16:9" - -msgid "Letterbox 14/9" -msgstr "Letterbox 14:9" - -msgid "Pillarbox" -msgstr "Pillarbox" - -msgid "CentreCutOut" -msgstr "CentreCutOut" - -msgid "Always 16/9" -msgstr "immer 16:9" - -msgid "Zoom 16/9" -msgstr "Zoome 16:9" - -msgid "Off" -msgstr "aus" - -msgid "Frame rate" -msgstr "passende Framerate" - -msgid "HD Only" -msgstr "nur bei HD" - -msgid "Always" -msgstr "immer" - -msgid "Disabled" -msgstr "abgeschaltet" - -msgid "Analogue only" -msgstr "nur analoge Ausgänge" - -msgid "HDMI only" -msgstr "nur HDMI" - -msgid "Follow resolution" -msgstr "folge Auflösung" - -msgid "none" -msgstr "keins" - -msgid "Resolution" -msgstr "Auflösung" - -msgid "Video Mode Adaption" -msgstr "Auflösungsanpassung" - -msgid "TV format" -msgstr "TV-Format" - -msgid "Analogue Video" -msgstr "Analoges Video" - -msgid "Audio Delay (ms)" -msgstr "Audio Verzögerung (ms)" - -msgid "Audio Downmix" -msgstr "Audio Downmix" - -msgid "A/V Sync Shift (ms)" -msgstr "A/V-Sync Verschiebung (ms)" - -msgid "OSD Size" -msgstr "OSD Größe" - -msgid "HDMI CEC" -msgstr "HDMI CEC" - -msgid "CEC: Switch TV on" -msgstr "CEC: TV einschalten" - -msgid "CEC: Switch TV off" -msgstr "CEC: TV ausschalten" - -msgid "Remote Control Protocol" -msgstr "Fernbedienungsprotokoll" - -msgid "Remote Control Address" -msgstr "Fernbedienungsadresse" - -msgid "High Level OSD" -msgstr "High Level OSD" - -msgid "Allow True Color OSD" -msgstr "Erlaube True Color OSD" - -msgid "True Color format" -msgstr "True Color Format" - -msgid "Hide mainmenu entry" -msgstr "Hauptmenüeintrag verstecken" diff --git a/PLUGINS/src/dvbhddevice/po/et_EE.po b/PLUGINS/src/dvbhddevice/po/et_EE.po deleted file mode 100644 index b483cfec..00000000 --- a/PLUGINS/src/dvbhddevice/po/et_EE.po +++ /dev/null @@ -1,128 +0,0 @@ -# VDR plugin language source file -# Copyright (C) 2015 Andreas Regel -# This file is distributed under the same license as the dvbhddevice package. -# Arthur Konovalov , 2015 -# -msgid "" -msgstr "" -"Project-Id-Version: vdr-dvbhddevice 2.2.0\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-09-21 14:01+0200\n" -"PO-Revision-Date: 2011-04-25 21:44+0200\n" -"Last-Translator: Arthur Konovalov \n" -"Language-Team: Estonian \n" -"Language: et\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -msgid "HD Full Featured DVB device" -msgstr "Täisfunktsionaalne HD DVB seade" - -msgid "Video Conversion" -msgstr "Video konverteerimine" - -msgid "TV on" -msgstr "TV sisse" - -msgid "TV off" -msgstr "TV välja" - -msgid "Automatic" -msgstr "automaatne" - -msgid "Letterbox 16/9" -msgstr "letterbox 16:9" - -msgid "Letterbox 14/9" -msgstr "letterbox 14:9" - -msgid "Pillarbox" -msgstr "pillarbox" - -msgid "CentreCutOut" -msgstr "center cut out" - -msgid "Always 16/9" -msgstr "alati 16:9" - -msgid "Zoom 16/9" -msgstr "suum 16/9" - -msgid "Off" -msgstr "väljas" - -msgid "Frame rate" -msgstr "kaadrisagedus" - -msgid "HD Only" -msgstr "ainult HD-resolutsioon" - -msgid "Always" -msgstr "alati" - -msgid "Disabled" -msgstr "keelatud" - -msgid "Analogue only" -msgstr "ainult analoog" - -msgid "HDMI only" -msgstr "ainult HDMI" - -msgid "Follow resolution" -msgstr "vastavalt resolutsioonile" - -msgid "none" -msgstr "ei" - -msgid "Resolution" -msgstr "Resolutsioon" - -msgid "Video Mode Adaption" -msgstr "Videorežiimi sobitus" - -msgid "TV format" -msgstr "TV külgsuhe" - -msgid "Analogue Video" -msgstr "Analoogvideo" - -msgid "Audio Delay (ms)" -msgstr "Heli viide (ms)" - -msgid "Audio Downmix" -msgstr "Heli downmix" - -msgid "A/V Sync Shift (ms)" -msgstr "A/V-sünkro nihe (ms)" - -msgid "OSD Size" -msgstr "Ekraanimenüü suurus" - -msgid "HDMI CEC" -msgstr "HDMI CEC" - -msgid "CEC: Switch TV on" -msgstr "CEC: TV sisselülitus" - -msgid "CEC: Switch TV off" -msgstr "CEC: TV väljalülitus" - -msgid "Remote Control Protocol" -msgstr "Kaugjuhtimispuldi protokoll" - -msgid "Remote Control Address" -msgstr "Kaugjuhtimispuldi aadress" - -msgid "High Level OSD" -msgstr "Kõrgema taseme ekraanimenüü" - -msgid "Allow True Color OSD" -msgstr "True Color ekraanimenüü" - -msgid "True Color format" -msgstr "True Color formaat" - -msgid "Hide mainmenu entry" -msgstr "Peita peamenüü valikus" diff --git a/PLUGINS/src/dvbhddevice/po/fi_FI.po b/PLUGINS/src/dvbhddevice/po/fi_FI.po deleted file mode 100644 index c97827f1..00000000 --- a/PLUGINS/src/dvbhddevice/po/fi_FI.po +++ /dev/null @@ -1,128 +0,0 @@ -# VDR plugin language source file -# Copyright (C) 2015 Andreas Regel -# This file is distributed under the same license as the dvbhddevice package. -# Christoph Haubrich , 2011 -# -msgid "" -msgstr "" -"Project-Id-Version: vdr-dvbhddevice 2.2.0\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-09-21 14:01+0200\n" -"PO-Revision-Date: 2011-04-25 21:44+0200\n" -"Last-Translator: Rolf Ahrenberg\n" -"Language-Team: Finnish \n" -"Language: fi\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -msgid "HD Full Featured DVB device" -msgstr "DVB-laite HD-ulostulolla" - -msgid "Video Conversion" -msgstr "Näyttömuoto" - -msgid "TV on" -msgstr "TV päälle" - -msgid "TV off" -msgstr "TV kiinni" - -msgid "Automatic" -msgstr "automaattinen" - -msgid "Letterbox 16/9" -msgstr "letterbox 16:9" - -msgid "Letterbox 14/9" -msgstr "letterbox 14:9" - -msgid "Pillarbox" -msgstr "pillarbox" - -msgid "CentreCutOut" -msgstr "center cut out" - -msgid "Always 16/9" -msgstr "aina 16:9" - -msgid "Zoom 16/9" -msgstr "zoom 16/9" - -msgid "Off" -msgstr "ei" - -msgid "Frame rate" -msgstr "kuvataajuuden mukaan" - -msgid "HD Only" -msgstr "vain HD-resoluutiolla" - -msgid "Always" -msgstr "aina" - -msgid "Disabled" -msgstr "ei käytössä" - -msgid "Analogue only" -msgstr "vain analoginen" - -msgid "HDMI only" -msgstr "vain HDMI" - -msgid "Follow resolution" -msgstr "resoluution mukaan" - -msgid "none" -msgstr "ei" - -msgid "Resolution" -msgstr "Kuvaresoluutio" - -msgid "Video Mode Adaption" -msgstr "Sovita näyttömoodi" - -msgid "TV format" -msgstr "Näytön kuvasuhde" - -msgid "Analogue Video" -msgstr "Analoginen kuvalähtö" - -msgid "Audio Delay (ms)" -msgstr "Äänen viive (ms)" - -msgid "Audio Downmix" -msgstr "Äänen alasmiksaus" - -msgid "A/V Sync Shift (ms)" -msgstr "A/V-synkronointi (ms)" - -msgid "OSD Size" -msgstr "Kuvaruutunäytön koko" - -msgid "HDMI CEC" -msgstr "Käytä HDMI CEC-toimintoa" - -msgid "CEC: Switch TV on" -msgstr "CEC: Laita TV päälle" - -msgid "CEC: Switch TV off" -msgstr "CEC: Sammuta TV" - -msgid "Remote Control Protocol" -msgstr "Kaukosäätimen protokolla" - -msgid "Remote Control Address" -msgstr "Kaukosäätimen osoite" - -msgid "High Level OSD" -msgstr "Käytä korkean tason kuvaruutunäyttöä" - -msgid "Allow True Color OSD" -msgstr "Salli tosivärit kuvaruutunäytölle" - -msgid "True Color format" -msgstr "Näytä tosivärit muodossa" - -msgid "Hide mainmenu entry" -msgstr "Piilota valinta päävalikosta" diff --git a/PLUGINS/src/dvbhddevice/po/it_IT.po b/PLUGINS/src/dvbhddevice/po/it_IT.po deleted file mode 100644 index 16752a81..00000000 --- a/PLUGINS/src/dvbhddevice/po/it_IT.po +++ /dev/null @@ -1,131 +0,0 @@ -# VDR plugin language source file -# Copyright (C) 2015 Andreas Regel -# This file is distributed under the same license as the dvbhddevice package. -# Christoph Haubrich , 2011 -# Diego Pierotto , 2013 -# -msgid "" -msgstr "" -"Project-Id-Version: vdr-dvbhddevice 2.2.0\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-09-21 14:01+0200\n" -"PO-Revision-Date: 2015-02-08 19:48+0100\n" -"Last-Translator: Diego Pierotto \n" -"Language-Team: \n" -"Language: it\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Poedit-SourceCharset: utf-8\n" -"X-Generator: Poedit 1.5.4\n" - -msgid "HD Full Featured DVB device" -msgstr "Scheda DVB HD Full Featured" - -msgid "Video Conversion" -msgstr "Conversione video" - -msgid "TV on" -msgstr "TV accesa" - -msgid "TV off" -msgstr "TV spenta" - -msgid "Automatic" -msgstr "Automatica" - -msgid "Letterbox 16/9" -msgstr "Letterbox 16:9" - -msgid "Letterbox 14/9" -msgstr "Letterbox 14:9" - -msgid "Pillarbox" -msgstr "Pillarbox" - -msgid "CentreCutOut" -msgstr "CentreCutOut" - -msgid "Always 16/9" -msgstr "Sempre 16:9" - -msgid "Zoom 16/9" -msgstr "Ingrandimento 16/9" - -msgid "Off" -msgstr "Spento" - -msgid "Frame rate" -msgstr "Frame rate" - -msgid "HD Only" -msgstr "Solo HD" - -msgid "Always" -msgstr "Sempre" - -msgid "Disabled" -msgstr "Disabilitata" - -msgid "Analogue only" -msgstr "Solo analogica" - -msgid "HDMI only" -msgstr "Solo HDMI" - -msgid "Follow resolution" -msgstr "Risoluzione seguente" - -msgid "none" -msgstr "nessuna" - -msgid "Resolution" -msgstr "Risoluzione" - -msgid "Video Mode Adaption" -msgstr "Adattamento modalità video" - -msgid "TV format" -msgstr "Formato TV" - -msgid "Analogue Video" -msgstr "Video analogico" - -msgid "Audio Delay (ms)" -msgstr "Ritardo audio (ms)" - -msgid "Audio Downmix" -msgstr "Scala Audio" - -msgid "A/V Sync Shift (ms)" -msgstr "Alterna sincro A/V (ms)" - -msgid "OSD Size" -msgstr "Dimensione OSD" - -msgid "HDMI CEC" -msgstr "HDMI CEC" - -msgid "CEC: Switch TV on" -msgstr "CEC: Passa a TV accesa" - -msgid "CEC: Switch TV off" -msgstr "CEC: Passa a TV spenta" - -msgid "Remote Control Protocol" -msgstr "Protocollo controllo remoto" - -msgid "Remote Control Address" -msgstr "Indirizzo controllo remoto" - -msgid "High Level OSD" -msgstr "OSD alto livello" - -msgid "Allow True Color OSD" -msgstr "Permetti OSD True Color" - -msgid "True Color format" -msgstr "Formato True Color" - -msgid "Hide mainmenu entry" -msgstr "Nascondi voce menu principale" diff --git a/PLUGINS/src/dvbhddevice/po/uk_UA.po b/PLUGINS/src/dvbhddevice/po/uk_UA.po deleted file mode 100644 index 7d438195..00000000 --- a/PLUGINS/src/dvbhddevice/po/uk_UA.po +++ /dev/null @@ -1,129 +0,0 @@ -# VDR plugin language source file. -# Copyright (C) 2015 Andreas Regel -# This file is distributed under the same license as the dvbhddevice package. -# Yarema Aka Knedlyk , 2015 -# -msgid "" -msgstr "" -"Project-Id-Version: vdr-dvbhddevice 2.2.0\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-03 13:28+0100\n" -"PO-Revision-Date: 2015-02-01 23:31+0100\n" -"Last-Translator: Yarema aka Knedlyk \n" -"Language-Team: Ukrainian \n" -"Language: uk\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 1.7.3\n" - -msgid "HD Full Featured DVB device" -msgstr "HD Full Featured DVB device" - -msgid "Video Conversion" -msgstr "Конвертація відео" - -msgid "TV on" -msgstr "Вкл ТБ" - -msgid "TV off" -msgstr "Викл ТБ" - -msgid "Automatic" -msgstr "Автоматично" - -msgid "Letterbox 16/9" -msgstr "Конверт 16:9" - -msgid "Letterbox 14/9" -msgstr "Конверт 14:9" - -msgid "Pillarbox" -msgstr "Pillarbox" - -msgid "CentreCutOut" -msgstr "Вірізання від центру" - -msgid "Always 16/9" -msgstr "Завжди 16:9" - -msgid "Zoom 16/9" -msgstr "Допасувати до 16:9" - -msgid "Off" -msgstr "Викл" - -msgid "Frame rate" -msgstr "Частота кадрів" - -msgid "HD Only" -msgstr "Тільки HD" - -msgid "Always" -msgstr "Завжди" - -msgid "Disabled" -msgstr "Відключено" - -msgid "Analogue only" -msgstr "Тільки аналоговий" - -msgid "HDMI only" -msgstr "Тільки HDMI" - -msgid "Follow resolution" -msgstr "Відносно розд. здатн." - -msgid "none" -msgstr "немає" - -msgid "Resolution" -msgstr "Розд. здатність" - -msgid "Video Mode Adaption" -msgstr "Адаптація відео режиму" - -msgid "TV format" -msgstr "Формат ТБ" - -msgid "Analogue Video" -msgstr "Аналогове відео" - -msgid "Audio Delay (ms)" -msgstr "Затримка аудіо (мс)" - -msgid "Audio Downmix" -msgstr "Міксування аудіо" - -msgid "A/V Sync Shift (ms)" -msgstr "А/В зсув синхронізації (мс)" - -msgid "OSD Size" -msgstr "Розмір OSD" - -msgid "HDMI CEC" -msgstr "HDMI CEC" - -msgid "CEC: Switch TV on" -msgstr "CEC: включити ТБ" - -msgid "CEC: Switch TV off" -msgstr "CEC: виключити ТБ" - -msgid "Remote Control Protocol" -msgstr "Протокол пульта" - -msgid "Remote Control Address" -msgstr "Адрес пульта" - -msgid "High Level OSD" -msgstr "High Level OSD" - -msgid "Allow True Color OSD" -msgstr "Включити True Color OSD" - -msgid "True Color format" -msgstr "Формат True Color" - -msgid "Hide mainmenu entry" -msgstr "Приховати в головному меню" diff --git a/PLUGINS/src/dvbhddevice/setup.c b/PLUGINS/src/dvbhddevice/setup.c deleted file mode 100644 index fd16a480..00000000 --- a/PLUGINS/src/dvbhddevice/setup.c +++ /dev/null @@ -1,476 +0,0 @@ -/* - * setup.c: Setup for the DVB HD Full Featured On Screen Display - * - * See the README file for copyright information and how to reach the author. - */ - -#include "setup.h" -#include "hdffcmd.h" - -const int kResolution1080i = 0; -const int kResolution720p = 1; -const int kResolution576p = 2; -const int kResolution576i = 3; - - -cHdffSetup gHdffSetup; - -cHdffSetup::cHdffSetup(void) -{ - Resolution = kResolution1080i; - VideoModeAdaption = HDFF_VIDEO_MODE_ADAPT_OFF; - TvFormat = HDFF_TV_FORMAT_16_BY_9; - VideoConversion = HDFF_VIDEO_CONVERSION_PILLARBOX; - AnalogueVideo = HDFF_VIDEO_OUT_CVBS_YUV; - AudioDelay = 0; - AudioDownmix = HDFF_AUDIO_DOWNMIX_AUTOMATIC; - AvSyncShift = 0; - OsdSize = 0; - CecEnabled = 1; - CecTvOn = 1; - CecTvOff = 0; - RemoteProtocol = 1; - RemoteAddress = -1; - HighLevelOsd = 1; - TrueColorOsd = 1; - TrueColorFormat = 0; - HideMainMenu = 0; -} - -bool cHdffSetup::SetupParse(const char *Name, const char *Value) -{ - if (strcmp(Name, "Resolution") == 0) Resolution = atoi(Value); - else if (strcmp(Name, "VideoModeAdaption") == 0) VideoModeAdaption = atoi(Value); - else if (strcmp(Name, "TvFormat") == 0) TvFormat = atoi(Value); - else if (strcmp(Name, "VideoConversion") == 0) VideoConversion = atoi(Value); - else if (strcmp(Name, "AnalogueVideo") == 0) AnalogueVideo = atoi(Value); - else if (strcmp(Name, "AudioDelay") == 0) AudioDelay = atoi(Value); - else if (strcmp(Name, "AudioDownmix") == 0) AudioDownmix = atoi(Value); - else if (strcmp(Name, "AvSyncShift") == 0) AvSyncShift = atoi(Value); - else if (strcmp(Name, "OsdSize") == 0) OsdSize = atoi(Value); - else if (strcmp(Name, "CecEnabled") == 0) CecEnabled = atoi(Value); - else if (strcmp(Name, "CecTvOn") == 0) CecTvOn = atoi(Value); - else if (strcmp(Name, "CecTvOff") == 0) CecTvOff = atoi(Value); - else if (strcmp(Name, "RemoteProtocol") == 0) RemoteProtocol = atoi(Value); - else if (strcmp(Name, "RemoteAddress") == 0) RemoteAddress = atoi(Value); - else if (strcmp(Name, "HighLevelOsd") == 0) HighLevelOsd = atoi(Value); - else if (strcmp(Name, "TrueColorOsd") == 0) TrueColorOsd = atoi(Value); - else if (strcmp(Name, "TrueColorFormat") == 0) TrueColorFormat = atoi(Value); - else if (strcmp(Name, "HideMainMenu") == 0) HideMainMenu = atoi(Value); - else return false; - return true; -} - -void cHdffSetup::GetOsdSize(int &Width, int &Height, double &PixelAspect) -{ - if (OsdSize == 0) { - if (Resolution == kResolution1080i) { - Width = 1920; - Height = 1080; - } - else if (Resolution == kResolution720p) { - Width = 1280; - Height = 720; - } - else { - Width = 720; - Height = 576; - } - if (TvFormat == HDFF_TV_FORMAT_16_BY_9) - PixelAspect = 16.0 / 9.0; - else - PixelAspect = 4.0 / 3.0; - } - else if (OsdSize == 1) { - Width = 1920; - Height = 1080; - PixelAspect = 16.0 / 9.0; - } - else if (OsdSize == 2) { - Width = 1280; - Height = 720; - PixelAspect = 16.0 / 9.0; - } - else if (OsdSize == 3) { - Width = 1024; - Height = 576; - PixelAspect = 16.0 / 9.0; - } - else { - Width = 720; - Height = 576; - PixelAspect = 4.0 / 3.0; - } - PixelAspect /= double(Width) / Height; -} - -HdffVideoMode_t cHdffSetup::GetVideoMode(void) -{ - switch (Resolution) - { - case kResolution1080i: - default: - return HDFF_VIDEO_MODE_1080I50; - case kResolution720p: - return HDFF_VIDEO_MODE_720P50; - case kResolution576p: - return HDFF_VIDEO_MODE_576P50; - case kResolution576i: - return HDFF_VIDEO_MODE_576I50; - } -} - -void cHdffSetup::SetNextVideoConversion(void) -{ - int nextVideoConversion = HDFF_VIDEO_CONVERSION_AUTOMATIC; - - if (TvFormat == HDFF_TV_FORMAT_16_BY_9) - { - switch (VideoConversion) - { - case HDFF_VIDEO_CONVERSION_PILLARBOX: - nextVideoConversion = HDFF_VIDEO_CONVERSION_CENTRE_CUT_OUT; - break; - case HDFF_VIDEO_CONVERSION_CENTRE_CUT_OUT: - nextVideoConversion = HDFF_VIDEO_CONVERSION_ALWAYS_16_BY_9; - break; - case HDFF_VIDEO_CONVERSION_ALWAYS_16_BY_9: - nextVideoConversion = HDFF_VIDEO_CONVERSION_ZOOM_16_BY_9; - break; - case HDFF_VIDEO_CONVERSION_ZOOM_16_BY_9: - nextVideoConversion = HDFF_VIDEO_CONVERSION_PILLARBOX; - break; - } - } - else - { - switch (VideoConversion) - { - case HDFF_VIDEO_CONVERSION_LETTERBOX_16_BY_9: - nextVideoConversion = HDFF_VIDEO_CONVERSION_LETTERBOX_14_BY_9; - break; - case HDFF_VIDEO_CONVERSION_LETTERBOX_14_BY_9: - nextVideoConversion = HDFF_VIDEO_CONVERSION_CENTRE_CUT_OUT; - break; - case HDFF_VIDEO_CONVERSION_CENTRE_CUT_OUT: - nextVideoConversion = HDFF_VIDEO_CONVERSION_LETTERBOX_16_BY_9; - break; - } - } - VideoConversion = nextVideoConversion; -} - -const char * cHdffSetup::GetVideoConversionString(void) -{ - switch (VideoConversion) - { - case HDFF_VIDEO_CONVERSION_AUTOMATIC: - default: - return tr("Automatic"); - case HDFF_VIDEO_CONVERSION_LETTERBOX_16_BY_9: - return tr("Letterbox 16/9"); - case HDFF_VIDEO_CONVERSION_LETTERBOX_14_BY_9: - return tr("Letterbox 14/9"); - case HDFF_VIDEO_CONVERSION_PILLARBOX: - return tr("Pillarbox"); - case HDFF_VIDEO_CONVERSION_CENTRE_CUT_OUT: - return tr("CentreCutOut"); - case HDFF_VIDEO_CONVERSION_ALWAYS_16_BY_9: - return tr("Always 16/9"); - case HDFF_VIDEO_CONVERSION_ZOOM_16_BY_9: - return tr("Zoom 16/9"); - } -} - -void cHdffSetup::SetVideoFormat(HDFF::cHdffCmdIf * HdffCmdIf) -{ - HdffVideoFormat_t videoFormat; - - videoFormat.AutomaticEnabled = true; - videoFormat.AfdEnabled = false; - videoFormat.TvFormat = (HdffTvFormat_t) TvFormat; - videoFormat.VideoConversion = (HdffVideoConversion_t) VideoConversion; - HdffCmdIf->CmdAvSetVideoFormat(0, &videoFormat); -} - -cHdffSetupPage::cHdffSetupPage(HDFF::cHdffCmdIf * pHdffCmdIf) -{ - const int kResolutions = 4; - const int kVideoModeAdaptions = 4; - const int kTvFormats = 2; - const int kAnalogueVideos = 4; - const int kAudioDownmixes = 5; - const int kOsdSizes = 5; - const int kRemoteProtocols = 3; - const int kTrueColorFormats = 3; - - static const char * ResolutionItems[kResolutions] = - { - "1080i", - "720p", - "576p", - "576i", - }; - - static const char * VideoModeAdaptionItems[kVideoModeAdaptions] = - { - tr("Off"), - tr("Frame rate"), - tr("HD Only"), - tr("Always") - }; - - static const char * TvFormatItems[kTvFormats] = - { - "4/3", - "16/9", - }; - - static const char * AnalogueVideoItems[kAnalogueVideos] = - { - tr("Disabled"), - "RGB", - "CVBS + YUV", - "YC (S-Video)", - }; - - static const char * AudioDownmixItems[kAudioDownmixes] = - { - tr("Disabled"), - tr("Analogue only"), - tr("Always"), - tr("Automatic"), - tr("HDMI only"), - }; - - static const char * OsdSizeItems[kOsdSizes] = - { - tr("Follow resolution"), - "1920x1080", - "1280x720", - "1024x576", - "720x576", - }; - - static const char * RemoteProtocolItems[] = - { - tr("none"), - "RC5", - "RC6", - }; - - static const char * TrueColorFormatItems[kTrueColorFormats] = - { - "ARGB8888", - "ARGB8565", - "ARGB4444", - }; - - mHdffCmdIf = pHdffCmdIf; - mNewHdffSetup = gHdffSetup; - - Add(new cMenuEditStraItem(tr("Resolution"), &mNewHdffSetup.Resolution, kResolutions, ResolutionItems)); - Add(new cMenuEditStraItem(tr("Video Mode Adaption"), &mNewHdffSetup.VideoModeAdaption, kVideoModeAdaptions, VideoModeAdaptionItems)); - mTvFormatItem = new cMenuEditStraItem(tr("TV format"), &mNewHdffSetup.TvFormat, kTvFormats, TvFormatItems); - Add(mTvFormatItem); - Add(new cMenuEditStraItem(tr("Analogue Video"), &mNewHdffSetup.AnalogueVideo, kAnalogueVideos, AnalogueVideoItems)); - Add(new cMenuEditIntItem(tr("Audio Delay (ms)"), &mNewHdffSetup.AudioDelay, 0, 500)); - Add(new cMenuEditStraItem(tr("Audio Downmix"), &mNewHdffSetup.AudioDownmix, kAudioDownmixes, AudioDownmixItems)); - Add(new cMenuEditIntItem(tr("A/V Sync Shift (ms)"), &mNewHdffSetup.AvSyncShift, -500, 500)); - Add(new cMenuEditStraItem(tr("OSD Size"), &mNewHdffSetup.OsdSize, kOsdSizes, OsdSizeItems)); - Add(new cMenuEditBoolItem(tr("HDMI CEC"), &mNewHdffSetup.CecEnabled)); - Add(new cMenuEditBoolItem(tr("CEC: Switch TV on"), &mNewHdffSetup.CecTvOn)); - Add(new cMenuEditBoolItem(tr("CEC: Switch TV off"), &mNewHdffSetup.CecTvOff)); - Add(new cMenuEditStraItem(tr("Remote Control Protocol"), &mNewHdffSetup.RemoteProtocol, kRemoteProtocols, RemoteProtocolItems)); - Add(new cMenuEditIntItem(tr("Remote Control Address"), &mNewHdffSetup.RemoteAddress, -1, 31)); - Add(new cMenuEditBoolItem(tr("High Level OSD"), &mNewHdffSetup.HighLevelOsd)); - Add(new cMenuEditBoolItem(tr("Allow True Color OSD"), &mNewHdffSetup.TrueColorOsd)); - Add(new cMenuEditStraItem(tr("True Color format"), &mNewHdffSetup.TrueColorFormat, kTrueColorFormats, TrueColorFormatItems)); - Add(new cMenuEditBoolItem(tr("Hide mainmenu entry"), &mNewHdffSetup.HideMainMenu)); - - mVideoConversion = 0; - if (mNewHdffSetup.TvFormat == HDFF_TV_FORMAT_16_BY_9) - { - switch (mNewHdffSetup.VideoConversion) - { - case HDFF_VIDEO_CONVERSION_PILLARBOX: - mVideoConversion = 0; - break; - case HDFF_VIDEO_CONVERSION_CENTRE_CUT_OUT: - mVideoConversion = 1; - break; - case HDFF_VIDEO_CONVERSION_ALWAYS_16_BY_9: - mVideoConversion = 2; - break; - case HDFF_VIDEO_CONVERSION_ZOOM_16_BY_9: - mVideoConversion = 3; - break; - } - } - else - { - switch (mNewHdffSetup.VideoConversion) - { - case HDFF_VIDEO_CONVERSION_LETTERBOX_16_BY_9: - mVideoConversion = 0; - break; - case HDFF_VIDEO_CONVERSION_LETTERBOX_14_BY_9: - mVideoConversion = 1; - break; - case HDFF_VIDEO_CONVERSION_CENTRE_CUT_OUT: - mVideoConversion = 2; - break; - } - } - BuildVideoConversionItem(); -} - -cHdffSetupPage::~cHdffSetupPage(void) -{ -} - -void cHdffSetupPage::BuildVideoConversionItem(void) -{ - const int kVideoConversions4by3 = 3; - const int kVideoConversions16by9 = 4; - - static const char * VideoConversionItems4by3[kVideoConversions4by3] = - { - tr("Letterbox 16/9"), - tr("Letterbox 14/9"), - tr("CentreCutOut") - }; - - static const char * VideoConversionItems16by9[kVideoConversions16by9] = - { - tr("Pillarbox"), - tr("CentreCutOut"), - tr("Always 16/9"), - tr("Zoom 16/9") - }; - - cOsdItem * item; - - cList::Del(mTvFormatItem->Next()); - if (mNewHdffSetup.TvFormat == HDFF_TV_FORMAT_16_BY_9) - { - item = new cMenuEditStraItem(tr("Video Conversion"), &mVideoConversion, - kVideoConversions16by9, VideoConversionItems16by9); - } - else - { - item = new cMenuEditStraItem(tr("Video Conversion"), &mVideoConversion, - kVideoConversions4by3, VideoConversionItems4by3); - } - Add(item, false, mTvFormatItem); -} - -void cHdffSetupPage::Store(void) -{ - if (mNewHdffSetup.TvFormat == HDFF_TV_FORMAT_16_BY_9) - { - switch (mVideoConversion) - { - case 0: - mNewHdffSetup.VideoConversion = HDFF_VIDEO_CONVERSION_PILLARBOX; - break; - case 1: - mNewHdffSetup.VideoConversion = HDFF_VIDEO_CONVERSION_CENTRE_CUT_OUT; - break; - case 2: - mNewHdffSetup.VideoConversion = HDFF_VIDEO_CONVERSION_ALWAYS_16_BY_9; - break; - case 3: - mNewHdffSetup.VideoConversion = HDFF_VIDEO_CONVERSION_ZOOM_16_BY_9; - break; - } - } - else - { - switch (mVideoConversion) - { - case 0: - mNewHdffSetup.VideoConversion = HDFF_VIDEO_CONVERSION_LETTERBOX_16_BY_9; - break; - case 1: - mNewHdffSetup.VideoConversion = HDFF_VIDEO_CONVERSION_LETTERBOX_14_BY_9; - break; - case 2: - mNewHdffSetup.VideoConversion = HDFF_VIDEO_CONVERSION_CENTRE_CUT_OUT; - break; - } - } - SetupStore("Resolution", mNewHdffSetup.Resolution); - SetupStore("VideoModeAdaption", mNewHdffSetup.VideoModeAdaption); - SetupStore("TvFormat", mNewHdffSetup.TvFormat); - SetupStore("VideoConversion", mNewHdffSetup.VideoConversion); - SetupStore("AnalogueVideo", mNewHdffSetup.AnalogueVideo); - SetupStore("AudioDelay", mNewHdffSetup.AudioDelay); - SetupStore("AudioDownmix", mNewHdffSetup.AudioDownmix); - SetupStore("AvSyncShift", mNewHdffSetup.AvSyncShift); - SetupStore("OsdSize", mNewHdffSetup.OsdSize); - SetupStore("CecEnabled", mNewHdffSetup.CecEnabled); - SetupStore("CecTvOn", mNewHdffSetup.CecTvOn); - SetupStore("CecTvOff", mNewHdffSetup.CecTvOff); - SetupStore("RemoteProtocol", mNewHdffSetup.RemoteProtocol); - SetupStore("RemoteAddress", mNewHdffSetup.RemoteAddress); - SetupStore("HighLevelOsd", mNewHdffSetup.HighLevelOsd); - SetupStore("TrueColorOsd", mNewHdffSetup.TrueColorOsd); - SetupStore("TrueColorFormat", mNewHdffSetup.TrueColorFormat); - SetupStore("HideMainMenu", mNewHdffSetup.HideMainMenu); - - if (mHdffCmdIf) - { - if (mNewHdffSetup.Resolution != gHdffSetup.Resolution) - { - mHdffCmdIf->CmdHdmiSetVideoMode(mNewHdffSetup.GetVideoMode()); - } - HdffHdmiConfig_t hdmiConfig; - - mNewHdffSetup.SetVideoFormat(mHdffCmdIf); - - mHdffCmdIf->CmdAvSetAudioDelay(mNewHdffSetup.AudioDelay); - mHdffCmdIf->CmdAvSetAudioDownmix((HdffAudioDownmixMode_t) mNewHdffSetup.AudioDownmix); - mHdffCmdIf->CmdAvSetSyncShift(mNewHdffSetup.AvSyncShift); - - mHdffCmdIf->CmdMuxSetVideoOut((HdffVideoOut_t) mNewHdffSetup.AnalogueVideo); - - memset(&hdmiConfig, 0, sizeof(hdmiConfig)); - hdmiConfig.TransmitAudio = true; - hdmiConfig.ForceDviMode = false; - hdmiConfig.CecEnabled = mNewHdffSetup.CecEnabled; - hdmiConfig.VideoModeAdaption = (HdffVideoModeAdaption_t) mNewHdffSetup.VideoModeAdaption; - mHdffCmdIf->CmdHdmiConfigure(&hdmiConfig); - - mHdffCmdIf->CmdRemoteSetProtocol((HdffRemoteProtocol_t) mNewHdffSetup.RemoteProtocol); - mHdffCmdIf->CmdRemoteSetAddressFilter(mNewHdffSetup.RemoteAddress >= 0, mNewHdffSetup.RemoteAddress); - } - - gHdffSetup = mNewHdffSetup; -} - -eOSState cHdffSetupPage::ProcessKey(eKeys key) -{ - eOSState state = cMenuSetupPage::ProcessKey(key); - - if (state == osContinue) - { - cOsdItem * item; - switch (key) - { - case kLeft: - case kRight: - item = Get(Current()); - if (item == mTvFormatItem) - { - mVideoConversion = 0; - BuildVideoConversionItem(); - Display(); - } - break; - default: - break; - } - } - return state; -} diff --git a/PLUGINS/src/dvbhddevice/setup.h b/PLUGINS/src/dvbhddevice/setup.h deleted file mode 100644 index 582ee76c..00000000 --- a/PLUGINS/src/dvbhddevice/setup.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * setup.h: Setup for the DVB HD Full Featured On Screen Display - * - * See the README file for copyright information and how to reach the author. - */ - -#ifndef _HDFF_SETUP_H_ -#define _HDFF_SETUP_H_ - -#include -#include "hdffcmd.h" - -struct cHdffSetup -{ - cHdffSetup(void); - bool SetupParse(const char * Name, const char * Value); - void GetOsdSize(int &Width, int &Height, double &PixelAspect); - HdffVideoMode_t GetVideoMode(void); - void SetNextVideoConversion(void); - const char * GetVideoConversionString(void); - void SetVideoFormat(HDFF::cHdffCmdIf * HdffCmdIf); - - int Resolution; - int VideoModeAdaption; - int TvFormat; - int VideoConversion; - int AnalogueVideo; - int AudioDelay; - int AudioDownmix; - int AvSyncShift; - int OsdSize; - int CecEnabled; - int CecTvOn; - int CecTvOff; - int RemoteProtocol; - int RemoteAddress; - - int HighLevelOsd; - int TrueColorOsd; - int TrueColorFormat; - - int HideMainMenu; -}; - -extern cHdffSetup gHdffSetup; - -class cHdffSetupPage : public cMenuSetupPage -{ -private: - HDFF::cHdffCmdIf * mHdffCmdIf; - cHdffSetup mNewHdffSetup; - cOsdItem * mTvFormatItem; - int mVideoConversion; - - void BuildVideoConversionItem(void); - -protected: - virtual void Store(void); - -public: - cHdffSetupPage(HDFF::cHdffCmdIf * pHdffCmdIf); - virtual ~cHdffSetupPage(void); - virtual eOSState ProcessKey(eKeys Key); -}; - -#endif diff --git a/PLUGINS/src/dvbsddevice/COPYING b/PLUGINS/src/dvbsddevice/COPYING deleted file mode 100644 index f90922ee..00000000 --- a/PLUGINS/src/dvbsddevice/COPYING +++ /dev/null @@ -1,340 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/PLUGINS/src/dvbsddevice/HISTORY b/PLUGINS/src/dvbsddevice/HISTORY deleted file mode 100644 index 68e3e6dd..00000000 --- a/PLUGINS/src/dvbsddevice/HISTORY +++ /dev/null @@ -1,65 +0,0 @@ -VDR Plugin 'dvbsddevice' Revision History ------------------------------------------ - -2009-12-28: Version 0.0.1 - -- Initial revision. - -2010-01-04: Version 0.0.2 - -- Calling the MakePrimaryDevice() function of the base class to allow - the cDevice to stop displaying subtitles. -- Added support for DVB cards with multiple fontends. - -2010-01-30: Version 0.0.3 - -- The PCR pid is now recorded for channels where this is different from the - video PID. - -2011-04-17: Version 0.0.4 - -- Removed an obsolete local variable in dvbsdffosd.c (thanks to Paul Menzel). - -2011-08-27: Version 0.0.5 - -- Added option --outputonly to use the device only for output (thanks to Udo Richter). - -2012-03-07: Version 0.0.6 - -- Removed the call to EITScanner.UsesDevice(this) from dvbsddevice.c, because - the code following these calls is only executed if LiveView is true, which is - never the case when the EITScanner switches to a channel. - -2012-12-27: Version 0.0.7 - -- Adapted Makefile to changes introduced in recent VDR versions. - -2013-01-12: Version 0.0.8 - -- Adapted Makefile to changes introduced in recent VDR versions. - -2013-01-25: Version 0.0.9 - -- Returning 0 from cDvbSdFfDevice::NumProvidedSystems() if option --outputonly is given. - -2013-03-31: Version 2.0.0 - -- Official release. - -2013-08-22: Version 2.0.1 - -- Fixed handling the -o option (short form of --outputonly; problem reported by - Mario Edelmann). - -2014-01-01: Version 2.1.1 - -- Avoiding unnecessary pkg-config warnings in plugin Makefiles. -- cDevice::TrickSpeed() now has an additional parameter named Forward. - -2014-03-15: Version 2.1.2 - -- The function cDevice::GetVideoSystem() has been deprecated. - -2015-02-19: Version 2.2.0 - -- Official release. diff --git a/PLUGINS/src/dvbsddevice/Makefile b/PLUGINS/src/dvbsddevice/Makefile deleted file mode 100644 index a8647741..00000000 --- a/PLUGINS/src/dvbsddevice/Makefile +++ /dev/null @@ -1,94 +0,0 @@ -# -# Makefile for a Video Disk Recorder plugin -# -# $Id: Makefile 3.1 2014/01/01 13:29:54 kls Exp $ - -# The official name of this plugin. -# This name will be used in the '-P...' option of VDR to load the plugin. -# By default the main source file also carries this name. - -PLUGIN = dvbsddevice - -### The version number of this plugin (taken from the main source file): - -VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g') - -### The directory environment: - -# Use package data if installed...otherwise assume we're under the VDR source directory: -PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell PKG_CONFIG_PATH="$$PKG_CONFIG_PATH:../../.." pkg-config --variable=$(1) vdr)) -LIBDIR = $(call PKGCFG,libdir) -PLGCFG = $(call PKGCFG,plgcfg) -# -TMPDIR ?= /tmp - -### The compiler options: - -export CFLAGS = $(call PKGCFG,cflags) -export CXXFLAGS = $(call PKGCFG,cxxflags) - -### The version number of VDR's plugin API: - -APIVERSION = $(call PKGCFG,apiversion) - -### Allow user defined options to overwrite defaults: - --include $(PLGCFG) - -### The name of the distribution archive: - -ARCHIVE = $(PLUGIN)-$(VERSION) -PACKAGE = vdr-$(ARCHIVE) - -### The name of the shared object file: - -SOFILE = libvdr-$(PLUGIN).so - -### Includes and Defines (add further entries here): - -INCLUDES += - -DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' - -### The object files (add further files here): - -OBJS = $(PLUGIN).o dvbsdffdevice.o dvbsdffosd.o - -### The main target: - -all: $(SOFILE) - -### Implicit rules: - -%.o: %.c - $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $< - -### Dependencies: - -MAKEDEP = $(CXX) -MM -MG -DEPFILE = .dependencies -$(DEPFILE): Makefile - @$(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@ - --include $(DEPFILE) - -### Targets: - -$(SOFILE): $(OBJS) - $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) -o $@ - -install-lib: $(SOFILE) - install -D $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION) - -install: install-lib - -dist: clean - @-rm -rf $(TMPDIR)/$(ARCHIVE) - @mkdir $(TMPDIR)/$(ARCHIVE) - @cp -a * $(TMPDIR)/$(ARCHIVE) - @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE) - @-rm -rf $(TMPDIR)/$(ARCHIVE) - @echo Distribution package created as $(PACKAGE).tgz - -clean: - @-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~ diff --git a/PLUGINS/src/dvbsddevice/README b/PLUGINS/src/dvbsddevice/README deleted file mode 100644 index 9b1280fe..00000000 --- a/PLUGINS/src/dvbsddevice/README +++ /dev/null @@ -1,20 +0,0 @@ -This is a "plugin" for the Video Disk Recorder (VDR). - -Written by: Klaus Schmidinger - -Project's homepage: http://www.tvdr.de - -Latest version available at: ftp://ftp.tvdr.de/vdr - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. -See the file COPYING for more information. - -Description: - -The 'dvbsddevice' plugin implements the output device for the -"Full Featured" DVB cards based on the TechnoTrend/Fujitsu-Siemens -design. This code was originally part of the core VDR source, and -was moved into this plugin in VDR version 1.7.11. diff --git a/PLUGINS/src/dvbsddevice/dvbsddevice.c b/PLUGINS/src/dvbsddevice/dvbsddevice.c deleted file mode 100644 index 81c15e2b..00000000 --- a/PLUGINS/src/dvbsddevice/dvbsddevice.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * dvbsddevice.c: A plugin for the Video Disk Recorder - * - * See the README file for copyright information and how to reach the author. - * - * $Id: dvbsddevice.c 3.4 2015/02/17 13:11:55 kls Exp $ - */ - -#include -#include -#include "dvbsdffdevice.h" - -static const char *VERSION = "2.2.0"; -static const char *DESCRIPTION = "SD Full Featured DVB device"; - -class cPluginDvbsddevice : public cPlugin { -private: - cDvbSdFfDeviceProbe *probe; -public: - cPluginDvbsddevice(void); - virtual ~cPluginDvbsddevice(); - virtual const char *Version(void) { return VERSION; } - virtual const char *Description(void) { return DESCRIPTION; } - virtual const char *CommandLineHelp(void); - virtual bool ProcessArgs(int argc, char *argv[]); - }; - -cPluginDvbsddevice::cPluginDvbsddevice(void) -{ - probe = new cDvbSdFfDeviceProbe; -} - -cPluginDvbsddevice::~cPluginDvbsddevice() -{ - delete probe; -} - -const char *cPluginDvbsddevice::CommandLineHelp(void) -{ - return " -o --outputonly do not receive, just use as output device\n"; -} - -bool cPluginDvbsddevice::ProcessArgs(int argc, char *argv[]) -{ - static struct option long_options[] = { - { "outputonly", no_argument, NULL, 'o' }, - { NULL, no_argument, NULL, 0 } - }; - - int c; - while ((c = getopt_long(argc, argv, "o", long_options, NULL)) != -1) { - switch (c) { - case 'o': probe->SetOutputOnly(true); - break; - default: return false; - } - } - return true; -} - -VDRPLUGINCREATOR(cPluginDvbsddevice); // Don't touch this! diff --git a/PLUGINS/src/dvbsddevice/dvbsdffdevice.c b/PLUGINS/src/dvbsddevice/dvbsdffdevice.c deleted file mode 100644 index 5711e04d..00000000 --- a/PLUGINS/src/dvbsddevice/dvbsdffdevice.c +++ /dev/null @@ -1,784 +0,0 @@ -/* - * dvbsdffdevice.h: The DVB SD Full Featured device interface - * - * See the README file for copyright information and how to reach the author. - * - * $Id: dvbsdffdevice.c 3.3 2014/03/15 12:35:21 kls Exp $ - */ - -#include "dvbsdffdevice.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "dvbsdffosd.h" - -// --- cDvbSdFfDevice -------------------------------------------------------- - -int cDvbSdFfDevice::devVideoOffset = -1; - -cDvbSdFfDevice::cDvbSdFfDevice(int Adapter, int Frontend, bool OutputOnly) -:cDvbDevice(Adapter, Frontend) -{ - spuDecoder = NULL; - digitalAudio = false; - playMode = pmNone; - outputOnly = OutputOnly; - - // Devices that are only present on cards with decoders: - - fd_osd = DvbOpen(DEV_DVB_OSD, adapter, frontend, O_RDWR); - fd_video = DvbOpen(DEV_DVB_VIDEO, adapter, frontend, O_RDWR | O_NONBLOCK); - fd_audio = DvbOpen(DEV_DVB_AUDIO, adapter, frontend, O_RDWR | O_NONBLOCK); - fd_stc = DvbOpen(DEV_DVB_DEMUX, adapter, frontend, O_RDWR); - - // The offset of the /dev/video devices: - - if (devVideoOffset < 0) { // the first one checks this - FILE *f = NULL; - char buffer[PATH_MAX]; - for (int ofs = 0; ofs < 100; ofs++) { - snprintf(buffer, sizeof(buffer), "/proc/video/dev/video%d", ofs); - if ((f = fopen(buffer, "r")) != NULL) { - if (fgets(buffer, sizeof(buffer), f)) { - if (strstr(buffer, "DVB Board")) { // found the _first_ DVB card - devVideoOffset = ofs; - dsyslog("video device offset is %d", devVideoOffset); - break; - } - } - else - break; - fclose(f); - } - else - break; - } - if (devVideoOffset < 0) - devVideoOffset = 0; - if (f) - fclose(f); - } - devVideoIndex = devVideoOffset >= 0 ? devVideoOffset++ : -1; -} - -cDvbSdFfDevice::~cDvbSdFfDevice() -{ - delete spuDecoder; - // We're not explicitly closing any device files here, since this sometimes - // caused segfaults. Besides, the program is about to terminate anyway... -} - -void cDvbSdFfDevice::MakePrimaryDevice(bool On) -{ - if (On) - new cDvbOsdProvider(fd_osd); - cDvbDevice::MakePrimaryDevice(On); -} - -bool cDvbSdFfDevice::HasDecoder(void) const -{ - return true; -} - -bool cDvbSdFfDevice::AvoidRecording(void) const -{ - return true; -} - -cSpuDecoder *cDvbSdFfDevice::GetSpuDecoder(void) -{ - if (!spuDecoder && IsPrimaryDevice()) - spuDecoder = new cDvbSpuDecoder(); - return spuDecoder; -} - -uchar *cDvbSdFfDevice::GrabImage(int &Size, bool Jpeg, int Quality, int SizeX, int SizeY) -{ - if (devVideoIndex < 0) - return NULL; - char buffer[PATH_MAX]; - snprintf(buffer, sizeof(buffer), "%s%d", DEV_VIDEO, devVideoIndex); - int videoDev = open(buffer, O_RDWR); - if (videoDev >= 0) { - uchar *result = NULL; - // set up the size and RGB - v4l2_format fmt; - memset(&fmt, 0, sizeof(fmt)); - fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fmt.fmt.pix.width = SizeX; - fmt.fmt.pix.height = SizeY; - fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24; - fmt.fmt.pix.field = V4L2_FIELD_ANY; - if (ioctl(videoDev, VIDIOC_S_FMT, &fmt) == 0) { - v4l2_requestbuffers reqBuf; - memset(&reqBuf, 0, sizeof(reqBuf)); - reqBuf.count = 2; - reqBuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - reqBuf.memory = V4L2_MEMORY_MMAP; - if (ioctl(videoDev, VIDIOC_REQBUFS, &reqBuf) >= 0) { - v4l2_buffer mbuf; - memset(&mbuf, 0, sizeof(mbuf)); - mbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - mbuf.memory = V4L2_MEMORY_MMAP; - if (ioctl(videoDev, VIDIOC_QUERYBUF, &mbuf) == 0) { - int msize = mbuf.length; - unsigned char *mem = (unsigned char *)mmap(0, msize, PROT_READ | PROT_WRITE, MAP_SHARED, videoDev, 0); - if (mem && mem != (unsigned char *)-1) { - v4l2_buffer buf; - memset(&buf, 0, sizeof(buf)); - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - buf.index = 0; - if (ioctl(videoDev, VIDIOC_QBUF, &buf) == 0) { - v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (ioctl (videoDev, VIDIOC_STREAMON, &type) == 0) { - memset(&buf, 0, sizeof(buf)); - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - buf.index = 0; - if (ioctl(videoDev, VIDIOC_DQBUF, &buf) == 0) { - if (ioctl(videoDev, VIDIOC_STREAMOFF, &type) == 0) { - // make RGB out of BGR: - int memsize = fmt.fmt.pix.width * fmt.fmt.pix.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; - - dsyslog("grabbing to %s %d %d %d", Jpeg ? "JPEG" : "PNM", Quality, fmt.fmt.pix.width, fmt.fmt.pix.height); - if (Jpeg) { - // convert to JPEG: - result = RgbToJpeg(mem, fmt.fmt.pix.width, fmt.fmt.pix.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", fmt.fmt.pix.width, fmt.fmt.pix.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"); - } - } - else - esyslog("ERROR: video device VIDIOC_STREAMOFF failed"); - } - else - esyslog("ERROR: video device VIDIOC_DQBUF failed"); - } - else - esyslog("ERROR: video device VIDIOC_STREAMON failed"); - } - else - esyslog("ERROR: video device VIDIOC_QBUF failed"); - munmap(mem, msize); - } - else - esyslog("ERROR: failed to memmap video device"); - } - else - esyslog("ERROR: video device VIDIOC_QUERYBUF failed"); - } - else - esyslog("ERROR: video device VIDIOC_REQBUFS failed"); - } - else - esyslog("ERROR: video device VIDIOC_S_FMT failed"); - close(videoDev); - return result; - } - else - LOG_ERROR_STR(buffer); - return NULL; -} - -void cDvbSdFfDevice::SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat) -{ - cDevice::SetVideoDisplayFormat(VideoDisplayFormat); - if (Setup.VideoFormat) { - CHECK(ioctl(fd_video, VIDEO_SET_DISPLAY_FORMAT, VIDEO_LETTER_BOX)); - } - else { - switch (VideoDisplayFormat) { - case vdfPanAndScan: - CHECK(ioctl(fd_video, VIDEO_SET_DISPLAY_FORMAT, VIDEO_PAN_SCAN)); - break; - case vdfLetterBox: - CHECK(ioctl(fd_video, VIDEO_SET_DISPLAY_FORMAT, VIDEO_LETTER_BOX)); - break; - case vdfCenterCutOut: - CHECK(ioctl(fd_video, VIDEO_SET_DISPLAY_FORMAT, VIDEO_CENTER_CUT_OUT)); - break; - default: esyslog("ERROR: unknown video display format %d", VideoDisplayFormat); - } - } -} - -void cDvbSdFfDevice::SetVideoFormat(bool VideoFormat16_9) -{ - CHECK(ioctl(fd_video, VIDEO_SET_FORMAT, VideoFormat16_9 ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3)); - SetVideoDisplayFormat(eVideoDisplayFormat(Setup.VideoDisplayFormat)); -} - -void cDvbSdFfDevice::GetVideoSize(int &Width, int &Height, double &VideoAspect) -{ - if (fd_video >= 0) { - video_size_t vs; - if (ioctl(fd_video, VIDEO_GET_SIZE, &vs) == 0) { - Width = vs.w; - Height = vs.h; - switch (vs.aspect_ratio) { - default: - case VIDEO_FORMAT_4_3: VideoAspect = 4.0 / 3.0; break; - case VIDEO_FORMAT_16_9: VideoAspect = 16.0 / 9.0; break; - case VIDEO_FORMAT_221_1: VideoAspect = 2.21; break; - } - return; - } - else - LOG_ERROR; - } - cDevice::GetVideoSize(Width, Height, VideoAspect); -} - -void cDvbSdFfDevice::GetOsdSize(int &Width, int &Height, double &PixelAspect) -{ - if (fd_video >= 0) { - video_size_t vs; - if (ioctl(fd_video, VIDEO_GET_SIZE, &vs) == 0) { - Width = 720; - if (vs.h != 480 && vs.h != 240) - Height = 576; // PAL - else - Height = 480; // NTSC - switch (Setup.VideoFormat ? vs.aspect_ratio : VIDEO_FORMAT_4_3) { - default: - case VIDEO_FORMAT_4_3: PixelAspect = 4.0 / 3.0; break; - case VIDEO_FORMAT_221_1: // FF DVB cards only distinguish between 4:3 and 16:9 - case VIDEO_FORMAT_16_9: PixelAspect = 16.0 / 9.0; break; - } - PixelAspect /= double(Width) / Height; - return; - } - else - LOG_ERROR; - } - cDevice::GetOsdSize(Width, Height, PixelAspect); -} - -bool cDvbSdFfDevice::SetAudioBypass(bool On) -{ - if (setTransferModeForDolbyDigital != 1) - return false; - return ioctl(fd_audio, AUDIO_SET_BYPASS_MODE, On) == 0; -} - -// ptAudio ptVideo ptPcr ptTeletext ptDolby ptOther -static dmx_pes_type_t PesTypes[] = { DMX_PES_AUDIO, DMX_PES_VIDEO, DMX_PES_PCR, DMX_PES_TELETEXT, DMX_PES_OTHER, DMX_PES_OTHER }; - -bool cDvbSdFfDevice::SetPid(cPidHandle *Handle, int Type, bool On) -{ - if (Handle->pid) { - dmx_pes_filter_params pesFilterParams; - memset(&pesFilterParams, 0, sizeof(pesFilterParams)); - if (On) { - if (Handle->handle < 0) { - Handle->handle = DvbOpen(DEV_DVB_DEMUX, adapter, frontend, O_RDWR | O_NONBLOCK, true); - if (Handle->handle < 0) { - LOG_ERROR; - return false; - } - } - pesFilterParams.pid = Handle->pid; - pesFilterParams.input = DMX_IN_FRONTEND; - pesFilterParams.output = (Type <= ptTeletext && Handle->used <= 1) ? DMX_OUT_DECODER : DMX_OUT_TS_TAP; - pesFilterParams.pes_type= PesTypes[Type < ptOther ? Type : ptOther]; - pesFilterParams.flags = DMX_IMMEDIATE_START; - if (ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams) < 0) { - LOG_ERROR; - return false; - } - } - else if (!Handle->used) { - CHECK(ioctl(Handle->handle, DMX_STOP)); - if (Type <= ptTeletext) { - pesFilterParams.pid = 0x1FFF; - pesFilterParams.input = DMX_IN_FRONTEND; - pesFilterParams.output = DMX_OUT_DECODER; - pesFilterParams.pes_type= PesTypes[Type]; - pesFilterParams.flags = DMX_IMMEDIATE_START; - CHECK(ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams)); - if (PesTypes[Type] == DMX_PES_VIDEO) // let's only do this once - SetPlayMode(pmNone); // necessary to switch a PID from DMX_PES_VIDEO/AUDIO to DMX_PES_OTHER - } - close(Handle->handle); - Handle->handle = -1; - } - } - return true; -} - -bool cDvbSdFfDevice::ProvidesSource(int Source) const -{ - if (outputOnly) - return false; - else - return cDvbDevice::ProvidesSource(Source); -} - -int cDvbSdFfDevice::NumProvidedSystems(void) const -{ - if (outputOnly) - return 0; - return cDvbDevice::NumProvidedSystems(); -} - -void cDvbSdFfDevice::TurnOffLiveMode(bool LiveView) -{ - if (LiveView) { - // Avoid noise while switching: - CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true)); - CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true)); - CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER)); - CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER)); - } - - // Turn off live PIDs: - - DetachAll(pidHandles[ptAudio].pid); - DetachAll(pidHandles[ptVideo].pid); - DetachAll(pidHandles[ptPcr].pid); - DetachAll(pidHandles[ptTeletext].pid); - DelPid(pidHandles[ptAudio].pid); - DelPid(pidHandles[ptVideo].pid); - DelPid(pidHandles[ptPcr].pid, ptPcr); - DelPid(pidHandles[ptTeletext].pid); - DelPid(pidHandles[ptDolby].pid); -} - -bool cDvbSdFfDevice::SetChannelDevice(const cChannel *Channel, bool LiveView) -{ - int apid = Channel->Apid(0); - int vpid = Channel->Vpid(); - int dpid = Channel->Dpid(0); - - bool DoTune = !IsTunedToTransponder(Channel); - - bool pidHandlesVideo = vpid && pidHandles[ptVideo].pid == vpid; - bool pidHandlesAudio = apid && pidHandles[ptAudio].pid == apid; - - bool TurnOffLivePIDs = DoTune - || !IsPrimaryDevice() - || LiveView // for a new live view the old PIDs need to be turned off - || pidHandlesVideo // for recording the PIDs must be shifted from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER - ; - - bool StartTransferMode = IsPrimaryDevice() && !DoTune - && (LiveView && HasPid(vpid ? vpid : apid) && (!pidHandlesVideo || (!pidHandlesAudio && (dpid ? pidHandles[ptAudio].pid != dpid : true)))// the PID is already set as DMX_PES_OTHER - || !LiveView && (pidHandlesVideo || pidHandlesAudio) // a recording is going to shift the PIDs from DMX_PES_AUDIO/VIDEO to DMX_PES_OTHER - ); - if (CamSlot() && !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), CamSlot()->SlotNumber())) - StartTransferMode |= LiveView && IsPrimaryDevice() && Channel->Ca() >= CA_ENCRYPTED_MIN; - - bool TurnOnLivePIDs = !StartTransferMode && LiveView; - - // Turn off live PIDs if necessary: - - if (TurnOffLivePIDs) - TurnOffLiveMode(LiveView); - - // Set the tuner: - - if (!cDvbDevice::SetChannelDevice(Channel, LiveView)) - return false; - - // PID settings: - - if (TurnOnLivePIDs) { - SetAudioBypass(false); - if (!(AddPid(Channel->Ppid(), ptPcr) && AddPid(vpid, ptVideo) && AddPid(apid, ptAudio))) { - esyslog("ERROR: failed to set PIDs for channel %d on device %d", Channel->Number(), CardIndex() + 1); - return false; - } - if (IsPrimaryDevice()) - AddPid(Channel->Tpid(), ptTeletext); - CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true)); // actually one would expect 'false' here, but according to Marco Schluessler this works - // to avoid missing audio after replaying a DVD; with 'false' there is an audio disturbance when switching - // between two channels on the same transponder on DVB-S - CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true)); - } - else if (StartTransferMode) - cControl::Launch(new cTransferControl(this, Channel)); - - return true; -} - -int cDvbSdFfDevice::GetAudioChannelDevice(void) -{ - audio_status_t as; - CHECK(ioctl(fd_audio, AUDIO_GET_STATUS, &as)); - return as.channel_select; -} - -void cDvbSdFfDevice::SetAudioChannelDevice(int AudioChannel) -{ - CHECK(ioctl(fd_audio, AUDIO_CHANNEL_SELECT, AudioChannel)); -} - -void cDvbSdFfDevice::SetVolumeDevice(int Volume) -{ - if (digitalAudio) - Volume = 0; - audio_mixer_t am; - // conversion for linear volume response: - am.volume_left = am.volume_right = 2 * Volume - Volume * Volume / 255; - CHECK(ioctl(fd_audio, AUDIO_SET_MIXER, &am)); -} - -void cDvbSdFfDevice::SetDigitalAudioDevice(bool On) -{ - if (digitalAudio != On) { - if (digitalAudio) - cCondWait::SleepMs(1000); // Wait until any leftover digital data has been flushed - digitalAudio = On; - SetVolumeDevice(On || IsMute() ? 0 : CurrentVolume()); - } -} - -void cDvbSdFfDevice::SetAudioTrackDevice(eTrackType Type) -{ - const tTrackId *TrackId = GetTrack(Type); - if (TrackId && TrackId->id) { - SetAudioBypass(false); - if (IS_AUDIO_TRACK(Type) || (IS_DOLBY_TRACK(Type) && SetAudioBypass(true))) { - if (pidHandles[ptAudio].pid && pidHandles[ptAudio].pid != TrackId->id) { - DetachAll(pidHandles[ptAudio].pid); - if (CamSlot()) - CamSlot()->SetPid(pidHandles[ptAudio].pid, false); - pidHandles[ptAudio].pid = TrackId->id; - SetPid(&pidHandles[ptAudio], ptAudio, true); - if (CamSlot()) { - CamSlot()->SetPid(pidHandles[ptAudio].pid, true); - CamSlot()->StartDecrypting(); - } - } - } - else if (IS_DOLBY_TRACK(Type)) { - if (setTransferModeForDolbyDigital == 0) - return; - // Currently this works only in Transfer Mode - ForceTransferMode(); - } - } -} - -bool cDvbSdFfDevice::CanReplay(void) const -{ - return cDevice::CanReplay(); -} - -bool cDvbSdFfDevice::SetPlayMode(ePlayMode PlayMode) -{ - if (PlayMode != pmExtern_THIS_SHOULD_BE_AVOIDED && fd_video < 0 && fd_audio < 0) { - // reopen the devices - fd_video = DvbOpen(DEV_DVB_VIDEO, adapter, frontend, O_RDWR | O_NONBLOCK); - fd_audio = DvbOpen(DEV_DVB_AUDIO, adapter, frontend, O_RDWR | O_NONBLOCK); - SetVideoFormat(Setup.VideoFormat); - } - - switch (PlayMode) { - case pmNone: - // special handling to return from PCM replay: - CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true)); - CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY)); - CHECK(ioctl(fd_video, VIDEO_PLAY)); - - CHECK(ioctl(fd_video, VIDEO_STOP, true)); - CHECK(ioctl(fd_audio, AUDIO_STOP, true)); - CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER)); - CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER)); - CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX)); - CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_DEMUX)); - CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true)); - CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false)); - break; - case pmAudioVideo: - case pmAudioOnlyBlack: - if (playMode == pmNone) - TurnOffLiveMode(true); - CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true)); - CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY)); - CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, PlayMode == pmAudioVideo)); - CHECK(ioctl(fd_audio, AUDIO_PLAY)); - CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY)); - CHECK(ioctl(fd_video, VIDEO_PLAY)); - break; - case pmAudioOnly: - CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true)); - CHECK(ioctl(fd_audio, AUDIO_STOP, true)); - CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER)); - CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY)); - CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false)); - CHECK(ioctl(fd_audio, AUDIO_PLAY)); - CHECK(ioctl(fd_video, VIDEO_SET_BLANK, false)); - break; - case pmVideoOnly: - CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true)); - CHECK(ioctl(fd_video, VIDEO_STOP, true)); - CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_DEMUX)); - CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false)); - CHECK(ioctl(fd_audio, AUDIO_PLAY)); - CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER)); - CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY)); - CHECK(ioctl(fd_video, VIDEO_PLAY)); - break; - case pmExtern_THIS_SHOULD_BE_AVOIDED: - close(fd_video); - close(fd_audio); - fd_video = fd_audio = -1; - break; - default: esyslog("ERROR: unknown playmode %d", PlayMode); - } - playMode = PlayMode; - return true; -} - -int64_t cDvbSdFfDevice::GetSTC(void) -{ - if (fd_stc >= 0) { - struct dmx_stc stc; - stc.num = 0; - if (ioctl(fd_stc, DMX_GET_STC, &stc) == -1) { - esyslog("ERROR: stc %d: %m", CardIndex() + 1); - return -1; - } - return stc.stc / stc.base; - } - return -1; -} - -void cDvbSdFfDevice::TrickSpeed(int Speed, bool Forward) -{ - if (fd_video >= 0) - CHECK(ioctl(fd_video, VIDEO_SLOWMOTION, Speed)); -} - -void cDvbSdFfDevice::Clear(void) -{ - if (fd_video >= 0) - CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER)); - if (fd_audio >= 0) - CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER)); - cDevice::Clear(); -} - -void cDvbSdFfDevice::Play(void) -{ - if (playMode == pmAudioOnly || playMode == pmAudioOnlyBlack) { - if (fd_audio >= 0) - CHECK(ioctl(fd_audio, AUDIO_CONTINUE)); - } - else { - if (fd_audio >= 0) { - CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true)); - CHECK(ioctl(fd_audio, AUDIO_CONTINUE)); - } - if (fd_video >= 0) - CHECK(ioctl(fd_video, VIDEO_CONTINUE)); - } - cDevice::Play(); -} - -void cDvbSdFfDevice::Freeze(void) -{ - if (playMode == pmAudioOnly || playMode == pmAudioOnlyBlack) { - if (fd_audio >= 0) - CHECK(ioctl(fd_audio, AUDIO_PAUSE)); - } - else { - if (fd_audio >= 0) { - CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false)); - CHECK(ioctl(fd_audio, AUDIO_PAUSE)); - } - if (fd_video >= 0) - CHECK(ioctl(fd_video, VIDEO_FREEZE)); - } - cDevice::Freeze(); -} - -void cDvbSdFfDevice::Mute(void) -{ - if (fd_audio >= 0) { - CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false)); - CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true)); - } - cDevice::Mute(); -} - -void cDvbSdFfDevice::StillPicture(const uchar *Data, int Length) -{ - if (!Data || Length < TS_SIZE) - return; - if (Data[0] == 0x47) { - // TS data - cDevice::StillPicture(Data, Length); - } - else if (Data[0] == 0x00 && Data[1] == 0x00 && Data[2] == 0x01 && (Data[3] & 0xF0) == 0xE0) { - // PES data - char *buf = MALLOC(char, Length); - if (!buf) - return; - int i = 0; - int blen = 0; - while (i < Length - 6) { - if (Data[i] == 0x00 && Data[i + 1] == 0x00 && Data[i + 2] == 0x01) { - int len = Data[i + 4] * 256 + Data[i + 5]; - if ((Data[i + 3] & 0xF0) == 0xE0) { // video packet - // skip PES header - int offs = i + 6; - // skip header extension - if ((Data[i + 6] & 0xC0) == 0x80) { - // MPEG-2 PES header - if (Data[i + 8] >= Length) - break; - offs += 3; - offs += Data[i + 8]; - len -= 3; - len -= Data[i + 8]; - if (len < 0 || offs + len > Length) - break; - } - else { - // MPEG-1 PES header - while (offs < Length && len > 0 && Data[offs] == 0xFF) { - offs++; - len--; - } - if (offs <= Length - 2 && len >= 2 && (Data[offs] & 0xC0) == 0x40) { - offs += 2; - len -= 2; - } - if (offs <= Length - 5 && len >= 5 && (Data[offs] & 0xF0) == 0x20) { - offs += 5; - len -= 5; - } - else if (offs <= Length - 10 && len >= 10 && (Data[offs] & 0xF0) == 0x30) { - offs += 10; - len -= 10; - } - else if (offs < Length && len > 0) { - offs++; - len--; - } - } - if (blen + len > Length) // invalid PES length field - break; - memcpy(&buf[blen], &Data[offs], len); - i = offs + len; - blen += len; - } - else if (Data[i + 3] >= 0xBD && Data[i + 3] <= 0xDF) // other PES packets - i += len + 6; - else - i++; - } - else - i++; - } - video_still_picture sp = { buf, blen }; - CHECK(ioctl(fd_video, VIDEO_STILLPICTURE, &sp)); - free(buf); - } - else { - // non-PES data - video_still_picture sp = { (char *)Data, Length }; - CHECK(ioctl(fd_video, VIDEO_STILLPICTURE, &sp)); - } -} - -bool cDvbSdFfDevice::Poll(cPoller &Poller, int TimeoutMs) -{ - Poller.Add((playMode == pmAudioOnly || playMode == pmAudioOnlyBlack) ? fd_audio : fd_video, true); - return Poller.Poll(TimeoutMs); -} - -bool cDvbSdFfDevice::Flush(int TimeoutMs) -{ - //TODO actually this function should wait until all buffered data has been processed by the card, but how? - return true; -} - -int cDvbSdFfDevice::PlayVideo(const uchar *Data, int Length) -{ - return WriteAllOrNothing(fd_video, Data, Length, 1000, 10); -} - -int cDvbSdFfDevice::PlayAudio(const uchar *Data, int Length, uchar Id) -{ - return WriteAllOrNothing(fd_audio, Data, Length, 1000, 10); -} - -int cDvbSdFfDevice::PlayTsVideo(const uchar *Data, int Length) -{ - return WriteAllOrNothing(fd_video, Data, Length, 1000, 10); -} - -int cDvbSdFfDevice::PlayTsAudio(const uchar *Data, int Length) -{ - return WriteAllOrNothing(fd_audio, Data, Length, 1000, 10); -} - -// --- cDvbSdFfDeviceProbe --------------------------------------------------- - -cDvbSdFfDeviceProbe::cDvbSdFfDeviceProbe(void) -{ - outputOnly = false; -} - -bool cDvbSdFfDeviceProbe::Probe(int Adapter, int Frontend) -{ - static uint32_t SubsystemIds[] = { - 0x110A0000, // Fujitsu Siemens DVB-C - 0x13C20000, // Technotrend/Hauppauge WinTV DVB-S rev1.X or Fujitsu Siemens DVB-C - 0x13C20001, // Technotrend/Hauppauge WinTV DVB-T rev1.X - 0x13C20002, // Technotrend/Hauppauge WinTV DVB-C rev2.X - 0x13C20003, // Technotrend/Hauppauge WinTV Nexus-S rev2.X - 0x13C20004, // Galaxis DVB-S rev1.3 - 0x13C20006, // Fujitsu Siemens DVB-S rev1.6 - 0x13C20008, // Technotrend/Hauppauge DVB-T - 0x13C2000A, // Technotrend/Hauppauge WinTV Nexus-CA rev1.X - 0x13C2000E, // Technotrend/Hauppauge WinTV Nexus-S rev2.3 - 0x13C21002, // Technotrend/Hauppauge WinTV DVB-S rev1.3 SE - 0x00000000 - }; - uint32_t SubsystemId = GetSubsystemId(Adapter, Frontend); - for (uint32_t *sid = SubsystemIds; *sid; sid++) { - if (*sid == SubsystemId) { - dsyslog("creating cDvbSdFfDevice"); - new cDvbSdFfDevice(Adapter, Frontend, outputOnly); - return true; - } - } - return false; -} diff --git a/PLUGINS/src/dvbsddevice/dvbsdffdevice.h b/PLUGINS/src/dvbsddevice/dvbsdffdevice.h deleted file mode 100644 index f7348d0f..00000000 --- a/PLUGINS/src/dvbsddevice/dvbsdffdevice.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * dvbsdffdevice.h: The DVB SD Full Featured device interface - * - * See the README file for copyright information and how to reach the author. - * - * $Id: dvbsdffdevice.h 3.2 2014/03/15 12:36:35 kls Exp $ - */ - -#ifndef __DVBSDFFDEVICE_H -#define __DVBSDFFDEVICE_H - -#include -#include - -/// The cDvbSdFfDevice implements a DVB device which can be accessed through the Linux DVB driver API. - -class cDvbSdFfDevice : public cDvbDevice { -private: - int fd_osd, fd_audio, fd_video, fd_stc; - bool outputOnly; -protected: - virtual void MakePrimaryDevice(bool On); -public: - cDvbSdFfDevice(int Adapter, int Frontend, bool OutputOnly); - virtual ~cDvbSdFfDevice(); - virtual bool HasDecoder(void) const; - virtual bool AvoidRecording(void) const; - -// SPU facilities - -private: - cDvbSpuDecoder *spuDecoder; -public: - virtual cSpuDecoder *GetSpuDecoder(void); - -// Channel facilities - -public: - virtual bool ProvidesSource(int Source) const; - virtual int NumProvidedSystems(void) const; -private: - void TurnOffLiveMode(bool LiveView); -protected: - virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView); - -// PID handle facilities - -private: - bool SetAudioBypass(bool On); -protected: - virtual bool SetPid(cPidHandle *Handle, int Type, bool On); - -// Image Grab facilities - -private: - static int devVideoOffset; - int devVideoIndex; -public: - virtual uchar *GrabImage(int &Size, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1); - -// Video format facilities - -public: - virtual void SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat); - virtual void SetVideoFormat(bool VideoFormat16_9); - virtual void GetVideoSize(int &Width, int &Height, double &VideoAspect); - virtual void GetOsdSize(int &Width, int &Height, double &PixelAspect); - -// Track facilities - -protected: - virtual void SetAudioTrackDevice(eTrackType Type); - -// Audio facilities - -private: - bool digitalAudio; -protected: - virtual int GetAudioChannelDevice(void); - virtual void SetAudioChannelDevice(int AudioChannel); - virtual void SetVolumeDevice(int Volume); - virtual void SetDigitalAudioDevice(bool On); - -// Player facilities - -protected: - ePlayMode playMode; - virtual bool CanReplay(void) const; - virtual bool SetPlayMode(ePlayMode PlayMode); - virtual int PlayVideo(const uchar *Data, int Length); - virtual int PlayAudio(const uchar *Data, int Length, uchar Id); - virtual int PlayTsVideo(const uchar *Data, int Length); - virtual int PlayTsAudio(const uchar *Data, int Length); -public: - virtual int64_t GetSTC(void); - virtual void TrickSpeed(int Speed, bool Forward); - virtual void Clear(void); - virtual void Play(void); - virtual void Freeze(void); - virtual void Mute(void); - virtual void StillPicture(const uchar *Data, int Length); - virtual bool Poll(cPoller &Poller, int TimeoutMs = 0); - virtual bool Flush(int TimeoutMs = 0); - }; - -class cDvbSdFfDeviceProbe : public cDvbDeviceProbe { -private: - bool outputOnly; -public: - cDvbSdFfDeviceProbe(void); - void SetOutputOnly(bool On) { outputOnly = On; } - virtual bool Probe(int Adapter, int Frontend); - }; - -#endif //__DVBSDFFDEVICE_H diff --git a/PLUGINS/src/dvbsddevice/dvbsdffosd.c b/PLUGINS/src/dvbsddevice/dvbsdffosd.c deleted file mode 100644 index 1a8caf89..00000000 --- a/PLUGINS/src/dvbsddevice/dvbsdffosd.c +++ /dev/null @@ -1,211 +0,0 @@ -/* - * dvbsdffosd.c: Implementation of the DVB SD Full Featured On Screen Display - * - * See the README file for copyright information and how to reach the author. - * - * $Id: dvbsdffosd.c 3.0 2011/04/17 12:55:09 kls Exp $ - */ - -#include "dvbsdffosd.h" -#include -#include -#include -#include -#include - -// --- cDvbSdFfOsd ----------------------------------------------------------- - -#define MAXNUMWINDOWS 7 // OSD windows are counted 1...7 -#define MAXOSDMEMORY 92000 // number of bytes available to the OSD (for unmodified DVB cards) - -class cDvbSdFfOsd : public cOsd { -private: - int osdDev; - int osdMem; - bool shown; - void Cmd(OSD_Command cmd, int color = 0, int x0 = 0, int y0 = 0, int x1 = 0, int y1 = 0, const void *data = NULL); -protected: - virtual void SetActive(bool On); -public: - cDvbSdFfOsd(int Left, int Top, int OsdDev, uint Level); - virtual ~cDvbSdFfOsd(); - virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas); - virtual eOsdError SetAreas(const tArea *Areas, int NumAreas); - virtual void Flush(void); - }; - -cDvbSdFfOsd::cDvbSdFfOsd(int Left, int Top, int OsdDev, uint Level) -:cOsd(Left, Top, Level) -{ - osdDev = OsdDev; - shown = false; - if (osdDev < 0) - esyslog("ERROR: invalid OSD device handle (%d)!", osdDev); - else { - osdMem = MAXOSDMEMORY; -#ifdef OSD_CAP_MEMSIZE - // modified DVB cards may have more OSD memory: - osd_cap_t cap; - cap.cmd = OSD_CAP_MEMSIZE; - if (ioctl(osdDev, OSD_GET_CAPABILITY, &cap) == 0) - osdMem = cap.val; -#endif - } -} - -cDvbSdFfOsd::~cDvbSdFfOsd() -{ - SetActive(false); -} - -void cDvbSdFfOsd::SetActive(bool On) -{ - if (On != Active()) { - cOsd::SetActive(On); - if (On) { - // must clear all windows here to avoid flashing effects - doesn't work if done - // in Flush() only for the windows that are actually used... - for (int i = 0; i < MAXNUMWINDOWS; i++) { - Cmd(OSD_SetWindow, 0, i + 1); - Cmd(OSD_Clear); - } - if (GetBitmap(0)) // only flush here if there are already bitmaps - Flush(); - } - else if (shown) { - for (int i = 0; GetBitmap(i); i++) { - Cmd(OSD_SetWindow, 0, i + 1); - Cmd(OSD_Close); - } - shown = false; - } - } -} - -eOsdError cDvbSdFfOsd::CanHandleAreas(const tArea *Areas, int NumAreas) -{ - eOsdError Result = cOsd::CanHandleAreas(Areas, NumAreas); - if (Result == oeOk) { - if (NumAreas > MAXNUMWINDOWS) - return oeTooManyAreas; - int TotalMemory = 0; - for (int i = 0; i < NumAreas; i++) { - if (Areas[i].bpp != 1 && Areas[i].bpp != 2 && Areas[i].bpp != 4 && Areas[i].bpp != 8) - 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) - return oeOutOfMemory; - } - return Result; -} - -eOsdError cDvbSdFfOsd::SetAreas(const tArea *Areas, int NumAreas) -{ - if (shown) { - for (int i = 0; GetBitmap(i); i++) { - Cmd(OSD_SetWindow, 0, i + 1); - Cmd(OSD_Close); - } - shown = false; - } - return cOsd::SetAreas(Areas, NumAreas); -} - -void cDvbSdFfOsd::Cmd(OSD_Command cmd, int color, int x0, int y0, int x1, int y1, const void *data) -{ - if (osdDev >= 0) { - osd_cmd_t dc; - dc.cmd = cmd; - dc.color = color; - dc.x0 = x0; - dc.y0 = y0; - dc.x1 = x1; - dc.y1 = y1; - dc.data = (void *)data; - ioctl(osdDev, OSD_SEND_CMD, &dc); - } -} - -void cDvbSdFfOsd::Flush(void) -{ - if (!Active()) - return; - cBitmap *Bitmap; - for (int i = 0; (Bitmap = GetBitmap(i)) != NULL; i++) { - Cmd(OSD_SetWindow, 0, i + 1); - if (!shown) - Cmd(OSD_Open, Bitmap->Bpp(), Left() + Bitmap->X0(), Top() + Bitmap->Y0(), Left() + Bitmap->X0() + Bitmap->Width() - 1, Top() + Bitmap->Y0() + Bitmap->Height() - 1, (void *)1); // initially hidden! - int x1 = 0, y1 = 0, x2 = 0, y2 = 0; - if (!shown || Bitmap->Dirty(x1, y1, x2, y2)) { - if (!shown) { - x1 = y1 = 0; - x2 = Bitmap->Width() - 1; - y2 = Bitmap->Height() - 1; - } - //TODO Workaround: apparently the bitmap sent to the driver always has to be a multiple - //TODO of 8 bits wide, and (dx * dy) also has to be a multiple of 8. - //TODO Fix driver (should be able to handle any size bitmaps!) - while ((x1 > 0 || x2 < Bitmap->Width() - 1) && ((x2 - x1) & 7) != 7) { - if (x2 < Bitmap->Width() - 1) - x2++; - else if (x1 > 0) - x1--; - } - //TODO "... / 2" <==> Bpp??? - while ((y1 > 0 || y2 < Bitmap->Height() - 1) && (((x2 - x1 + 1) * (y2 - y1 + 1) / 2) & 7) != 0) { - if (y2 < Bitmap->Height() - 1) - y2++; - else if (y1 > 0) - y1--; - } - while ((x1 > 0 || x2 < Bitmap->Width() - 1) && (((x2 - x1 + 1) * (y2 - y1 + 1) / 2) & 7) != 0) { - if (x2 < Bitmap->Width() - 1) - x2++; - else if (x1 > 0) - x1--; - } - // commit colors: - int NumColors; - const tColor *Colors = Bitmap->Colors(NumColors); - if (Colors) { - //TODO this should be fixed in the driver! - tColor colors[NumColors]; - for (int i = 0; i < NumColors; i++) { - // convert AARRGGBB to AABBGGRR (the driver expects the colors the wrong way): - colors[i] = (Colors[i] & 0xFF000000) | ((Colors[i] & 0x0000FF) << 16) | (Colors[i] & 0x00FF00) | ((Colors[i] & 0xFF0000) >> 16); - } - Colors = colors; - //TODO end of stuff that should be fixed in the driver - Cmd(OSD_SetPalette, 0, NumColors - 1, 0, 0, 0, Colors); - } - // commit modified data: - Cmd(OSD_SetBlock, Bitmap->Width(), x1, y1, x2, y2, Bitmap->Data(x1, y1)); - } - Bitmap->Clean(); - } - if (!shown) { - // Showing the windows in a separate loop to avoid seeing them come up one after another - for (int i = 0; (Bitmap = GetBitmap(i)) != NULL; i++) { - Cmd(OSD_SetWindow, 0, i + 1); - Cmd(OSD_MoveWindow, 0, Left() + Bitmap->X0(), Top() + Bitmap->Y0()); - } - shown = true; - } -} - -// --- cDvbOsdProvider ------------------------------------------------------- - -cDvbOsdProvider::cDvbOsdProvider(int OsdDev) -{ - osdDev = OsdDev; -} - -cOsd *cDvbOsdProvider::CreateOsd(int Left, int Top, uint Level) -{ - return new cDvbSdFfOsd(Left, Top, osdDev, Level); -} diff --git a/PLUGINS/src/dvbsddevice/dvbsdffosd.h b/PLUGINS/src/dvbsddevice/dvbsdffosd.h deleted file mode 100644 index b8322087..00000000 --- a/PLUGINS/src/dvbsddevice/dvbsdffosd.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * dvbsdffosd.h: Implementation of the DVB SD Full Featured On Screen Display - * - * See the README file for copyright information and how to reach the author. - * - * $Id: dvbsdffosd.h 3.0 2012/12/03 13:43:55 kls Exp $ - */ - -#ifndef __DVBSDFFODF_H -#define __DVBSDFFODF_H - -#include - -class cDvbOsdProvider : public cOsdProvider { -private: - int osdDev; -public: - cDvbOsdProvider(int OsdDev); - virtual cOsd *CreateOsd(int Left, int Top, uint Level); - }; - -#endif //__DVBSDFFODF_H diff --git a/PLUGINS/src/epgtableid0/Makefile b/PLUGINS/src/epgtableid0/Makefile index e3c0c7b3..161fefe4 100644 --- a/PLUGINS/src/epgtableid0/Makefile +++ b/PLUGINS/src/epgtableid0/Makefile @@ -1,7 +1,7 @@ # # Makefile for a Video Disk Recorder plugin # -# $Id: Makefile 3.1 2014/01/01 13:29:54 kls Exp $ +# $Id: Makefile 4.0 2014/01/01 13:29:54 kls Exp $ # The official name of this plugin. # This name will be used in the '-P...' option of VDR to load the plugin. diff --git a/PLUGINS/src/epgtableid0/epgtableid0.c b/PLUGINS/src/epgtableid0/epgtableid0.c index 01bdc83d..79960ae9 100644 --- a/PLUGINS/src/epgtableid0/epgtableid0.c +++ b/PLUGINS/src/epgtableid0/epgtableid0.c @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: epgtableid0.c 3.2 2015/02/17 13:12:18 kls Exp $ + * $Id: epgtableid0.c 4.0 2015/02/17 13:12:18 kls Exp $ */ #include diff --git a/PLUGINS/src/hello/Makefile b/PLUGINS/src/hello/Makefile index 455721fc..186eae4a 100644 --- a/PLUGINS/src/hello/Makefile +++ b/PLUGINS/src/hello/Makefile @@ -1,7 +1,7 @@ # # Makefile for a Video Disk Recorder plugin # -# $Id: Makefile 3.1 2014/01/01 13:29:54 kls Exp $ +# $Id: Makefile 4.0 2014/01/01 13:29:54 kls Exp $ # The official name of this plugin. # This name will be used in the '-P...' option of VDR to load the plugin. diff --git a/PLUGINS/src/hello/hello.c b/PLUGINS/src/hello/hello.c index 0bd511cb..2d686cd8 100644 --- a/PLUGINS/src/hello/hello.c +++ b/PLUGINS/src/hello/hello.c @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: hello.c 3.2 2015/02/17 13:12:26 kls Exp $ + * $Id: hello.c 4.0 2015/02/17 13:12:26 kls Exp $ */ #include diff --git a/PLUGINS/src/osddemo/HISTORY b/PLUGINS/src/osddemo/HISTORY index 40495b1c..099ca758 100644 --- a/PLUGINS/src/osddemo/HISTORY +++ b/PLUGINS/src/osddemo/HISTORY @@ -72,3 +72,10 @@ VDR Plugin 'osddemo' Revision History 2015-02-19: Version 2.2.0 - Official release. + +2015-03-08: Version 2.3.1 + +- Now using cOsd::MaxPixmapSize(). +- Fixed a vertical black line in the "TiledPixmaps" area on the rpihddevice OSD with + 1280x800 pixel (thanks to Thomas Reufer). +- Added a demo case for storing images (thanks to Thomas Reufer). diff --git a/PLUGINS/src/osddemo/Makefile b/PLUGINS/src/osddemo/Makefile index 30e3de47..980de2ae 100644 --- a/PLUGINS/src/osddemo/Makefile +++ b/PLUGINS/src/osddemo/Makefile @@ -1,7 +1,7 @@ # # Makefile for a Video Disk Recorder plugin # -# $Id: Makefile 3.1 2014/01/01 13:29:54 kls Exp $ +# $Id: Makefile 4.0 2014/01/01 13:29:54 kls Exp $ # The official name of this plugin. # This name will be used in the '-P...' option of VDR to load the plugin. diff --git a/PLUGINS/src/osddemo/osddemo.c b/PLUGINS/src/osddemo/osddemo.c index 6273f460..89fd6954 100644 --- a/PLUGINS/src/osddemo/osddemo.c +++ b/PLUGINS/src/osddemo/osddemo.c @@ -3,13 +3,13 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: osddemo.c 3.4 2015/02/17 13:12:36 kls Exp $ + * $Id: osddemo.c 4.3 2015/04/12 09:35:21 kls Exp $ */ #include #include -static const char *VERSION = "2.2.0"; +static const char *VERSION = "2.3.1"; static const char *DESCRIPTION = "Demo of arbitrary OSD setup"; static const char *MAINMENUENTRY = "Osd Demo"; @@ -90,6 +90,52 @@ void DrawSlopes(cOsd *Osd) Osd->Flush(); } +// --- DrawImages ------------------------------------------------------------ + +struct tOsdImageRef { + int image; + cSize size; + }; + +#define NUMOSDIMAGES 16 +#define NUMOSDIMAGEVARIANTS 8 + +void DrawImages(cOsd *Osd) +{ + // Create images: + cImage *images[NUMOSDIMAGEVARIANTS]; + for (int i = 0; i < NUMOSDIMAGEVARIANTS; i++) { + images[i] = new cImage(cSize( + i == 0 || i == 1 ? Osd->MaxPixmapSize().Width() + 1 : rand() % Osd->Width(), + i == 0 || i == 2 ? Osd->MaxPixmapSize().Height() + 1 : rand() % Osd->Height())); + for (int x = 0; x < images[i]->Width(); x++) { + for (int y = 0; y < images[i]->Height(); y++) { + images[i]->SetPixel(cPoint(x, y), + (!x || !y || x == images[i]->Width() - 1 || y == images[i]->Height() - 1) ? clrWhite : + (x > images[i]->Width() / 2 ? + (y > images[i]->Height() / 2 ? clrBlue : clrGreen) : + (y > images[i]->Height() / 2 ? clrRed : clrYellow))); + } + } + } + // Store images: + tOsdImageRef osdImages[NUMOSDIMAGES]; + for (int i = 0; i < NUMOSDIMAGES; i++) { + osdImages[i].image = cOsdProvider::StoreImage(*images[i % NUMOSDIMAGEVARIANTS]); + osdImages[i].size.Set(images[i % NUMOSDIMAGEVARIANTS]->Size()); + } + // Delete images: + for (int i = 0; i < NUMOSDIMAGEVARIANTS; i++) + delete images[i]; + // Draw images: + for (int i = 0; i < NUMOSDIMAGES; i++) + Osd->DrawImage(cPoint(rand() % (Osd->Width() + osdImages[i].size.Width()), rand() % (Osd->Height() + osdImages[i].size.Height())).Shifted(-osdImages[i].size.Width(), -osdImages[i].size.Height()), osdImages[i].image); + // Drop image references: + for (int i = 0; i < NUMOSDIMAGES; i++) + cOsdProvider::DropImage(osdImages[i].image); + Osd->Flush(); +} + // --- cLineGame ------------------------------------------------------------- class cLineGame : public cOsdObject { @@ -210,7 +256,7 @@ cPixmap *cTrueColorDemo::CreateTextPixmap(const char *s, int Line, int Layer, tC if (Pixmap) { Pixmap->Clear(); Pixmap->SetAlpha(0); - Pixmap->DrawText(cPoint(0, 0), s, ColorFg, ColorBg, Font); + Pixmap->DrawText(cPoint(0, 0), s, ColorFg, ColorBg, Font, w); } return Pixmap; } @@ -427,16 +473,20 @@ void cTrueColorDemo::Action(void) const int Size = SmlFont->Width(Text) + 10; const int NumDots = 12; const int AnimFrames = NumDots; + int Rows = min(osd->MaxPixmapSize().Height() / Size, AnimFrames); + int Cols = (AnimFrames + Rows - 1) / Rows; // Temporarily using pixmap layer 0 to have the text alpha blended: - AnimPixmap = osd->CreatePixmap(0, cRect((osd->Width() - Size) / 2, StartLine, Size, Size), cRect(0, 0, Size, Size * AnimFrames)); + AnimPixmap = osd->CreatePixmap(0, cRect((osd->Width() - Size) / 2, StartLine, Size, Size), cRect(0, 0, Size * Cols, Size * Rows)); if (AnimPixmap) { AnimPixmap->SetAlpha(0); AnimPixmap->Clear(); const int Diameter = Size / 5; - int xc = Size / 2 - Diameter / 2; for (int Frame = 0; Frame < AnimFrames; Frame++) { - AnimPixmap->DrawEllipse(cRect(0, Frame * Size, Size, Size), 0xDDFFFFFF); - int yc = Frame * Size + Size / 2 - Diameter / 2; + int x0 = Frame / Rows * Size; + int y0 = Frame % Rows * Size; + AnimPixmap->DrawEllipse(cRect(x0, y0, Size, Size), 0xDDFFFFFF); + int xc = x0 + Size / 2 - Diameter / 2; + int yc = y0 + Size / 2 - Diameter / 2; int Color = 0xFF; int Delta = Color / NumDots / 3; for (int a = 0; a < NumDots; a++) { @@ -446,7 +496,7 @@ void cTrueColorDemo::Action(void) AnimPixmap->DrawEllipse(cRect(x, y, Diameter, Diameter), ArgbToColor(0xFF, Color, Color, Color)); Color -= Delta; } - AnimPixmap->DrawText(cPoint(0, Frame * Size), Text, clrBlack, clrTransparent, SmlFont, Size, Size, taCenter); + AnimPixmap->DrawText(cPoint(x0, y0), Text, clrBlack, clrTransparent, SmlFont, Size, Size, taCenter); } AnimPixmap->SetLayer(3); // now setting the actual pixmap layer FadeInPixmap = AnimPixmap; @@ -536,6 +586,10 @@ eOSState cTrueColorDemo::ProcessKey(eKeys Key) SetArea(); DrawSlopes(osd); break; + case k3: Cancel(3); + SetArea(); + DrawImages(osd); + break; case kBack: case kOk: return osEnd; default: return state; diff --git a/PLUGINS/src/pictures/HISTORY b/PLUGINS/src/pictures/HISTORY index 06e7415a..b9e476e2 100644 --- a/PLUGINS/src/pictures/HISTORY +++ b/PLUGINS/src/pictures/HISTORY @@ -99,3 +99,7 @@ VDR Plugin 'pictures' Revision History 2015-02-19: Version 2.2.0 - Official release. + +2015-07.23: Version 2.3.1 + +- Added a missing 'const'. diff --git a/PLUGINS/src/pictures/Makefile b/PLUGINS/src/pictures/Makefile index 912e3d97..e749e981 100644 --- a/PLUGINS/src/pictures/Makefile +++ b/PLUGINS/src/pictures/Makefile @@ -1,7 +1,7 @@ # # Makefile for a Video Disk Recorder plugin # -# $Id: Makefile 3.1 2014/01/01 13:29:54 kls Exp $ +# $Id: Makefile 4.0 2014/01/01 13:29:54 kls Exp $ # The official name of this plugin. # This name will be used in the '-P...' option of VDR to load the plugin. diff --git a/PLUGINS/src/pictures/entry.c b/PLUGINS/src/pictures/entry.c index ce2a3e2b..73406417 100644 --- a/PLUGINS/src/pictures/entry.c +++ b/PLUGINS/src/pictures/entry.c @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: entry.c 3.0 2012/02/17 14:00:28 kls Exp $ + * $Id: entry.c 4.0 2012/02/17 14:00:28 kls Exp $ */ #include "entry.h" diff --git a/PLUGINS/src/pictures/entry.h b/PLUGINS/src/pictures/entry.h index 9de4dd3a..667173ed 100644 --- a/PLUGINS/src/pictures/entry.h +++ b/PLUGINS/src/pictures/entry.h @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: entry.h 3.0 2008/01/06 12:30:50 kls Exp $ + * $Id: entry.h 4.0 2008/01/06 12:30:50 kls Exp $ */ #ifndef _ENTRY_H diff --git a/PLUGINS/src/pictures/menu.c b/PLUGINS/src/pictures/menu.c index 567f9e7b..2599526e 100644 --- a/PLUGINS/src/pictures/menu.c +++ b/PLUGINS/src/pictures/menu.c @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: menu.c 3.0 2008/01/13 11:35:18 kls Exp $ + * $Id: menu.c 4.1 2015/07/23 10:13:56 kls Exp $ */ #include "menu.h" @@ -85,7 +85,7 @@ eOSState cPictureMenu::SelectItem(const char *Path, bool SlideShow) if (Item) { const cList *l = pictureEntry->Entries(); if (l) { - cPictureEntry *pe = l->Get(Current()); + const cPictureEntry *pe = l->Get(Current()); if (pe) { if (SlideShow) { cControl::Launch(new cPictureControl(pictures, pe, true)); diff --git a/PLUGINS/src/pictures/menu.h b/PLUGINS/src/pictures/menu.h index 585b0d57..a9a3fa61 100644 --- a/PLUGINS/src/pictures/menu.h +++ b/PLUGINS/src/pictures/menu.h @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: menu.h 3.0 2008/01/12 11:22:52 kls Exp $ + * $Id: menu.h 4.0 2008/01/12 11:22:52 kls Exp $ */ #ifndef _MENU_H diff --git a/PLUGINS/src/pictures/pic2mpg b/PLUGINS/src/pictures/pic2mpg index 8bb6c2e5..27d0264a 100755 --- a/PLUGINS/src/pictures/pic2mpg +++ b/PLUGINS/src/pictures/pic2mpg @@ -7,7 +7,7 @@ # # See the README file for copyright information and how to reach the author. # -# $Id: pic2mpg 3.1 2013/07/01 08:33:38 kls Exp $ +# $Id: pic2mpg 4.0 2013/07/01 08:33:38 kls Exp $ use File::Path; use File::Spec; diff --git a/PLUGINS/src/pictures/pictures.c b/PLUGINS/src/pictures/pictures.c index 46a08716..509514ea 100644 --- a/PLUGINS/src/pictures/pictures.c +++ b/PLUGINS/src/pictures/pictures.c @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: pictures.c 3.2 2015/02/17 13:12:50 kls Exp $ + * $Id: pictures.c 4.1 2015/07/23 10:14:22 kls Exp $ */ #include @@ -11,7 +11,7 @@ #include "menu.h" #include "player.h" -static const char *VERSION = "2.2.0"; +static const char *VERSION = "2.3.1"; static const char *DESCRIPTION = trNOOP("A simple picture viewer"); static const char *MAINMENUENTRY = trNOOP("Pictures"); diff --git a/PLUGINS/src/pictures/player.c b/PLUGINS/src/pictures/player.c index d2dbd7b8..82fd6995 100644 --- a/PLUGINS/src/pictures/player.c +++ b/PLUGINS/src/pictures/player.c @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: player.c 3.1 2014/02/08 12:48:12 kls Exp $ + * $Id: player.c 4.0 2014/02/08 12:48:12 kls Exp $ */ #include "player.h" diff --git a/PLUGINS/src/pictures/player.h b/PLUGINS/src/pictures/player.h index cf18d355..6c6b9171 100644 --- a/PLUGINS/src/pictures/player.h +++ b/PLUGINS/src/pictures/player.h @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: player.h 3.0 2012/04/28 11:56:01 kls Exp $ + * $Id: player.h 4.0 2012/04/28 11:56:01 kls Exp $ */ #ifndef _PLAYER_H diff --git a/PLUGINS/src/rcu/COPYING b/PLUGINS/src/rcu/COPYING deleted file mode 100644 index f90922ee..00000000 --- a/PLUGINS/src/rcu/COPYING +++ /dev/null @@ -1,340 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/PLUGINS/src/rcu/HISTORY b/PLUGINS/src/rcu/HISTORY deleted file mode 100644 index 8fabe098..00000000 --- a/PLUGINS/src/rcu/HISTORY +++ /dev/null @@ -1,30 +0,0 @@ -VDR Plugin 'rcu' Revision History ---------------------------------- - -2012-02-27: Version 0.0.1 - -- Initial revision. - -2012-03-07: Version 0.0.2 - -- Added new parameter LiveView to ChannelSwitch(). - -2012-12-27: Version 0.0.3 - -- Adapted Makefile to changes introduced in recent VDR versions. - -2013-01-12: Version 0.0.4 - -- Adapted Makefile to changes introduced in recent VDR versions. - -2013-03-31: Version 2.0.0 - -- Official release. - -2014-01-01: Version 2.1.1 - -- Avoiding unnecessary pkg-config warnings in plugin Makefiles. - -2015-02-19: Version 2.2.0 - -- Official release. diff --git a/PLUGINS/src/rcu/Makefile b/PLUGINS/src/rcu/Makefile deleted file mode 100644 index 25bde4af..00000000 --- a/PLUGINS/src/rcu/Makefile +++ /dev/null @@ -1,94 +0,0 @@ -# -# Makefile for a Video Disk Recorder plugin -# -# $Id: Makefile 3.1 2014/01/01 13:29:54 kls Exp $ - -# The official name of this plugin. -# This name will be used in the '-P...' option of VDR to load the plugin. -# By default the main source file also carries this name. - -PLUGIN = rcu - -### The version number of this plugin (taken from the main source file): - -VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g') - -### The directory environment: - -# Use package data if installed...otherwise assume we're under the VDR source directory: -PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell PKG_CONFIG_PATH="$$PKG_CONFIG_PATH:../../.." pkg-config --variable=$(1) vdr)) -LIBDIR = $(call PKGCFG,libdir) -PLGCFG = $(call PKGCFG,plgcfg) -# -TMPDIR ?= /tmp - -### The compiler options: - -export CFLAGS = $(call PKGCFG,cflags) -export CXXFLAGS = $(call PKGCFG,cxxflags) - -### The version number of VDR's plugin API: - -APIVERSION = $(call PKGCFG,apiversion) - -### Allow user defined options to overwrite defaults: - --include $(PLGCFG) - -### The name of the distribution archive: - -ARCHIVE = $(PLUGIN)-$(VERSION) -PACKAGE = vdr-$(ARCHIVE) - -### The name of the shared object file: - -SOFILE = libvdr-$(PLUGIN).so - -### Includes and Defines (add further entries here): - -INCLUDES += - -DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' - -### The object files (add further files here): - -OBJS = $(PLUGIN).o - -### The main target: - -all: $(SOFILE) - -### Implicit rules: - -%.o: %.c - $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $< - -### Dependencies: - -MAKEDEP = $(CXX) -MM -MG -DEPFILE = .dependencies -$(DEPFILE): Makefile - @$(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@ - --include $(DEPFILE) - -### Targets: - -$(SOFILE): $(OBJS) - $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) -o $@ - -install-lib: $(SOFILE) - install -D $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION) - -install: install-lib - -dist: clean - @-rm -rf $(TMPDIR)/$(ARCHIVE) - @mkdir $(TMPDIR)/$(ARCHIVE) - @cp -a * $(TMPDIR)/$(ARCHIVE) - @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE) - @-rm -rf $(TMPDIR)/$(ARCHIVE) - @echo Distribution package created as $(PACKAGE).tgz - -clean: - @-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~ diff --git a/PLUGINS/src/rcu/README b/PLUGINS/src/rcu/README deleted file mode 100644 index d66729ee..00000000 --- a/PLUGINS/src/rcu/README +++ /dev/null @@ -1,19 +0,0 @@ -This is a "plugin" for the Video Disk Recorder (VDR). - -Written by: Klaus Schmidinger - -Project's homepage: http://www.tvdr.de/remote.htm - -Latest version available at: http://www.tvdr.de - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. -See the file COPYING for more information. - -Description: -============ - -The "Remote Control Unit" used to be part of the core VDR source -code, and has been moved into a separate plugin in version 1.7.25. diff --git a/PLUGINS/src/rcu/rcu.c b/PLUGINS/src/rcu/rcu.c deleted file mode 100644 index 9e833b19..00000000 --- a/PLUGINS/src/rcu/rcu.c +++ /dev/null @@ -1,420 +0,0 @@ -/* - * rcu.c: A plugin for the Video Disk Recorder - * - * See the README file for copyright information and how to reach the author. - * - * $Id: rcu.c 3.2 2015/02/17 13:13:00 kls Exp $ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static const char *VERSION = "2.2.0"; -static const char *DESCRIPTION = "Remote Control Unit"; - -#define REPEATLIMIT 150 // ms -#define REPEATDELAY 350 // ms -#define HANDSHAKETIMEOUT 20 // ms -#define DEFAULTDEVICE "/dev/ttyS1" - -class cRcuRemote : public cRemote, private cThread, private cStatus { -private: - enum { modeH = 'h', modeB = 'b', modeS = 's' }; - int f; - unsigned char dp, code, mode; - 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 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); - void SetString(const char *s); - bool DetectCode(unsigned char *Code); - virtual void Action(void); - virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber, bool LiveView); - virtual void Recording(const cDevice *Device, const char *Name, const char *FileName, bool On); -public: - cRcuRemote(const char *DeviceName); - virtual ~cRcuRemote(); - virtual bool Ready(void); - virtual bool Initialize(void); - }; - -cRcuRemote::cRcuRemote(const char *DeviceName) -:cRemote("RCU") -,cThread("RCU remote control") -{ - dp = 0; - mode = modeB; - code = 0; - number = 0; - data = 0; - receivedCommand = false; - if ((f = open(DeviceName, O_RDWR | O_NONBLOCK)) >= 0) { - struct termios t; - if (tcgetattr(f, &t) == 0) { - cfsetspeed(&t, B9600); - cfmakeraw(&t); - if (tcsetattr(f, TCSAFLUSH, &t) == 0) { - SetNumber(8888); - const char *Setup = GetSetup(); - if (Setup) { - code = *Setup; - SetCode(code); - isyslog("connecting to %s remote control using code %c", Name(), code); - } - Start(); - return; - } - } - LOG_ERROR_STR(DeviceName); - close(f); - } - else - LOG_ERROR_STR(DeviceName); - f = -1; -} - -cRcuRemote::~cRcuRemote() -{ - Cancel(); -} - -bool cRcuRemote::Ready(void) -{ - return f >= 0; -} - -bool cRcuRemote::Initialize(void) -{ - if (f >= 0) { - unsigned char Code = '0'; - isyslog("trying codes for %s remote control...", Name()); - for (;;) { - if (DetectCode(&Code)) { - code = Code; - break; - } - } - isyslog("established connection to %s remote control using code %c", Name(), code); - char buffer[16]; - snprintf(buffer, sizeof(buffer), "%c", code); - PutSetup(buffer); - return true; - } - return false; -} - -void cRcuRemote::Action(void) -{ -#pragma pack(1) - union { - struct { - unsigned short address; - unsigned int command; - } data; - unsigned char raw[6]; - } buffer; -#pragma pack() - - time_t LastCodeRefresh = 0; - cTimeMs FirstTime; - unsigned char LastCode = 0, LastMode = 0; - uint64_t LastCommand = ~0; // 0x00 might be a valid command - unsigned int LastData = 0; - bool repeat = false; - - while (Running() && f >= 0) { - if (ReceiveByte(REPEATLIMIT) == 'X') { - for (int i = 0; i < 6; i++) { - int b = ReceiveByte(); - if (b >= 0) { - buffer.raw[i] = b; - if (i == 5) { - unsigned short Address = ntohs(buffer.data.address); // the PIC sends bytes in "network order" - uint64_t Command = ntohl(buffer.data.command); - if (code == 'B' && Address == 0x0000 && Command == 0x00004000) - // Well, well, if it isn't the "d-box"... - // This remote control sends the above command before and after - // each keypress - let's just drop this: - break; - Command |= uint64_t(Address) << 32; - if (Command != LastCommand) { - LastCommand = Command; - repeat = false; - FirstTime.Set(); - } - else { - if (FirstTime.Elapsed() < REPEATDELAY) - break; // repeat function kicks in after a short delay - repeat = true; - } - Put(Command, repeat); - receivedCommand = true; - } - } - else - break; - } - } - else if (repeat) { // the last one was a repeat, so let's generate a release - Put(LastCommand, false, true); - repeat = false; - LastCommand = ~0; - } - else { - 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 (!repeat && code && time(NULL) - LastCodeRefresh > 60) { - SendCommand(code); // in case the PIC listens to the wrong code - LastCodeRefresh = time(NULL); - } - } -} - -int cRcuRemote::ReceiveByte(int TimeoutMs) -{ - // Returns the byte if one was received within a timeout, -1 otherwise - if (cFile::FileReady(f, TimeoutMs)) { - unsigned char b; - if (safe_read(f, &b, 1) == 1) - return b; - else - LOG_ERROR; - } - return -1; -} - -bool cRcuRemote::SendByteHandshake(unsigned char c) -{ - if (f >= 0) { - int w = write(f, &c, 1); - if (w == 1) { - for (int reply = ReceiveByte(HANDSHAKETIMEOUT); reply >= 0;) { - if (reply == c) - return true; - else if (reply == 'X') { - // skip any incoming RC code - it will come again - for (int i = 6; i--;) { - if (ReceiveByte() < 0) - return false; - } - } - else - return false; - } - } - LOG_ERROR; - } - return false; -} - -bool cRcuRemote::SendByte(unsigned char c) -{ - for (int retry = 5; retry--;) { - if (SendByteHandshake(c)) - return true; - } - return false; -} - -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; -} - -void cRcuRemote::SetMode(unsigned char Mode) -{ - mode = Mode; -} - -bool cRcuRemote::SendCommand(unsigned char Cmd) -{ - return SendByte(Cmd | 0x80); -} - -void cRcuRemote::SetNumber(int n, bool Hex) -{ - number = n; - if (!Hex) { - char buf[8]; - sprintf(buf, "%4d", n & 0xFFFF); - n = 0; - for (char *d = buf; *d; d++) { - if (*d == ' ') - *d = 0xF; - n = (n << 4) | ((*d - '0') & 0x0F); - } - } - unsigned int m = 0; - for (int i = 0; i < 4; i++) { - m <<= 8; - m |= ((i & 0x03) << 5) | (n & 0x0F) | (((dp >> i) & 0x01) << 4); - n >>= 4; - } - data = m; -} - -void cRcuRemote::SetString(const char *s) -{ - const char *chars = mode == modeH ? "0123456789ABCDEF" : "0123456789-EHLP "; - int n = 0; - - for (int i = 0; *s && i < 4; s++, i++) { - n <<= 4; - for (const char *c = chars; *c; c++) { - if (*c == *s) { - n |= c - chars; - break; - } - } - } - SetNumber(n, true); -} - -void cRcuRemote::SetPoints(unsigned char Dp, bool On) -{ - if (On) - dp |= Dp; - else - dp &= ~Dp; - SetNumber(number); -} - -bool cRcuRemote::DetectCode(unsigned char *Code) -{ - // Caller should initialize 'Code' to 0 and call DetectCode() - // until it returns true. Whenever DetectCode() returns false - // and 'Code' is not 0, the caller can use 'Code' to display - // a message like "Trying code '%c'". If false is returned and - // 'Code' is 0, all possible codes have been tried and the caller - // can either stop calling DetectCode() (and give some error - // message), or start all over again. - if (*Code < 'A' || *Code > 'D') { - *Code = 'A'; - return false; - } - if (*Code <= 'D') { - SetMode(modeH); - char buf[5]; - sprintf(buf, "C0D%c", *Code); - SetString(buf); - SetCode(*Code); - cCondWait::SleepMs(2 * REPEATDELAY); - if (receivedCommand) { - SetMode(modeB); - SetString("----"); - return true; - } - if (*Code < 'D') { - (*Code)++; - return false; - } - } - *Code = 0; - return false; -} - -void cRcuRemote::ChannelSwitch(const cDevice *Device, int ChannelNumber, bool LiveView) -{ - if (ChannelNumber && LiveView) - SetNumber(cDevice::CurrentChannel()); -} - -void cRcuRemote::Recording(const cDevice *Device, const char *Name, const char *FileName, bool On) -{ - SetPoints(1 << Device->DeviceNumber(), Device->Receiving()); -} - -class cPluginRcu : public cPlugin { -private: - // Add any member variables or functions you may need here. - const char *device; -public: - cPluginRcu(void); - virtual const char *Version(void) { return VERSION; } - virtual const char *Description(void) { return DESCRIPTION; } - virtual const char *CommandLineHelp(void); - virtual bool ProcessArgs(int argc, char *argv[]); - virtual bool Start(void); - }; - -cPluginRcu::cPluginRcu(void) -{ - // Initialize any member variables here. - // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL - // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT! - device = DEFAULTDEVICE; -} - -const char *cPluginRcu::CommandLineHelp(void) -{ - // Return a string that describes all known command line options. - return " -d DEV, --device=DEV set the device to use (default is " DEFAULTDEVICE ")\n"; -} - -bool cPluginRcu::ProcessArgs(int argc, char *argv[]) -{ - // Implement command line argument processing here if applicable. - static struct option long_options[] = { - { "dev", required_argument, NULL, 'd' }, - { NULL, no_argument, NULL, 0 } - }; - - int c; - while ((c = getopt_long(argc, argv, "d:", long_options, NULL)) != -1) { - switch (c) { - case 'd': device = optarg; - break; - default: return false; - } - } - return true; -} - -bool cPluginRcu::Start(void) -{ - // Start any background activities the plugin shall perform. - new cRcuRemote(device); - return true; -} - -VDRPLUGINCREATOR(cPluginRcu); // Don't touch this! diff --git a/PLUGINS/src/servicedemo/Makefile b/PLUGINS/src/servicedemo/Makefile index 128b9c75..c021fad0 100644 --- a/PLUGINS/src/servicedemo/Makefile +++ b/PLUGINS/src/servicedemo/Makefile @@ -1,7 +1,7 @@ # # Makefile for a Video Disk Recorder plugin # -# $Id: Makefile 3.1 2014/01/01 13:29:54 kls Exp $ +# $Id: Makefile 4.0 2014/01/01 13:29:54 kls Exp $ # The official name of this plugin. # This name will be used in the '-P...' option of VDR to load the plugin. diff --git a/PLUGINS/src/servicedemo/svccli.c b/PLUGINS/src/servicedemo/svccli.c index 9a424403..201cf26a 100644 --- a/PLUGINS/src/servicedemo/svccli.c +++ b/PLUGINS/src/servicedemo/svccli.c @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: svccli.c 3.2 2015/02/17 13:13:05 kls Exp $ + * $Id: svccli.c 4.0 2015/02/17 13:13:05 kls Exp $ */ #include diff --git a/PLUGINS/src/servicedemo/svcsvr.c b/PLUGINS/src/servicedemo/svcsvr.c index 5c48b49a..3876d465 100644 --- a/PLUGINS/src/servicedemo/svcsvr.c +++ b/PLUGINS/src/servicedemo/svcsvr.c @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: svcsvr.c 3.2 2015/02/17 13:13:09 kls Exp $ + * $Id: svcsvr.c 4.0 2015/02/17 13:13:09 kls Exp $ */ #include diff --git a/PLUGINS/src/skincurses/Makefile b/PLUGINS/src/skincurses/Makefile index 812fe973..27dafe44 100644 --- a/PLUGINS/src/skincurses/Makefile +++ b/PLUGINS/src/skincurses/Makefile @@ -1,7 +1,7 @@ # # Makefile for a Video Disk Recorder plugin # -# $Id: Makefile 3.1 2014/01/01 13:29:54 kls Exp $ +# $Id: Makefile 4.0 2014/01/01 13:29:54 kls Exp $ # The official name of this plugin. # This name will be used in the '-P...' option of VDR to load the plugin. diff --git a/PLUGINS/src/skincurses/skincurses.c b/PLUGINS/src/skincurses/skincurses.c index 2d92a574..358035e0 100644 --- a/PLUGINS/src/skincurses/skincurses.c +++ b/PLUGINS/src/skincurses/skincurses.c @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: skincurses.c 3.3 2015/02/17 13:13:17 kls Exp $ + * $Id: skincurses.c 4.0 2015/02/17 13:13:17 kls Exp $ */ #include diff --git a/PLUGINS/src/status/Makefile b/PLUGINS/src/status/Makefile index 40a0351e..6c997d72 100644 --- a/PLUGINS/src/status/Makefile +++ b/PLUGINS/src/status/Makefile @@ -1,7 +1,7 @@ # # Makefile for a Video Disk Recorder plugin # -# $Id: Makefile 3.1 2014/01/01 13:29:54 kls Exp $ +# $Id: Makefile 4.0 2014/01/01 13:29:54 kls Exp $ # The official name of this plugin. # This name will be used in the '-P...' option of VDR to load the plugin. diff --git a/PLUGINS/src/status/status.c b/PLUGINS/src/status/status.c index a5575cce..ec50b456 100644 --- a/PLUGINS/src/status/status.c +++ b/PLUGINS/src/status/status.c @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: status.c 3.2 2015/02/17 13:13:21 kls Exp $ + * $Id: status.c 4.0 2015/02/17 13:13:21 kls Exp $ */ #include diff --git a/PLUGINS/src/svdrpdemo/Makefile b/PLUGINS/src/svdrpdemo/Makefile index 9a8235a4..91fad09a 100644 --- a/PLUGINS/src/svdrpdemo/Makefile +++ b/PLUGINS/src/svdrpdemo/Makefile @@ -1,7 +1,7 @@ # # Makefile for a Video Disk Recorder plugin # -# $Id: Makefile 3.1 2014/01/01 13:29:54 kls Exp $ +# $Id: Makefile 4.0 2014/01/01 13:29:54 kls Exp $ # The official name of this plugin. # This name will be used in the '-P...' option of VDR to load the plugin. diff --git a/PLUGINS/src/svdrpdemo/svdrpdemo.c b/PLUGINS/src/svdrpdemo/svdrpdemo.c index 9114854b..bde85597 100644 --- a/PLUGINS/src/svdrpdemo/svdrpdemo.c +++ b/PLUGINS/src/svdrpdemo/svdrpdemo.c @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: svdrpdemo.c 3.2 2015/02/17 13:13:29 kls Exp $ + * $Id: svdrpdemo.c 4.0 2015/02/17 13:13:29 kls Exp $ */ #include diff --git a/README b/README index f88b9172..25e4dd31 100644 --- a/README +++ b/README @@ -32,6 +32,10 @@ of commercial set-top boxes usually are a lot more fancy than the ones in this system, but here we have the full source code and can modify the menus in whatever way desired. +If you actually use VDR, please add yourself to the "VDR User Counter" +at http://www.tvdr.de/counter.htm. You can also like VDR on facebook +at https://www.facebook.com/VideoDiskRecorder. + "VDR Video Disk Recorder" is a registered trademark of Klaus Schmidinger. "TVDR TV Done Right" is a registered trademark of Klaus Schmidinger. diff --git a/args.c b/args.c index 6a316055..5926f6ca 100644 --- a/args.c +++ b/args.c @@ -6,7 +6,7 @@ * * Original version written by Lars Hanisch . * - * $Id: args.c 1.1 2014/04/14 12:02:38 kls Exp $ + * $Id: args.c 4.0 2014/04/14 12:02:38 kls Exp $ */ #include "args.h" diff --git a/args.h b/args.h index 2e91afb4..79593c59 100644 --- a/args.h +++ b/args.h @@ -6,7 +6,7 @@ * * Original version written by Lars Hanisch . * - * $Id: args.h 1.1 2014/04/14 11:54:21 kls Exp $ + * $Id: args.h 4.0 2014/04/14 11:54:21 kls Exp $ */ #ifndef __ARGS_H diff --git a/audio.c b/audio.c index ebb98ed5..afd24941 100644 --- a/audio.c +++ b/audio.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: audio.c 3.0 2010/05/16 13:30:11 kls Exp $ + * $Id: audio.c 4.0 2010/05/16 13:30:11 kls Exp $ */ #include "audio.h" diff --git a/audio.h b/audio.h index 37d0b516..a484bfea 100644 --- a/audio.h +++ b/audio.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: audio.h 3.0 2008/07/06 11:39:21 kls Exp $ + * $Id: audio.h 4.0 2008/07/06 11:39:21 kls Exp $ */ #ifndef __AUDIO_H diff --git a/channels.c b/channels.c index 564088f4..24fec5be 100644 --- a/channels.c +++ b/channels.c @@ -4,15 +4,13 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: channels.c 3.8 2015/02/01 13:47:05 kls Exp $ + * $Id: channels.c 4.3 2015/09/09 10:21:22 kls Exp $ */ #include "channels.h" #include #include "device.h" -#include "epg.h" #include "libsi/si.h" -#include "timers.h" // IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d' // format characters in order to allow any number of blanks after a numeric @@ -79,27 +77,13 @@ cChannel::cChannel(const cChannel &Channel) schedule = NULL; linkChannels = NULL; refChannel = NULL; + seen = 0; *this = Channel; } cChannel::~cChannel() { - delete linkChannels; - linkChannels = NULL; // more than one channel can link to this one, so we need the following loop - for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { - if (Channel->linkChannels) { - for (cLinkChannel *lc = Channel->linkChannels->First(); lc; lc = Channel->linkChannels->Next(lc)) { - if (lc->Channel() == this) { - Channel->linkChannels->Del(lc); - break; - } - } - if (Channel->linkChannels->Count() == 0) { - delete Channel->linkChannels; - Channel->linkChannels = NULL; - } - } - } + delete linkChannels; // any links from other channels pointing to this one have been deleted in cChannels::Del() free(name); free(shortName); free(provider); @@ -167,16 +151,7 @@ int cChannel::Transponder(void) const return tf; } -bool cChannel::HasTimer(void) const -{ - for (cTimer *Timer = Timers.First(); Timer; Timer = Timers.Next(Timer)) { - if (Timer->Channel() == this) - return true; - } - return false; -} - -int cChannel::Modification(int Mask) +int cChannel::Modification(int Mask) const { int Result = modification & Mask; modification = CHANNELMOD_NONE; @@ -223,41 +198,57 @@ bool cChannel::SetTransponderData(int Source, int Frequency, int Srate, const ch if (Number() && !Quiet) { dsyslog("changing transponder data of channel %d (%s) from %s to %s", Number(), name, *OldTransponderData, *TransponderDataToString()); modification |= CHANNELMOD_TRANSP; - Channels.SetModified(); } + return true; } - return true; + return false; } -void cChannel::SetId(int Nid, int Tid, int Sid, int Rid) +bool cChannel::SetSource(int Source) +{ + if (source != Source) { + if (Number()) { + dsyslog("changing source of channel %d (%s) from %s to %s", Number(), name, *cSource::ToString(source), *cSource::ToString(Source)); + modification |= CHANNELMOD_TRANSP; + } + source = Source; + return true; + } + return false; +} + +bool cChannel::SetId(cChannels *Channels, int Nid, int Tid, int Sid, int Rid) { if (nid != Nid || tid != Tid || sid != Sid || rid != Rid) { - if (Number()) { + if (Channels && Number()) { dsyslog("changing id of channel %d (%s) from %d-%d-%d-%d to %d-%d-%d-%d", Number(), name, nid, tid, sid, rid, Nid, Tid, Sid, Rid); modification |= CHANNELMOD_ID; - Channels.SetModified(); - Channels.UnhashChannel(this); + Channels->UnhashChannel(this); } nid = Nid; tid = Tid; sid = Sid; rid = Rid; - if (Number()) - Channels.HashChannel(this); + if (Channels) + Channels->HashChannel(this); schedule = NULL; + return true; } + return false; } -void cChannel::SetLcn(int Lcn) +bool cChannel::SetLcn(int Lcn) { if (lcn != Lcn) { if (Number()) dsyslog("changing lcn of channel %d (%s) from %d to %d\n", Number(), name, lcn, Lcn); lcn = Lcn; + return true; } + return false; } -void cChannel::SetName(const char *Name, const char *ShortName, const char *Provider) +bool cChannel::SetName(const char *Name, const char *ShortName, const char *Provider) { if (!isempty(Name)) { bool nn = strcmp(name, Name) != 0; @@ -267,7 +258,6 @@ void cChannel::SetName(const char *Name, const char *ShortName, const char *Prov if (Number()) { dsyslog("changing name of channel %d from '%s,%s;%s' to '%s,%s;%s'", Number(), name, shortName, provider, Name, ShortName, Provider); modification |= CHANNELMOD_NAME; - Channels.SetModified(); } if (nn) { name = strcpyrealloc(name, Name); @@ -279,20 +269,23 @@ void cChannel::SetName(const char *Name, const char *ShortName, const char *Prov } if (np) provider = strcpyrealloc(provider, Provider); + return true; } } + return false; } -void cChannel::SetPortalName(const char *PortalName) +bool cChannel::SetPortalName(const char *PortalName) { if (!isempty(PortalName) && strcmp(portalName, PortalName) != 0) { if (Number()) { dsyslog("changing portal name of channel %d (%s) from '%s' to '%s'", Number(), name, portalName, PortalName); modification |= CHANNELMOD_NAME; - Channels.SetModified(); } portalName = strcpyrealloc(portalName, PortalName); + return true; } + return false; } #define STRDIFF 0x01 @@ -337,7 +330,7 @@ static int IntArrayToString(char *s, const int *a, int Base = 10, const char n[] return q - s; } -void cChannel::SetPids(int Vpid, int Ppid, int Vtype, int *Apids, int *Atypes, char ALangs[][MAXLANGCODE2], int *Dpids, int *Dtypes, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid) +bool cChannel::SetPids(int Vpid, int Ppid, int Vtype, int *Apids, int *Atypes, char ALangs[][MAXLANGCODE2], int *Dpids, int *Dtypes, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid) { int mod = CHANNELMOD_NONE; if (vpid != Vpid || ppid != Ppid || vtype != Vtype) @@ -400,25 +393,33 @@ void cChannel::SetPids(int Vpid, int Ppid, int Vtype, int *Apids, int *Atypes, c spids[MAXSPIDS] = 0; tpid = Tpid; modification |= mod; - if (Number()) - Channels.SetModified(); + return true; } + return false; } -void cChannel::SetSubtitlingDescriptors(uchar *SubtitlingTypes, uint16_t *CompositionPageIds, uint16_t *AncillaryPageIds) +bool cChannel::SetSubtitlingDescriptors(uchar *SubtitlingTypes, uint16_t *CompositionPageIds, uint16_t *AncillaryPageIds) { + bool Modified = false; if (SubtitlingTypes) { - for (int i = 0; i < MAXSPIDS; i++) + for (int i = 0; i < MAXSPIDS; i++) { + Modified = subtitlingTypes[i] != SubtitlingTypes[i]; subtitlingTypes[i] = SubtitlingTypes[i]; + } } if (CompositionPageIds) { - for (int i = 0; i < MAXSPIDS; i++) + for (int i = 0; i < MAXSPIDS; i++) { + Modified = compositionPageIds[i] != CompositionPageIds[i]; compositionPageIds[i] = CompositionPageIds[i]; + } } if (AncillaryPageIds) { - for (int i = 0; i < MAXSPIDS; i++) + for (int i = 0; i < MAXSPIDS; i++) { + Modified = ancillaryPageIds[i] != AncillaryPageIds[i]; ancillaryPageIds[i] = AncillaryPageIds[i]; + } } + return Modified; } void cChannel::SetSeen(void) @@ -426,10 +427,26 @@ void cChannel::SetSeen(void) seen = time(NULL); } -void cChannel::SetCaIds(const int *CaIds) +void cChannel::DelLinkChannel(cChannel *LinkChannel) +{ + if (linkChannels) { + for (cLinkChannel *lc = linkChannels->First(); lc; lc = linkChannels->Next(lc)) { + if (lc->Channel() == LinkChannel) { + linkChannels->Del(lc); + break; + } + } + if (linkChannels->Count() == 0) { + delete linkChannels; + linkChannels = NULL; + } + } +} + +bool cChannel::SetCaIds(const int *CaIds) { if (caids[0] && caids[0] <= CA_USER_MAX) - return; // special values will not be overwritten + return false; // special values will not be overwritten if (IntArraysDiffer(caids, CaIds)) { char OldCaIdsBuf[MAXCAIDS * 5 + 10]; // 5: 4 digits plus delimiting ',', 10: paranoia char NewCaIdsBuf[MAXCAIDS * 5 + 10]; @@ -443,24 +460,26 @@ void cChannel::SetCaIds(const int *CaIds) break; } modification |= CHANNELMOD_CA; - Channels.SetModified(); + return true; } + return false; } -void cChannel::SetCaDescriptors(int Level) +bool cChannel::SetCaDescriptors(int Level) { if (Level > 0) { modification |= CHANNELMOD_CA; - Channels.SetModified(); if (Number() && Level > 1) dsyslog("changing ca descriptors of channel %d (%s)", Number(), name); + return true; } + return false; } -void cChannel::SetLinkChannels(cLinkChannels *LinkChannels) +bool cChannel::SetLinkChannels(cLinkChannels *LinkChannels) { if (!linkChannels && !LinkChannels) - return; + return false; if (linkChannels && LinkChannels) { cLinkChannel *lca = linkChannels->First(); cLinkChannel *lcb = LinkChannels->First(); @@ -474,7 +493,7 @@ void cChannel::SetLinkChannels(cLinkChannels *LinkChannels) } if (!lca && !lcb) { delete LinkChannels; - return; // linkage has not changed + return false; // linkage has not changed } } char buffer[((linkChannels ? linkChannels->Count() : 0) + (LinkChannels ? LinkChannels->Count() : 0)) * 6 + 256]; // 6: 5 digit channel number plus blank, 256: other texts (see below) plus reserve @@ -502,6 +521,7 @@ void cChannel::SetLinkChannels(cLinkChannels *LinkChannels) q += sprintf(q, " none"); if (Number()) dsyslog("%s", buffer); + return true; } void cChannel::SetRefChannel(cChannel *RefChannel) @@ -534,9 +554,9 @@ cString cChannel::ToText(const cChannel *Channel) cString buffer; if (Channel->groupSep) { if (Channel->number) - buffer = cString::sprintf(":@%d %s\n", Channel->number, FullName); + buffer = cString::sprintf(":@%d %s", Channel->number, FullName); else - buffer = cString::sprintf(":%s\n", FullName); + buffer = cString::sprintf(":%s", FullName); } else { char vpidbuf[32]; @@ -568,7 +588,7 @@ cString cChannel::ToText(const cChannel *Channel) q = caidbuf; q += IntArrayToString(q, Channel->caids, 16); *q = 0; - buffer = cString::sprintf("%s:%d:%s:%s:%d:%s:%s:%s:%s:%d:%d:%d:%d\n", FullName, Channel->frequency, *Channel->parameters, *cSource::ToString(Channel->source), Channel->srate, vpidbuf, apidbuf, tpidbuf, caidbuf, Channel->sid, Channel->nid, Channel->tid, Channel->rid); + buffer = cString::sprintf("%s:%d:%s:%s:%d:%s:%s:%s:%s:%d:%d:%d:%d", FullName, Channel->frequency, *Channel->parameters, *cSource::ToString(Channel->source), Channel->srate, vpidbuf, apidbuf, tpidbuf, caidbuf, Channel->sid, Channel->nid, Channel->tid, Channel->rid); } return buffer; } @@ -786,7 +806,7 @@ bool cChannel::Parse(const char *s) bool cChannel::Save(FILE *f) { - return fprintf(f, "%s", *ToText()) > 0; + return fprintf(f, "%s\n", *ToText()) > 0; } // --- cChannelSorter -------------------------------------------------------- @@ -807,40 +827,52 @@ public: // --- cChannels ------------------------------------------------------------- -cChannels Channels; +cChannels cChannels::channels; +int cChannels::maxNumber = 0; +int cChannels::maxChannelNameLength = 0; +int cChannels::maxShortChannelNameLength = 0; cChannels::cChannels(void) +:cConfig("Channels") { - maxNumber = 0; - maxChannelNameLength = 0; - maxShortChannelNameLength = 0; - modified = CHANNELSMOD_NONE; + modifiedByUser = 0; +} + +const cChannels *cChannels::GetChannelsRead(cStateKey &StateKey, int TimeoutMs) +{ + return channels.Lock(StateKey, false, TimeoutMs) ? &channels : NULL; +} + +cChannels *cChannels::GetChannelsWrite(cStateKey &StateKey, int TimeoutMs) +{ + return channels.Lock(StateKey, true, TimeoutMs) ? &channels : NULL; } void cChannels::DeleteDuplicateChannels(void) { cList ChannelSorter; - for (cChannel *channel = First(); channel; channel = Next(channel)) { - if (!channel->GroupSep()) - ChannelSorter.Add(new cChannelSorter(channel)); + for (cChannel *Channel = First(); Channel; Channel = Next(Channel)) { + if (!Channel->GroupSep()) + ChannelSorter.Add(new cChannelSorter(Channel)); } ChannelSorter.Sort(); cChannelSorter *cs = ChannelSorter.First(); while (cs) { - cChannelSorter *next = ChannelSorter.Next(cs); - if (next && cs->channelID == next->channelID) { - dsyslog("deleting duplicate channel %s", *next->channel->ToText()); - Del(next->channel); + cChannelSorter *Next = ChannelSorter.Next(cs); + if (Next && cs->channelID == Next->channelID) { + dsyslog("deleting duplicate channel %s", *Next->channel->ToText()); + Del(Next->channel); } - cs = next; + cs = Next; } } bool cChannels::Load(const char *FileName, bool AllowComments, bool MustExist) { - if (cConfig::Load(FileName, AllowComments, MustExist)) { - DeleteDuplicateChannels(); - ReNumber(); + LOCK_CHANNELS_WRITE; + if (channels.cConfig::Load(FileName, AllowComments, MustExist)) { + channels.DeleteDuplicateChannels(); + channels.ReNumber(); return true; } return false; @@ -856,36 +888,36 @@ void cChannels::UnhashChannel(cChannel *Channel) channelsHashSid.Del(Channel, Channel->Sid()); } -int cChannels::GetNextGroup(int Idx) +int cChannels::GetNextGroup(int Idx) const { - cChannel *channel = Get(++Idx); - while (channel && !(channel->GroupSep() && *channel->Name())) - channel = Get(++Idx); - return channel ? Idx : -1; + const cChannel *Channel = Get(++Idx); + while (Channel && !(Channel->GroupSep() && *Channel->Name())) + Channel = Get(++Idx); + return Channel ? Idx : -1; } -int cChannels::GetPrevGroup(int Idx) +int cChannels::GetPrevGroup(int Idx) const { - cChannel *channel = Get(--Idx); - while (channel && !(channel->GroupSep() && *channel->Name())) - channel = Get(--Idx); - return channel ? Idx : -1; + const cChannel *Channel = Get(--Idx); + while (Channel && !(Channel->GroupSep() && *Channel->Name())) + Channel = Get(--Idx); + return Channel ? Idx : -1; } -int cChannels::GetNextNormal(int Idx) +int cChannels::GetNextNormal(int Idx) const { - cChannel *channel = Get(++Idx); - while (channel && channel->GroupSep()) - channel = Get(++Idx); - return channel ? Idx : -1; + const cChannel *Channel = Get(++Idx); + while (Channel && Channel->GroupSep()) + Channel = Get(++Idx); + return Channel ? Idx : -1; } -int cChannels::GetPrevNormal(int Idx) +int cChannels::GetPrevNormal(int Idx) const { - cChannel *channel = Get(--Idx); - while (channel && channel->GroupSep()) - channel = Get(--Idx); - return channel ? Idx : -1; + const cChannel *Channel = Get(--Idx); + while (Channel && Channel->GroupSep()) + Channel = Get(--Idx); + return Channel ? Idx : -1; } void cChannels::ReNumber(void) @@ -893,110 +925,120 @@ void cChannels::ReNumber(void) channelsHashSid.Clear(); maxNumber = 0; int Number = 1; - for (cChannel *channel = First(); channel; channel = Next(channel)) { - if (channel->GroupSep()) { - if (channel->Number() > Number) - Number = channel->Number(); + for (cChannel *Channel = First(); Channel; Channel = Next(Channel)) { + if (Channel->GroupSep()) { + if (Channel->Number() > Number) + Number = Channel->Number(); } else { - HashChannel(channel); + HashChannel(Channel); maxNumber = Number; - channel->SetNumber(Number++); + Channel->SetNumber(Number++); } } } -cChannel *cChannels::GetByNumber(int Number, int SkipGap) +void cChannels::Del(cChannel *Channel) { - cChannel *previous = NULL; - for (cChannel *channel = First(); channel; channel = Next(channel)) { - if (!channel->GroupSep()) { - if (channel->Number() == Number) - return channel; - else if (SkipGap && channel->Number() > Number) - return SkipGap > 0 ? channel : previous; - previous = channel; + UnhashChannel(Channel); + for (cChannel *ch = First(); ch; ch = Next(ch)) + ch->DelLinkChannel(Channel); + cList::Del(Channel); +} + +const cChannel *cChannels::GetByNumber(int Number, int SkipGap) const +{ + const cChannel *Previous = NULL; + for (const cChannel *Channel = First(); Channel; Channel = Next(Channel)) { + if (!Channel->GroupSep()) { + if (Channel->Number() == Number) + return Channel; + else if (SkipGap && Channel->Number() > Number) + return SkipGap > 0 ? Channel : Previous; + Previous = Channel; } } return NULL; } -cChannel *cChannels::GetByServiceID(int Source, int Transponder, unsigned short ServiceID) +const cChannel *cChannels::GetByServiceID(int Source, int Transponder, unsigned short ServiceID) const { cList *list = channelsHashSid.GetList(ServiceID); if (list) { for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) { - cChannel *channel = (cChannel *)hobj->Object(); - if (channel->Sid() == ServiceID && channel->Source() == Source && ISTRANSPONDER(channel->Transponder(), Transponder)) - return channel; + cChannel *Channel = (cChannel *)hobj->Object(); + if (Channel->Sid() == ServiceID && Channel->Source() == Source && ISTRANSPONDER(Channel->Transponder(), Transponder)) + return Channel; } } return NULL; } -cChannel *cChannels::GetByChannelID(tChannelID ChannelID, bool TryWithoutRid, bool TryWithoutPolarization) +const cChannel *cChannels::GetByChannelID(tChannelID ChannelID, bool TryWithoutRid, bool TryWithoutPolarization) const { int sid = ChannelID.Sid(); cList *list = channelsHashSid.GetList(sid); if (list) { for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) { - cChannel *channel = (cChannel *)hobj->Object(); - if (channel->Sid() == sid && channel->GetChannelID() == ChannelID) - return channel; + cChannel *Channel = (cChannel *)hobj->Object(); + if (Channel->Sid() == sid && Channel->GetChannelID() == ChannelID) + return Channel; } if (TryWithoutRid) { ChannelID.ClrRid(); for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) { - cChannel *channel = (cChannel *)hobj->Object(); - if (channel->Sid() == sid && channel->GetChannelID().ClrRid() == ChannelID) - return channel; + cChannel *Channel = (cChannel *)hobj->Object(); + if (Channel->Sid() == sid && Channel->GetChannelID().ClrRid() == ChannelID) + return Channel; } } if (TryWithoutPolarization) { ChannelID.ClrPolarization(); for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) { - cChannel *channel = (cChannel *)hobj->Object(); - if (channel->Sid() == sid && channel->GetChannelID().ClrPolarization() == ChannelID) - return channel; + cChannel *Channel = (cChannel *)hobj->Object(); + if (Channel->Sid() == sid && Channel->GetChannelID().ClrPolarization() == ChannelID) + return Channel; } } } return NULL; } -cChannel *cChannels::GetByTransponderID(tChannelID ChannelID) + +const cChannel *cChannels::GetByTransponderID(tChannelID ChannelID) const { int source = ChannelID.Source(); int nid = ChannelID.Nid(); int tid = ChannelID.Tid(); - for (cChannel *channel = First(); channel; channel = Next(channel)) { - if (channel->Tid() == tid && channel->Nid() == nid && channel->Source() == source) - return channel; + for (const cChannel *Channel = First(); Channel; Channel = Next(Channel)) { + if (Channel->Tid() == tid && Channel->Nid() == nid && Channel->Source() == source) + return Channel; } return NULL; } -bool cChannels::HasUniqueChannelID(cChannel *NewChannel, cChannel *OldChannel) +bool cChannels::HasUniqueChannelID(const cChannel *NewChannel, const cChannel *OldChannel) const { tChannelID NewChannelID = NewChannel->GetChannelID(); - for (cChannel *channel = First(); channel; channel = Next(channel)) { - if (!channel->GroupSep() && channel != OldChannel && channel->GetChannelID() == NewChannelID) + for (const cChannel *Channel = First(); Channel; Channel = Next(Channel)) { + if (!Channel->GroupSep() && Channel != OldChannel && Channel->GetChannelID() == NewChannelID) return false; } return true; } -bool cChannels::SwitchTo(int Number) +bool cChannels::SwitchTo(int Number) const { - cChannel *channel = GetByNumber(Number); - return channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true); + const cChannel *Channel = GetByNumber(Number); + return Channel && cDevice::PrimaryDevice()->SwitchChannel(Channel, true); } int cChannels::MaxChannelNameLength(void) { if (!maxChannelNameLength) { - for (cChannel *channel = First(); channel; channel = Next(channel)) { - if (!channel->GroupSep()) - maxChannelNameLength = max(Utf8StrLen(channel->Name()), maxChannelNameLength); + LOCK_CHANNELS_READ; + for (const cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) { + if (!Channel->GroupSep()) + maxChannelNameLength = max(Utf8StrLen(Channel->Name()), maxChannelNameLength); } } return maxChannelNameLength; @@ -1005,24 +1047,25 @@ int cChannels::MaxChannelNameLength(void) int cChannels::MaxShortChannelNameLength(void) { if (!maxShortChannelNameLength) { - for (cChannel *channel = First(); channel; channel = Next(channel)) { - if (!channel->GroupSep()) - maxShortChannelNameLength = max(Utf8StrLen(channel->ShortName(true)), maxShortChannelNameLength); + LOCK_CHANNELS_READ; + for (const cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) { + if (!Channel->GroupSep()) + maxShortChannelNameLength = max(Utf8StrLen(Channel->ShortName(true)), maxShortChannelNameLength); } } return maxShortChannelNameLength; } -void cChannels::SetModified(bool ByUser) +void cChannels::SetModifiedByUser(void) { - modified = ByUser ? CHANNELSMOD_USER : !modified ? CHANNELSMOD_AUTO : modified; + modifiedByUser++; maxChannelNameLength = maxShortChannelNameLength = 0; } -int cChannels::Modified(void) +bool cChannels::ModifiedByUser(int &State) const { - int Result = modified; - modified = CHANNELSMOD_NONE; + int Result = State != modifiedByUser; + State = modifiedByUser; return Result; } @@ -1032,7 +1075,7 @@ cChannel *cChannels::NewChannel(const cChannel *Transponder, const char *Name, c dsyslog("creating new channel '%s,%s;%s' on %s transponder %d with id %d-%d-%d-%d", Name, ShortName, Provider, *cSource::ToString(Transponder->Source()), Transponder->Transponder(), Nid, Tid, Sid, Rid); cChannel *NewChannel = new cChannel; NewChannel->CopyTransponderData(Transponder); - NewChannel->SetId(Nid, Tid, Sid, Rid); + NewChannel->SetId(this, Nid, Tid, Sid, Rid); NewChannel->SetName(Name, ShortName, Provider); NewChannel->SetSeen(); Add(NewChannel); @@ -1045,17 +1088,19 @@ cChannel *cChannels::NewChannel(const cChannel *Transponder, const char *Name, c #define CHANNELMARKOBSOLETE "OBSOLETE" #define CHANNELTIMEOBSOLETE 3600 // seconds to wait before declaring a channel obsolete (in case it has actually been seen before) -void cChannels::MarkObsoleteChannels(int Source, int Nid, int Tid) +bool cChannels::MarkObsoleteChannels(int Source, int Nid, int Tid) { - for (cChannel *channel = First(); channel; channel = Next(channel)) { - if (time(NULL) - channel->Seen() > CHANNELTIMEOBSOLETE && channel->Source() == Source && channel->Nid() == Nid && channel->Tid() == Tid && channel->Rid() == 0) { + bool ChannelsModified = false; + for (cChannel *Channel = First(); Channel; Channel = Next(Channel)) { + if (time(NULL) - Channel->Seen() > CHANNELTIMEOBSOLETE && Channel->Source() == Source && Channel->Nid() == Nid && Channel->Tid() == Tid && Channel->Rid() == 0) { bool OldShowChannelNamesWithSource = Setup.ShowChannelNamesWithSource; Setup.ShowChannelNamesWithSource = false; - if (!endswith(channel->Name(), CHANNELMARKOBSOLETE)) - channel->SetName(cString::sprintf("%s %s", channel->Name(), CHANNELMARKOBSOLETE), channel->ShortName(), cString::sprintf("%s %s", CHANNELMARKOBSOLETE, channel->Provider())); + if (!endswith(Channel->Name(), CHANNELMARKOBSOLETE)) + ChannelsModified |= Channel->SetName(cString::sprintf("%s %s", Channel->Name(), CHANNELMARKOBSOLETE), Channel->ShortName(), cString::sprintf("%s %s", CHANNELMARKOBSOLETE, Channel->Provider())); Setup.ShowChannelNamesWithSource = OldShowChannelNamesWithSource; } } + return ChannelsModified; } cString ChannelString(const cChannel *Channel, int Number) diff --git a/channels.h b/channels.h index 33238821..478b3d64 100644 --- a/channels.h +++ b/channels.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: channels.h 3.3 2015/02/01 13:30:26 kls Exp $ + * $Id: channels.h 4.2 2015/08/17 09:39:48 kls Exp $ */ #ifndef __CHANNELS_H @@ -28,10 +28,6 @@ #define CHANNELMOD_LANGS 0x40 #define CHANNELMOD_RETUNE (CHANNELMOD_PIDS | CHANNELMOD_CA | CHANNELMOD_TRANSP) -#define CHANNELSMOD_NONE 0 -#define CHANNELSMOD_AUTO 1 -#define CHANNELSMOD_USER 2 - #define MAXAPIDS 32 // audio #define MAXDPIDS 16 // dolby (AC3 + DTS) #define MAXSPIDS 32 // subtitles @@ -86,6 +82,7 @@ class cLinkChannels : public cList { }; class cSchedule; +class cChannels; class cChannel : public cListObject { friend class cSchedules; @@ -128,7 +125,7 @@ private: mutable cString nameSource; mutable cString shortNameSource; cString parameters; - int modification; + mutable int modification; time_t seen; // When this channel was last seen in the SDT of its transponder mutable const cSchedule *schedule; cLinkChannels *linkChannels; @@ -188,65 +185,84 @@ public: bool IsTerr(void) const { return cSource::IsTerr(source); } bool IsSourceType(char Source) const { return cSource::IsType(source, Source); } tChannelID GetChannelID(void) const { return tChannelID(source, nid, (nid || tid) ? tid : Transponder(), sid, rid); } - bool HasTimer(void) const; - int Modification(int Mask = CHANNELMOD_ALL); - time_t Seen(void) { return seen; } + int Modification(int Mask = CHANNELMOD_ALL) const; + time_t Seen(void) const { return seen; } void CopyTransponderData(const cChannel *Channel); bool SetTransponderData(int Source, int Frequency, int Srate, const char *Parameters, bool Quiet = false); - void SetId(int Nid, int Tid, int Sid, int Rid = 0); - void SetLcn(int Lcn); - void SetName(const char *Name, const char *ShortName, const char *Provider); - void SetPortalName(const char *PortalName); - void SetPids(int Vpid, int Ppid, int Vtype, int *Apids, int *Atypes, char ALangs[][MAXLANGCODE2], int *Dpids, int *Dtypes, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid); - void SetCaIds(const int *CaIds); // list must be zero-terminated - void SetCaDescriptors(int Level); - void SetLinkChannels(cLinkChannels *LinkChannels); + bool SetSource(int Source); + bool SetId(cChannels *Channels, int Nid, int Tid, int Sid, int Rid = 0); + bool SetLcn(int Lcn); + bool SetName(const char *Name, const char *ShortName, const char *Provider); + bool SetPortalName(const char *PortalName); + bool SetPids(int Vpid, int Ppid, int Vtype, int *Apids, int *Atypes, char ALangs[][MAXLANGCODE2], int *Dpids, int *Dtypes, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid); + bool SetCaIds(const int *CaIds); // list must be zero-terminated + bool SetCaDescriptors(int Level); + bool SetLinkChannels(cLinkChannels *LinkChannels); void SetRefChannel(cChannel *RefChannel); - void SetSubtitlingDescriptors(uchar *SubtitlingTypes, uint16_t *CompositionPageIds, uint16_t *AncillaryPageIds); + bool SetSubtitlingDescriptors(uchar *SubtitlingTypes, uint16_t *CompositionPageIds, uint16_t *AncillaryPageIds); void SetSeen(void); + void DelLinkChannel(cChannel *LinkChannel); }; -class cChannels : public cRwLock, public cConfig { +class cChannels : public cConfig { private: - int maxNumber; - int maxChannelNameLength; - int maxShortChannelNameLength; - int modified; - int beingEdited; + static cChannels channels; + static int maxNumber; + static int maxChannelNameLength; + static int maxShortChannelNameLength; + int modifiedByUser; cHash channelsHashSid; void DeleteDuplicateChannels(void); public: cChannels(void); - bool Load(const char *FileName, bool AllowComments = false, bool MustExist = false); + static const cChannels *GetChannelsRead(cStateKey &StateKey, int TimeoutMs = 0); + ///< Gets the list of channels for read access. + ///< See cTimers::GetTimersRead() for details. + static cChannels *GetChannelsWrite(cStateKey &StateKey, int TimeoutMs = 0); + ///< Gets the list of channels for write access. + ///< See cTimers::GetTimersWrite() for details. + static bool Load(const char *FileName, bool AllowComments = false, bool MustExist = false); void HashChannel(cChannel *Channel); void UnhashChannel(cChannel *Channel); - int GetNextGroup(int Idx); // Get next channel group - int GetPrevGroup(int Idx); // Get previous channel group - int GetNextNormal(int Idx); // Get next normal channel (not group) - int GetPrevNormal(int Idx); // Get previous normal channel (not group) - void ReNumber(void); // Recalculate 'number' based on channel type - cChannel *GetByNumber(int Number, int SkipGap = 0); - cChannel *GetByServiceID(int Source, int Transponder, unsigned short ServiceID); - cChannel *GetByChannelID(tChannelID ChannelID, bool TryWithoutRid = false, bool TryWithoutPolarization = false); - cChannel *GetByTransponderID(tChannelID ChannelID); - int BeingEdited(void) { return beingEdited; } - void IncBeingEdited(void) { beingEdited++; } - void DecBeingEdited(void) { beingEdited--; } - bool HasUniqueChannelID(cChannel *NewChannel, cChannel *OldChannel = NULL); - bool SwitchTo(int Number); - int MaxNumber(void) { return maxNumber; } - int MaxChannelNameLength(void); - int MaxShortChannelNameLength(void); - void SetModified(bool ByUser = false); - int Modified(void); - ///< Returns 0 if no channels have been modified, 1 if an automatic - ///< modification has been made, and 2 if the user has made a modification. - ///< Calling this function resets the 'modified' flag to 0. + int GetNextGroup(int Idx) const; ///< Get next channel group + int GetPrevGroup(int Idx) const; ///< Get previous channel group + int GetNextNormal(int Idx) const; ///< Get next normal channel (not group) + int GetPrevNormal(int Idx) const; ///< Get previous normal channel (not group) + void ReNumber(void); ///< Recalculate 'number' based on channel type + void Del(cChannel *Channel); ///< Delete the given Channel from the list + const cChannel *GetByNumber(int Number, int SkipGap = 0) const; + cChannel *GetByNumber(int Number, int SkipGap = 0) { return const_cast(static_cast(this)->GetByNumber(Number, SkipGap)); } + const cChannel *GetByServiceID(int Source, int Transponder, unsigned short ServiceID) const; + cChannel *GetByServiceID(int Source, int Transponder, unsigned short ServiceID) { return const_cast(static_cast(this)->GetByServiceID(Source, Transponder, ServiceID)); } + const cChannel *GetByChannelID(tChannelID ChannelID, bool TryWithoutRid = false, bool TryWithoutPolarization = false) const; + cChannel *GetByChannelID(tChannelID ChannelID, bool TryWithoutRid = false, bool TryWithoutPolarization = false) { return const_cast(static_cast(this)->GetByChannelID(ChannelID, TryWithoutRid, TryWithoutPolarization)); } + const cChannel *GetByTransponderID(tChannelID ChannelID) const; + cChannel *GetByTransponderID(tChannelID ChannelID) { return const_cast(static_cast(this)->GetByTransponderID(ChannelID)); } + bool HasUniqueChannelID(const cChannel *NewChannel, const cChannel *OldChannel = NULL) const; + bool SwitchTo(int Number) const; + static int MaxNumber(void) { return maxNumber; } + static int MaxChannelNameLength(void); + static int MaxShortChannelNameLength(void); + void SetModifiedByUser(void); + bool ModifiedByUser(int &State) const; + ///< Returns true if the channels have been modified by the user since the last call + ///< to this function with the same State variable. State must be initialized with 0 + ///< and will be set to the current value of the list's internal state variable upon + ///< return from this function. cChannel *NewChannel(const cChannel *Transponder, const char *Name, const char *ShortName, const char *Provider, int Nid, int Tid, int Sid, int Rid = 0); - void MarkObsoleteChannels(int Source, int Nid, int Tid); + bool MarkObsoleteChannels(int Source, int Nid, int Tid); }; -extern cChannels Channels; +// Provide lock controlled access to the list: + +DEF_LIST_LOCK(Channels); + +// These macros provide a convenient way of locking the global channels list +// and making sure the lock is released as soon as the current scope is left +// (note that these macros wait forever to obtain the lock!): + +#define LOCK_CHANNELS_READ USE_LIST_LOCK_READ(Channels) +#define LOCK_CHANNELS_WRITE USE_LIST_LOCK_WRITE(Channels) cString ChannelString(const cChannel *Channel, int Number); diff --git a/ci.c b/ci.c index ffc7ff71..f86f668e 100644 --- a/ci.c +++ b/ci.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: ci.c 3.19 2015/02/02 13:57:39 kls Exp $ + * $Id: ci.c 4.2 2015/09/05 11:45:19 kls Exp $ */ #include "ci.h" @@ -125,8 +125,8 @@ protected: public: cCaPidReceiver(void); virtual ~cCaPidReceiver() { Detach(); } - virtual void Receive(uchar *Data, int Length); - bool HasCaPids(void) { return NumPids() - emmPids.Size() - 1 > 0; } + virtual void Receive(const uchar *Data, int Length); + bool HasCaPids(void) const { return NumPids() - emmPids.Size() - 1 > 0; } void Reset(void) { DelEmmPids(); catVersion = -1; } }; @@ -160,10 +160,10 @@ void cCaPidReceiver::Activate(bool On) catVersion = -1; // can be done independent of 'On' } -void cCaPidReceiver::Receive(uchar *Data, int Length) +void cCaPidReceiver::Receive(const uchar *Data, int Length) { if (TsPid(Data) == CATPID) { - uchar *p = NULL; + const uchar *p = NULL; if (TsPayloadStart(Data)) { if (Data[5] == SI::TableIdCAT) { length = (int(Data[6] & 0x03) << 8) | Data[7]; // section length @@ -251,7 +251,7 @@ private: time_t lastScrambledTime; int numTsPackets; protected: - virtual void Receive(uchar *Data, int Length); + virtual void Receive(const uchar *Data, int Length); public: cCaActivationReceiver(const cChannel *Channel, cCamSlot *CamSlot); virtual ~cCaActivationReceiver(); @@ -270,7 +270,7 @@ cCaActivationReceiver::~cCaActivationReceiver() Detach(); } -void cCaActivationReceiver::Receive(uchar *Data, int Length) +void cCaActivationReceiver::Receive(const uchar *Data, int Length) { if (numTsPackets++ % TS_PACKET_FACTOR == 0) { time_t Now = time(NULL); @@ -1921,7 +1921,8 @@ void cCamSlot::StartActivation(void) cMutexLock MutexLock(&mutex); if (!caActivationReceiver) { if (cDevice *d = Device()) { - if (cChannel *Channel = Channels.GetByNumber(cDevice::CurrentChannel())) { + LOCK_CHANNELS_READ; + if (const cChannel *Channel = Channels->GetByNumber(cDevice::CurrentChannel())) { caActivationReceiver = new cCaActivationReceiver(Channel, this); d->AttachReceiver(caActivationReceiver); dsyslog("CAM %d: activating on device %d with channel %d (%s)", SlotNumber(), d->DeviceNumber() + 1, Channel->Number(), Channel->Name()); diff --git a/ci.h b/ci.h index 5a4dc98d..a4200152 100644 --- a/ci.h +++ b/ci.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: ci.h 3.11 2015/01/31 14:36:41 kls Exp $ + * $Id: ci.h 4.0 2015/01/31 14:36:41 kls Exp $ */ #ifndef __CI_H diff --git a/config.c b/config.c index 9c6b71e5..e5f5463c 100644 --- a/config.c +++ b/config.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.c 3.10 2015/02/10 12:24:13 kls Exp $ + * $Id: config.c 4.4 2015/09/13 11:09:44 kls Exp $ */ #include "config.h" @@ -412,12 +412,16 @@ cSetup::cSetup(void) EPGBugfixLevel = 3; EPGLinger = 0; SVDRPTimeout = 300; + SVDRPPeering = 0; + strn0cpy(SVDRPHostName, GetHostName(), sizeof(SVDRPHostName)); + strcpy(SVDRPDefaultHost, ""); ZapTimeout = 3; ChannelEntryTimeout = 1000; RcRepeatDelay = 300; RcRepeatDelta = 100; DefaultPriority = 50; DefaultLifetime = MAXLIFETIME; + RecordKeyHandling = 2; PauseKeyHandling = 2; PausePriority = 10; PauseLifetime = 1; @@ -427,6 +431,7 @@ cSetup::cSetup(void) RecordingDirs = 1; FoldersInTimerMenu = 1; AlwaysSortFoldersFirst = 1; + DefaultSortModeRec = rsmTime; NumberKeysForChars = 1; ColorKey0 = 0; ColorKey1 = 1; @@ -634,12 +639,16 @@ bool cSetup::Parse(const char *Name, const char *Value) else if (!strcasecmp(Name, "EPGBugfixLevel")) EPGBugfixLevel = atoi(Value); else if (!strcasecmp(Name, "EPGLinger")) EPGLinger = atoi(Value); else if (!strcasecmp(Name, "SVDRPTimeout")) SVDRPTimeout = atoi(Value); + else if (!strcasecmp(Name, "SVDRPPeering")) SVDRPPeering = atoi(Value); + else if (!strcasecmp(Name, "SVDRPHostName")) strn0cpy(SVDRPHostName, Value, sizeof(SVDRPHostName)); + else if (!strcasecmp(Name, "SVDRPdefaultHost")) strn0cpy(SVDRPDefaultHost, Value, sizeof(SVDRPDefaultHost)); else if (!strcasecmp(Name, "ZapTimeout")) ZapTimeout = atoi(Value); else if (!strcasecmp(Name, "ChannelEntryTimeout")) ChannelEntryTimeout= atoi(Value); else if (!strcasecmp(Name, "RcRepeatDelay")) RcRepeatDelay = atoi(Value); else if (!strcasecmp(Name, "RcRepeatDelta")) RcRepeatDelta = atoi(Value); else if (!strcasecmp(Name, "DefaultPriority")) DefaultPriority = atoi(Value); else if (!strcasecmp(Name, "DefaultLifetime")) DefaultLifetime = atoi(Value); + else if (!strcasecmp(Name, "RecordKeyHandling")) RecordKeyHandling = atoi(Value); else if (!strcasecmp(Name, "PauseKeyHandling")) PauseKeyHandling = atoi(Value); else if (!strcasecmp(Name, "PausePriority")) PausePriority = atoi(Value); else if (!strcasecmp(Name, "PauseLifetime")) PauseLifetime = atoi(Value); @@ -649,6 +658,7 @@ bool cSetup::Parse(const char *Name, const char *Value) else if (!strcasecmp(Name, "RecordingDirs")) RecordingDirs = atoi(Value); else if (!strcasecmp(Name, "FoldersInTimerMenu")) FoldersInTimerMenu = atoi(Value); else if (!strcasecmp(Name, "AlwaysSortFoldersFirst")) AlwaysSortFoldersFirst = atoi(Value); + else if (!strcasecmp(Name, "DefaultSortModeRec")) DefaultSortModeRec = atoi(Value); else if (!strcasecmp(Name, "NumberKeysForChars")) NumberKeysForChars = atoi(Value); else if (!strcasecmp(Name, "ColorKey0")) ColorKey0 = atoi(Value); else if (!strcasecmp(Name, "ColorKey1")) ColorKey1 = atoi(Value); @@ -760,12 +770,16 @@ bool cSetup::Save(void) Store("EPGBugfixLevel", EPGBugfixLevel); Store("EPGLinger", EPGLinger); Store("SVDRPTimeout", SVDRPTimeout); + Store("SVDRPPeering", SVDRPPeering); + Store("SVDRPHostName", SVDRPHostName); + Store("SVDRPDefaultHost", SVDRPDefaultHost); Store("ZapTimeout", ZapTimeout); Store("ChannelEntryTimeout",ChannelEntryTimeout); Store("RcRepeatDelay", RcRepeatDelay); Store("RcRepeatDelta", RcRepeatDelta); Store("DefaultPriority", DefaultPriority); Store("DefaultLifetime", DefaultLifetime); + Store("RecordKeyHandling", RecordKeyHandling); Store("PauseKeyHandling", PauseKeyHandling); Store("PausePriority", PausePriority); Store("PauseLifetime", PauseLifetime); @@ -775,6 +789,7 @@ bool cSetup::Save(void) Store("RecordingDirs", RecordingDirs); Store("FoldersInTimerMenu", FoldersInTimerMenu); Store("AlwaysSortFoldersFirst", AlwaysSortFoldersFirst); + Store("DefaultSortModeRec", DefaultSortModeRec); Store("NumberKeysForChars", NumberKeysForChars); Store("ColorKey0", ColorKey0); Store("ColorKey1", ColorKey1); diff --git a/config.h b/config.h index d1bae044..e5565da3 100644 --- a/config.h +++ b/config.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.h 3.21 2015/02/13 15:39:08 kls Exp $ + * $Id: config.h 4.5 2015/09/11 08:07:34 kls Exp $ */ #ifndef __CONFIG_H @@ -22,13 +22,13 @@ // VDR's own version number: -#define VDRVERSION "2.2.0" -#define VDRVERSNUM 20200 // Version * 10000 + Major * 100 + Minor +#define VDRVERSION "2.3.1" +#define VDRVERSNUM 20301 // Version * 10000 + Major * 100 + Minor // The plugin API's version number: -#define APIVERSION "2.2.0" -#define APIVERSNUM 20200 // Version * 10000 + Major * 100 + Minor +#define APIVERSION "2.3.1" +#define APIVERSNUM 20301 // Version * 10000 + Major * 100 + Minor // When loading plugins, VDR searches them by their APIVERSION, which // may be smaller than VDRVERSION in case there have been no changes to @@ -110,7 +110,7 @@ private: cList::Clear(); } public: - cConfig(void) { fileName = NULL; } + cConfig(const char *NeedsLocking = NULL): cList(NeedsLocking) { fileName = NULL; } virtual ~cConfig() { free(fileName); } const char *FileName(void) { return fileName; } bool Load(const char *FileName = NULL, bool AllowComments = false, bool MustExist = false) @@ -160,7 +160,7 @@ public: fprintf(stderr, "vdr: error while reading '%s'\n", fileName); return result; } - bool Save(void) + bool Save(void) const { bool result = true; T *l = (T *)this->First(); @@ -288,19 +288,24 @@ public: int EPGBugfixLevel; int EPGLinger; int SVDRPTimeout; + int SVDRPPeering; + char SVDRPHostName[HOST_NAME_MAX]; + char SVDRPDefaultHost[HOST_NAME_MAX]; int ZapTimeout; int ChannelEntryTimeout; int RcRepeatDelay; int RcRepeatDelta; int DefaultPriority, DefaultLifetime; - int PausePriority, PauseLifetime; + int RecordKeyHandling; int PauseKeyHandling; + int PausePriority, PauseLifetime; int UseSubtitle; int UseVps; int VpsMargin; int RecordingDirs; int FoldersInTimerMenu; int AlwaysSortFoldersFirst; + int DefaultSortModeRec; int NumberKeysForChars; int ColorKey0, ColorKey1, ColorKey2, ColorKey3; int VideoDisplayFormat; diff --git a/cutter.c b/cutter.c index 9aee66b9..587fddf0 100644 --- a/cutter.c +++ b/cutter.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: cutter.c 3.4 2013/10/02 13:18:02 kls Exp $ + * $Id: cutter.c 4.2 2015/08/09 12:24:28 kls Exp $ */ #include "cutter.h" @@ -634,7 +634,6 @@ void cCuttingThread::Action(void) } } } - Recordings.TouchUpdate(); } else esyslog("no editing marks found!"); @@ -675,9 +674,11 @@ bool cCutter::Start(void) editedVersionName = EditedFileName(originalVersionName); if (*editedVersionName) { if (strcmp(originalVersionName, editedVersionName) != 0) { // names must be different! + cRecordingUserCommand::InvokeCommand(RUC_EDITINGRECORDING, editedVersionName, originalVersionName); if (cVideoDirectory::RemoveVideoFile(editedVersionName) && MakeDirs(editedVersionName, true)) { Recording.WriteInfo(editedVersionName); - Recordings.AddByName(editedVersionName, false); + LOCK_RECORDINGS_WRITE; + Recordings->AddByName(editedVersionName, false); cuttingThread = new cCuttingThread(originalVersionName, editedVersionName); return true; } @@ -702,7 +703,8 @@ void cCutter::Stop(void) if (cReplayControl::NowReplaying() && strcmp(cReplayControl::NowReplaying(), editedVersionName) == 0) cControl::Shutdown(); cVideoDirectory::RemoveVideoFile(editedVersionName); - Recordings.DelByName(editedVersionName); + LOCK_RECORDINGS_WRITE; + Recordings->DelByName(editedVersionName); } } diff --git a/cutter.h b/cutter.h index c67429a1..fdc94fca 100644 --- a/cutter.h +++ b/cutter.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: cutter.h 3.1 2013/10/05 11:34:55 kls Exp $ + * $Id: cutter.h 4.0 2013/10/05 11:34:55 kls Exp $ */ #ifndef __CUTTER_H diff --git a/device.c b/device.c index 14ab07d3..174281f4 100644 --- a/device.c +++ b/device.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: device.c 3.20 2015/01/30 12:11:30 kls Exp $ + * $Id: device.c 4.2 2015/09/05 11:42:17 kls Exp $ */ #include "device.h" @@ -24,7 +24,7 @@ class cLiveSubtitle : public cReceiver { protected: - virtual void Receive(uchar *Data, int Length); + virtual void Receive(const uchar *Data, int Length); public: cLiveSubtitle(int SPid); virtual ~cLiveSubtitle(); @@ -40,7 +40,7 @@ cLiveSubtitle::~cLiveSubtitle() cReceiver::Detach(); } -void cLiveSubtitle::Receive(uchar *Data, int Length) +void cLiveSubtitle::Receive(const uchar *Data, int Length) { if (cDevice::PrimaryDevice()) cDevice::PrimaryDevice()->PlayTs(Data, Length); @@ -722,20 +722,21 @@ bool cDevice::SwitchChannel(int Direction) cControl::Shutdown(); // prevents old channel from being shown too long if GetDevice() takes longer int n = CurrentChannel() + Direction; int first = n; - cChannel *channel; - while ((channel = Channels.GetByNumber(n, Direction)) != NULL) { + LOCK_CHANNELS_READ; + const cChannel *Channel; + while ((Channel = Channels->GetByNumber(n, Direction)) != NULL) { // try only channels which are currently available - if (GetDevice(channel, LIVEPRIORITY, true, true)) + if (GetDevice(Channel, LIVEPRIORITY, true, true)) break; - n = channel->Number() + Direction; + n = Channel->Number() + Direction; } - if (channel) { + if (Channel) { int d = n - first; if (abs(d) == 1) dsyslog("skipped channel %d", first); else if (d) dsyslog("skipped channels %d..%d", first, n - sgn(d)); - if (PrimaryDevice()->SwitchChannel(channel, true)) + if (PrimaryDevice()->SwitchChannel(Channel, true)) result = true; } else if (n != first) @@ -777,7 +778,6 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView) Result = scrNotAvailable; } else { - Channels.Lock(false); // Stop section handling: if (sectionHandler) { sectionHandler->SetStatus(false); @@ -790,7 +790,8 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView) if (SetChannelDevice(Channel, LiveView)) { // Start section handling: if (sectionHandler) { - patFilter->Trigger(Channel->Sid()); + if (patFilter) + patFilter->Trigger(Channel->Sid()); sectionHandler->SetChannel(Channel); sectionHandler->SetStatus(true); } @@ -800,7 +801,6 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView) } else Result = scrFailed; - Channels.Unlock(); } if (Result == scrOk) { @@ -829,8 +829,8 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView) void cDevice::ForceTransferMode(void) { if (!cTransferControl::ReceiverDevice()) { - cChannel *Channel = Channels.GetByNumber(CurrentChannel()); - if (Channel) + LOCK_CHANNELS_READ; + if (const cChannel *Channel = Channels->GetByNumber(CurrentChannel())) SetChannelDevice(Channel, false); // this implicitly starts Transfer Mode } } diff --git a/device.h b/device.h index b06d9770..31ee3038 100644 --- a/device.h +++ b/device.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: device.h 3.10 2015/01/12 14:39:09 kls Exp $ + * $Id: device.h 4.1 2015/04/19 12:12:43 kls Exp $ */ #ifndef __DEVICE_H @@ -55,7 +55,7 @@ enum ePlayMode { pmNone, // audio/video from decoder // KNOWN TO YOUR PLAYER. }; -#define DEPRECATED_VIDEOSYSTEM +//#define DEPRECATED_VIDEOSYSTEM #ifdef DEPRECATED_VIDEOSYSTEM enum eVideoSystem { vsPAL, vsNTSC diff --git a/diseqc.c b/diseqc.c index de64257f..b2f8e35c 100644 --- a/diseqc.c +++ b/diseqc.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: diseqc.c 3.4 2015/01/26 12:02:14 kls Exp $ + * $Id: diseqc.c 4.0 2015/01/26 12:02:14 kls Exp $ */ #include "diseqc.h" diff --git a/diseqc.h b/diseqc.h index eef36a6a..2c349d14 100644 --- a/diseqc.h +++ b/diseqc.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: diseqc.h 3.1 2013/06/12 11:52:17 kls Exp $ + * $Id: diseqc.h 4.0 2013/06/12 11:52:17 kls Exp $ */ #ifndef __DISEQC_H diff --git a/dvbci.c b/dvbci.c index fa65f0f6..afe3b312 100644 --- a/dvbci.c +++ b/dvbci.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbci.c 3.1 2015/01/14 11:13:49 kls Exp $ + * $Id: dvbci.c 4.0 2015/01/14 11:13:49 kls Exp $ */ #include "dvbci.h" diff --git a/dvbci.h b/dvbci.h index f74703c3..4e6d24be 100644 --- a/dvbci.h +++ b/dvbci.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbci.h 3.0 2006/11/26 11:19:42 kls Exp $ + * $Id: dvbci.h 4.0 2006/11/26 11:19:42 kls Exp $ */ #ifndef __DVBCI_H diff --git a/dvbdevice.c b/dvbdevice.c index 9321f16d..63af52e7 100644 --- a/dvbdevice.c +++ b/dvbdevice.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbdevice.c 3.14 2015/01/14 12:09:19 kls Exp $ + * $Id: dvbdevice.c 4.2 2015/04/18 16:19:28 kls Exp $ */ #include "dvbdevice.h" @@ -1064,6 +1064,7 @@ cOsdItem *cDvbSourceParam::GetOsdItem(void) // --- cDvbDevice ------------------------------------------------------------ +bool cDvbDevice::useDvbDevices = true; int cDvbDevice::setTransferModeForDolbyDigital = 1; cMutex cDvbDevice::bondMutex; @@ -1234,7 +1235,7 @@ bool cDvbDevice::Initialize(void) if (Exists(Adapter, Frontend)) { if (Found < MAXDEVICES) { Found++; - if (UseDevice(NextCardIndex())) { + if (useDvbDevices && UseDevice(NextCardIndex())) { if (Probe(Adapter, Frontend)) Used++; } @@ -1248,7 +1249,7 @@ bool cDvbDevice::Initialize(void) if (Found > 0) { isyslog("found %d DVB device%s", Found, Found > 1 ? "s" : ""); if (Used != Found) - isyslog("using only %d DVB device%s", Used, Used > 1 ? "s" : ""); + isyslog("using only %d DVB device%s", Used, Used != 1 ? "s" : ""); } else isyslog("no DVB device found"); diff --git a/dvbdevice.h b/dvbdevice.h index 0a148ce3..5ae4952f 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 3.5 2014/03/16 10:38:31 kls Exp $ + * $Id: dvbdevice.h 4.1 2015/04/18 13:57:27 kls Exp $ */ #ifndef __DVBDEVICE_H @@ -174,6 +174,7 @@ private: static bool Probe(int Adapter, int Frontend); ///< Probes for existing DVB devices. public: + static bool useDvbDevices; static bool Initialize(void); ///< Initializes the DVB devices. ///< Must be called before accessing any DVB functions. diff --git a/dvbplayer.c b/dvbplayer.c index aae0507c..ca4007e3 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 3.6 2015/02/13 15:12:57 kls Exp $ + * $Id: dvbplayer.c 4.1 2015/08/06 13:09:19 kls Exp $ */ #include "dvbplayer.h" @@ -210,7 +210,7 @@ private: cNonBlockingFileReader *nonBlockingFileReader; cRingBufferFrame *ringBuffer; cPtsIndex ptsIndex; - cMarks *marks; + const cMarks *marks; cFileName *fileName; cIndexFile *index; cUnbufferedFile *replayFile; @@ -239,7 +239,7 @@ protected: public: cDvbPlayer(const char *FileName, bool PauseLive); virtual ~cDvbPlayer(); - void SetMarks(cMarks *Marks); + void SetMarks(const cMarks *Marks); bool Active(void) { return cThread::Running(); } void Pause(void); void Play(void); @@ -311,7 +311,7 @@ cDvbPlayer::~cDvbPlayer() // don't delete marks here, we don't own them! } -void cDvbPlayer::SetMarks(cMarks *Marks) +void cDvbPlayer::SetMarks(const cMarks *Marks) { marks = Marks; } @@ -383,10 +383,11 @@ bool cDvbPlayer::Save(void) int Index = ptsIndex.FindIndex(DeviceGetSTC()); if (Index >= 0) { if (Setup.SkipEdited && marks) { - marks->Lock(); + cStateKey StateKey; + marks->Lock(StateKey); if (marks->First() && abs(Index - marks->First()->Position()) <= int(round(RESUMEBACKUP * framesPerSecond))) Index = 0; // when stopping within RESUMEBACKUP seconds of the first mark the recording shall still be considered unviewed - marks->Unlock(); + StateKey.Remove(); } Index -= int(round(RESUMEBACKUP * framesPerSecond)); if (Index > 0) @@ -419,7 +420,8 @@ void cDvbPlayer::Action(void) if (readIndex > 0) isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true, framesPerSecond)); else if (Setup.SkipEdited && marks) { - marks->Lock(); + cStateKey StateKey; + marks->Lock(StateKey); if (marks->First() && index) { int Index = marks->First()->Position(); uint16_t FileNumber; @@ -429,7 +431,7 @@ void cDvbPlayer::Action(void) readIndex = Index; } } - marks->Unlock(); + StateKey.Remove(); } nonBlockingFileReader = new cNonBlockingFileReader; @@ -500,8 +502,9 @@ void cDvbPlayer::Action(void) if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent, &Length) && NextFile(FileNumber, FileOffset)) { readIndex++; if ((Setup.SkipEdited || Setup.PauseAtLastMark) && marks) { - marks->Lock(); - cMark *m = marks->Get(readIndex); + cStateKey StateKey; + marks->Lock(StateKey); + const cMark *m = marks->Get(readIndex); if (m && (m->Index() & 0x01) != 0) { // we're at an end mark m = marks->GetNextBegin(m); int Index = -1; @@ -519,7 +522,7 @@ void cDvbPlayer::Action(void) CutIn = true; } } - marks->Unlock(); + StateKey.Remove(); } } else @@ -943,7 +946,7 @@ cDvbPlayerControl::~cDvbPlayerControl() Stop(); } -void cDvbPlayerControl::SetMarks(cMarks *Marks) +void cDvbPlayerControl::SetMarks(const cMarks *Marks) { if (player) player->SetMarks(Marks); diff --git a/dvbplayer.h b/dvbplayer.h index 7094454d..ef6f1fce 100644 --- a/dvbplayer.h +++ b/dvbplayer.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbplayer.h 3.2 2015/02/06 12:27:39 kls Exp $ + * $Id: dvbplayer.h 4.1 2015/08/02 13:01:44 kls Exp $ */ #ifndef __DVBPLAYER_H @@ -26,7 +26,7 @@ public: // file of the recording is long enough to allow the player to display // the first frame in still picture mode. virtual ~cDvbPlayerControl(); - void SetMarks(cMarks *Marks); + void SetMarks(const cMarks *Marks); bool Active(void); void Stop(void); // Stops the current replay session (if any). diff --git a/dvbspu.c b/dvbspu.c index da18d0a1..90454a53 100644 --- a/dvbspu.c +++ b/dvbspu.c @@ -8,7 +8,7 @@ * * parts of this file are derived from the OMS program. * - * $Id: dvbspu.c 3.0 2013/02/22 15:25:16 kls Exp $ + * $Id: dvbspu.c 4.0 2013/02/22 15:25:16 kls Exp $ */ #include "dvbspu.h" diff --git a/dvbspu.h b/dvbspu.h index dadf7345..69b08583 100644 --- a/dvbspu.h +++ b/dvbspu.h @@ -8,7 +8,7 @@ * * parts of this file are derived from the OMS program. * - * $Id: dvbspu.h 3.1 2014/02/08 12:27:34 kls Exp $ + * $Id: dvbspu.h 4.0 2014/02/08 12:27:34 kls Exp $ */ #ifndef __DVBSPU_H diff --git a/dvbsubtitle.c b/dvbsubtitle.c index d5ae60b0..fff3f8b8 100644 --- a/dvbsubtitle.c +++ b/dvbsubtitle.c @@ -7,7 +7,7 @@ * Original author: Marco Schluessler * With some input from the "subtitles plugin" by Pekka Virtanen * - * $Id: dvbsubtitle.c 3.10 2015/01/20 14:53:57 kls Exp $ + * $Id: dvbsubtitle.c 4.1 2015/03/25 12:37:08 kls Exp $ */ #include "dvbsubtitle.h" @@ -1516,8 +1516,7 @@ void cDvbSubtitleConverter::SetOsdData(void) osdDeltaX = osdDeltaY = 0; } else { - osdFactorX = VideoAspect * OsdHeight / displayWidth; - osdFactorY = double(OsdHeight) / displayHeight; + osdFactorX = osdFactorY = min(double(OsdWidth) / displayWidth, double(OsdHeight) / displayHeight); osdDeltaX = (OsdWidth - displayWidth * osdFactorX) / 2; osdDeltaY = (OsdHeight - displayHeight * osdFactorY) / 2; } diff --git a/dvbsubtitle.h b/dvbsubtitle.h index f178b61c..4d326015 100644 --- a/dvbsubtitle.h +++ b/dvbsubtitle.h @@ -6,7 +6,7 @@ * * Original author: Marco Schluessler * - * $Id: dvbsubtitle.h 3.2 2015/01/14 10:01:48 kls Exp $ + * $Id: dvbsubtitle.h 4.1 2015/04/28 09:25:57 kls Exp $ */ #ifndef __DVBSUBTITLE_H @@ -48,7 +48,7 @@ private: public: cDvbSubtitleConverter(void); virtual ~cDvbSubtitleConverter(); - void Action(void); + virtual void Action(void); void Reset(void); void Freeze(bool Status) { frozen = Status; } int ConvertFragments(const uchar *Data, int Length); // for legacy PES recordings diff --git a/eit.c b/eit.c index 1f960bb2..b5c671d2 100644 --- a/eit.c +++ b/eit.c @@ -8,7 +8,7 @@ * Robert Schneider and Rolf Hakenes . * Adapted to 'libsi' for VDR 1.3.0 by Marcel Wiesweg . * - * $Id: eit.c 3.6 2015/02/01 14:55:27 kls Exp $ + * $Id: eit.c 4.1 2015/08/23 10:43:36 kls Exp $ */ #include "eit.h" @@ -24,31 +24,53 @@ class cEIT : public SI::EIT { public: - cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bool OnlyRunningStatus = false); + cEIT(cSectionSyncerHash &SectionSyncerHash, int Source, u_char Tid, const u_char *Data); }; -cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bool OnlyRunningStatus) +cEIT::cEIT(cSectionSyncerHash &SectionSyncerHash, int Source, u_char Tid, const u_char *Data) :SI::EIT(Data, false) { if (!CheckCRCAndParse()) return; + int HashId = Tid * getServiceId(); + cSectionSyncerEntry *SectionSyncerEntry = SectionSyncerHash.Get(HashId); + if (!SectionSyncerEntry) { + SectionSyncerEntry = new cSectionSyncerEntry; + SectionSyncerHash.Add(SectionSyncerEntry, HashId); + } + bool Process = SectionSyncerEntry->Sync(getVersionNumber(), getSectionNumber(), getLastSectionNumber()); + if (Tid != 0x4E && !Process) // we need to set the 'seen' tag to watch the running status of the present/following event + return; time_t Now = time(NULL); if (Now < VALID_TIME) return; // we need the current time for handling PDC descriptors - if (!Channels.Lock(false, 10)) + cStateKey ChannelsStateKey; + cChannels *Channels = cChannels::GetChannelsWrite(ChannelsStateKey, 10); + if (!Channels) { + SectionSyncerEntry->Repeat(); // let's not miss any section of the EIT return; + } tChannelID channelID(Source, getOriginalNetworkId(), getTransportStreamId(), getServiceId()); - cChannel *channel = Channels.GetByChannelID(channelID, true); - if (!channel || EpgHandlers.IgnoreChannel(channel)) { - Channels.Unlock(); + cChannel *Channel = Channels->GetByChannelID(channelID, true); + if (!Channel || EpgHandlers.IgnoreChannel(Channel)) { + ChannelsStateKey.Remove(false); return; } - EpgHandlers.BeginSegmentTransfer(channel, OnlyRunningStatus); - bool handledExternally = EpgHandlers.HandledExternally(channel); - cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(channel, true); + cStateKey SchedulesStateKey; + cSchedules *Schedules = cSchedules::GetSchedulesWrite(SchedulesStateKey, 10); + if (!Schedules) { + SectionSyncerEntry->Repeat(); // let's not miss any section of the EIT + ChannelsStateKey.Remove(false); + return; + } + + bool ChannelsModified = false; + EpgHandlers.BeginSegmentTransfer(Channel); + bool handledExternally = EpgHandlers.HandledExternally(Channel); + cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(Channel, true); bool Empty = true; bool Modified = false; @@ -74,8 +96,6 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bo cEvent *rEvent = NULL; cEvent *pEvent = (cEvent *)pSchedule->GetEvent(SiEitEvent.getEventId(), StartTime); if (!pEvent || handledExternally) { - if (OnlyRunningStatus) - continue; if (handledExternally && !EpgHandlers.IsUpdate(SiEitEvent.getEventId(), StartTime, Tid, getVersionNumber())) continue; // If we don't have that event yet, we create a new one. @@ -94,14 +114,6 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bo // The lower the table ID, the more "current" the information. if (Tid > TableID) continue; - // If the new event comes from the same table and has the same version number - // as the existing one, let's skip it to avoid unnecessary work. - // Unfortunately some stations (like, e.g. "Premiere") broadcast their EPG data on several transponders (like - // the actual Premiere transponder and the Sat.1/Pro7 transponder), but use different version numbers on - // each of them :-( So if one DVB card is tuned to the Premiere transponder, while an other one is tuned - // to the Sat.1/Pro7 transponder, events will keep toggling because of the bogus version numbers. - else if (Tid == TableID && pEvent->Version() == getVersionNumber()) - continue; EpgHandlers.SetEventID(pEvent, SiEitEvent.getEventId()); // unfortunately some stations use different event ids for the same event in different tables :-( EpgHandlers.SetStartTime(pEvent, StartTime); EpgHandlers.SetDuration(pEvent, Duration); @@ -110,11 +122,9 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bo pEvent->SetTableID(Tid); if (Tid == 0x4E) { // we trust only the present/following info on the actual TS if (SiEitEvent.getRunningStatus() >= SI::RunningStatusNotRunning) - pSchedule->SetRunningStatus(pEvent, SiEitEvent.getRunningStatus(), channel); - } - if (OnlyRunningStatus) { - pEvent->SetVersion(0xFF); // we have already changed the table id above, so set the version to an invalid value to make sure the next full run will be executed - continue; // do this before setting the version, so that the full update can be done later + pSchedule->SetRunningStatus(pEvent, SiEitEvent.getRunningStatus(), Channel); + if (!Process) + continue; } pEvent->SetVersion(getVersionNumber()); @@ -206,7 +216,7 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bo break; case SI::TimeShiftedEventDescriptorTag: { SI::TimeShiftedEventDescriptor *tsed = (SI::TimeShiftedEventDescriptor *)d; - cSchedule *rSchedule = (cSchedule *)Schedules->GetSchedule(tChannelID(Source, channel->Nid(), channel->Tid(), tsed->getReferenceServiceId())); + cSchedule *rSchedule = (cSchedule *)Schedules->GetSchedule(tChannelID(Source, Channel->Nid(), Channel->Tid(), tsed->getReferenceServiceId())); if (!rSchedule) break; rEvent = (cEvent *)rSchedule->GetEvent(tsed->getReferenceEventId()); @@ -226,18 +236,18 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bo char linkName[ld->privateData.getLength() + 1]; strn0cpy(linkName, (const char *)ld->privateData.getData(), sizeof(linkName)); // TODO is there a standard way to determine the character set of this string? - cChannel *link = Channels.GetByChannelID(linkID); - if (link != channel) { // only link to other channels, not the same one - //fprintf(stderr, "Linkage %s %4d %4d %5d %5d %5d %5d %02X '%s'\n", hit ? "*" : "", channel->Number(), link ? link->Number() : -1, SiEitEvent.getEventId(), ld->getOriginalNetworkId(), ld->getTransportStreamId(), ld->getServiceId(), ld->getLinkageType(), linkName);//XXX + cChannel *link = Channels->GetByChannelID(linkID); + if (link != Channel) { // only link to other channels, not the same one if (link) { if (Setup.UpdateChannels == 1 || Setup.UpdateChannels >= 3) - link->SetName(linkName, "", ""); + ChannelsModified |= link->SetName(linkName, "", ""); } else if (Setup.UpdateChannels >= 4) { - cChannel *transponder = channel; - if (channel->Tid() != ld->getTransportStreamId()) - transponder = Channels.GetByTransponderID(linkID); - link = Channels.NewChannel(transponder, linkName, "", "", ld->getOriginalNetworkId(), ld->getTransportStreamId(), ld->getServiceId()); + cChannel *Transponder = Channel; + if (Channel->Tid() != ld->getTransportStreamId()) + Transponder = Channels->GetByTransponderID(linkID); + link = Channels->NewChannel(Transponder, linkName, "", "", ld->getOriginalNetworkId(), ld->getTransportStreamId(), ld->getServiceId()); + ChannelsModified = true; //XXX patFilter->Trigger(); } if (link) { @@ -247,7 +257,7 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bo } } else - channel->SetPortalName(linkName); + ChannelsModified |= Channel->SetPortalName(linkName); } } } @@ -293,7 +303,7 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bo EpgHandlers.FixEpgBugs(pEvent); if (LinkChannels) - channel->SetLinkChannels(LinkChannels); + ChannelsModified |= Channel->SetLinkChannels(LinkChannels); Modified = true; EpgHandlers.HandleEvent(pEvent); if (handledExternally) @@ -302,16 +312,17 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bo if (Tid == 0x4E) { if (Empty && getSectionNumber() == 0) // ETR 211: an empty entry in section 0 of table 0x4E means there is currently no event running - pSchedule->ClrRunningStatus(channel); + pSchedule->ClrRunningStatus(Channel); pSchedule->SetPresentSeen(); } - if (Modified && !OnlyRunningStatus) { + if (Modified) { EpgHandlers.SortSchedule(pSchedule); EpgHandlers.DropOutdated(pSchedule, SegmentStart, SegmentEnd, Tid, getVersionNumber()); - Schedules->SetModified(pSchedule); + pSchedule->SetModified(); } - Channels.Unlock(); - EpgHandlers.EndSegmentTransfer(Modified, OnlyRunningStatus); + SchedulesStateKey.Remove(Modified); + ChannelsStateKey.Remove(ChannelsModified); + EpgHandlers.EndSegmentTransfer(Modified); } // --- cTDT ------------------------------------------------------------------ @@ -372,6 +383,13 @@ cEitFilter::cEitFilter(void) Set(0x14, 0x70); // TDT } +void cEitFilter::SetStatus(bool On) +{ + cMutexLock MutexLock(&mutex); + cFilter::SetStatus(On); + sectionSyncerHash.Clear(); +} + void cEitFilter::SetDisableUntil(time_t Time) { disableUntil = Time; @@ -379,6 +397,7 @@ void cEitFilter::SetDisableUntil(time_t Time) void cEitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length) { + cMutexLock MutexLock(&mutex); if (disableUntil) { if (time(NULL) > disableUntil) disableUntil = 0; @@ -387,22 +406,8 @@ void cEitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length } switch (Pid) { case 0x12: { - if (Tid >= 0x4E && Tid <= 0x6F) { - cSchedulesLock SchedulesLock(true, 10); - cSchedules *Schedules = (cSchedules *)cSchedules::Schedules(SchedulesLock); - if (Schedules) - cEIT EIT(Schedules, Source(), Tid, Data); - else { - // If we don't get a write lock, let's at least get a read lock, so - // that we can set the running status and 'seen' timestamp (well, actually - // with a read lock we shouldn't be doing that, but it's only integers that - // get changed, so it should be ok) - cSchedulesLock SchedulesLock; - cSchedules *Schedules = (cSchedules *)cSchedules::Schedules(SchedulesLock); - if (Schedules) - cEIT EIT(Schedules, Source(), Tid, Data, true); - } - } + if (Tid >= 0x4E && Tid <= 0x6F) + cEIT EIT(sectionSyncerHash, Source(), Tid, Data); } break; case 0x14: { diff --git a/eit.h b/eit.h index ec30253e..51af6433 100644 --- a/eit.h +++ b/eit.h @@ -4,21 +4,29 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: eit.h 3.0 2010/01/03 15:28:34 kls Exp $ + * $Id: eit.h 4.1 2015/07/25 11:03:53 kls Exp $ */ #ifndef __EIT_H #define __EIT_H #include "filter.h" +#include "tools.h" + +class cSectionSyncerEntry : public cListObject, public cSectionSyncer {}; + +class cSectionSyncerHash : public cHash {}; class cEitFilter : public cFilter { private: + cMutex mutex; + cSectionSyncerHash sectionSyncerHash; static time_t disableUntil; protected: virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length); public: cEitFilter(void); + virtual void SetStatus(bool On); static void SetDisableUntil(time_t Time); }; diff --git a/eitscan.c b/eitscan.c index 77f15c64..41ac25e2 100644 --- a/eitscan.c +++ b/eitscan.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: eitscan.c 3.0 2012/04/07 14:39:28 kls Exp $ + * $Id: eitscan.c 4.2 2015/09/10 11:05:03 kls Exp $ */ #include "eitscan.h" @@ -45,13 +45,13 @@ int cScanData::Compare(const cListObject &ListObject) const class cScanList : public cList { public: - void AddTransponders(cList *Channels); + void AddTransponders(const cList *Channels); void AddTransponder(const cChannel *Channel); }; -void cScanList::AddTransponders(cList *Channels) +void cScanList::AddTransponders(const cList *Channels) { - for (cChannel *ch = Channels->First(); ch; ch = Channels->Next(ch)) + for (const cChannel *ch = Channels->First(); ch; ch = Channels->Next(ch)) AddTransponder(ch); Sort(); } @@ -118,7 +118,8 @@ void cEITScanner::ForceScan(void) void cEITScanner::Activity(void) { if (currentChannel) { - Channels.SwitchTo(currentChannel); + LOCK_CHANNELS_READ; + Channels->SwitchTo(currentChannel); currentChannel = 0; } lastActivity = time(NULL); @@ -129,7 +130,8 @@ void cEITScanner::Process(void) if (Setup.EPGScanTimeout || !lastActivity) { // !lastActivity means a scan was forced time_t now = time(NULL); if (now - lastScan > ScanTimeout && now - lastActivity > ActivityTimeout) { - if (Channels.Lock(false, 10)) { + cStateKey StateKey; + if (const cChannels *Channels = cChannels::GetChannelsRead(StateKey, 10)) { if (!scanList) { scanList = new cScanList; if (transponderList) { @@ -137,7 +139,7 @@ void cEITScanner::Process(void) delete transponderList; transponderList = NULL; } - scanList->AddTransponders(&Channels); + scanList->AddTransponders(Channels); } bool AnyDeviceSwitched = false; for (int i = 0; i < cDevice::NumDevices(); i++) { @@ -149,6 +151,10 @@ void cEITScanner::Process(void) if (!Channel->Ca() || Channel->Ca() == Device->DeviceNumber() + 1 || Channel->Ca() >= CA_ENCRYPTED_MIN) { if (Device->ProvidesTransponder(Channel)) { if (Device->Priority() < 0) { + if (const cPositioner *Positioner = Device->Positioner()) { + if (Positioner->LastLongitude() != cSource::Position(Channel->Source())) + continue; + } bool MaySwitchTransponder = Device->MaySwitchTransponder(Channel); if (MaySwitchTransponder || Device->ProvidesTransponderExclusively(Channel) && now - lastActivity > Setup.EPGScanTimeout * 3600) { if (!MaySwitchTransponder) { @@ -177,7 +183,7 @@ void cEITScanner::Process(void) if (lastActivity == 0) // this was a triggered scan Activity(); } - Channels.Unlock(); + StateKey.Remove(); } lastScan = time(NULL); } diff --git a/eitscan.h b/eitscan.h index 25658b6b..02d98222 100644 --- a/eitscan.h +++ b/eitscan.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: eitscan.h 3.0 2012/03/07 13:54:16 kls Exp $ + * $Id: eitscan.h 4.0 2012/03/07 13:54:16 kls Exp $ */ #ifndef __EITSCAN_H diff --git a/epg.c b/epg.c index 096b68cf..4297350e 100644 --- a/epg.c +++ b/epg.c @@ -7,7 +7,7 @@ * Original version (as used in VDR before 1.3.0) written by * Robert Schneider and Rolf Hakenes . * - * $Id: epg.c 3.3 2013/12/28 11:33:08 kls Exp $ + * $Id: epg.c 4.2 2015/09/10 10:58:19 kls Exp $ */ #include "epg.h" @@ -15,7 +15,6 @@ #include #include #include "libsi/si.h" -#include "timers.h" #define RUNNINGSTATUSTIMEOUT 30 // seconds before the running status is considered unknown #define EPGDATAWRITEDELTA 600 // seconds between writing the epg.data file @@ -111,9 +110,12 @@ tComponent *cComponents::GetComponent(int Index, uchar Stream, uchar Type) // --- cEvent ---------------------------------------------------------------- +cMutex cEvent::numTimersMutex; + cEvent::cEvent(tEventID EventID) { schedule = NULL; + numTimers = 0; eventID = EventID; tableID = 0xFF; // actual table ids are 0x4E..0x60 version = 0xFF; // actual version numbers are 0..31 @@ -170,9 +172,9 @@ void cEvent::SetVersion(uchar Version) version = Version; } -void cEvent::SetRunningStatus(int RunningStatus, cChannel *Channel) +void cEvent::SetRunningStatus(int RunningStatus, const cChannel *Channel) { - if (Channel && runningStatus != RunningStatus && (RunningStatus > SI::RunningStatusNotRunning || runningStatus > SI::RunningStatusUndefined) && Channel->HasTimer()) + if (Channel && runningStatus != RunningStatus && (RunningStatus > SI::RunningStatusNotRunning || runningStatus > SI::RunningStatusUndefined) && schedule && schedule->HasTimer()) isyslog("channel %d (%s) event %s status %d", Channel->Number(), Channel->Name(), *ToDescr(), RunningStatus); runningStatus = RunningStatus; } @@ -243,13 +245,22 @@ cString cEvent::ToDescr(void) const return cString::sprintf("%s %s-%s %s'%s'", *GetDateString(), *GetTimeString(), *GetEndTimeString(), vpsbuf, Title()); } -bool cEvent::HasTimer(void) const +void cEvent::IncNumTimers(void) const { - for (cTimer *t = Timers.First(); t; t = Timers.Next(t)) { - if (t->Event() == this) - return true; - } - return false; + numTimersMutex.Lock(); + numTimers++; + if (schedule) + schedule->IncNumTimers(); + numTimersMutex.Unlock(); +} + +void cEvent::DecNumTimers(void) const +{ + numTimersMutex.Lock(); + numTimers--; + if (schedule) + schedule->DecNumTimers(); + numTimersMutex.Unlock(); } bool cEvent::IsRunning(bool OrAboutToStart) const @@ -605,9 +616,9 @@ void ReportEpgBugFixStats(bool Force) bool PrintedStats = false; char *q = buffer; *buffer = 0; + LOCK_CHANNELS_READ; for (int c = 0; c < p->n; c++) { - cChannel *channel = Channels.GetByChannelID(p->channelIDs[c], true); - if (channel) { + if (const cChannel *Channel = Channels->GetByChannelID(p->channelIDs[c], true)) { if (!GotHits) { dsyslog("====================="); dsyslog("EPG bugfix statistics"); @@ -623,7 +634,7 @@ void ReportEpgBugFixStats(bool Force) q += snprintf(q, sizeof(buffer) - (q - buffer), "%-3d %-4d", i, p->hits); PrintedStats = true; } - q += snprintf(q, sizeof(buffer) - (q - buffer), "%s%s", delim, channel->Name()); + q += snprintf(q, sizeof(buffer) - (q - buffer), "%s%s", delim, Channel->Name()); delim = ", "; if (q - buffer > 80) { q += snprintf(q, sizeof(buffer) - (q - buffer), "%s...", delim); @@ -650,7 +661,7 @@ static void StripControlCharacters(char *s) uchar *p = (uchar *)s; if (l == 2 && *p == 0xC2) // UTF-8 sequence p++; - if (*p == 0x86 || *p == 0x87) { + if (*p == 0x86 || *p == 0x87 || *p == 0x0D) { memmove(s, p + 1, len - l + 1); // we also copy the terminating 0! len -= l; l = 0; @@ -878,14 +889,32 @@ Final: // --- cSchedule ------------------------------------------------------------- +cMutex cSchedule::numTimersMutex; + cSchedule::cSchedule(tChannelID ChannelID) { channelID = ChannelID; + events.SetUseGarbageCollector(); + numTimers = 0; hasRunning = false; modified = 0; presentSeen = 0; } +void cSchedule::IncNumTimers(void) const +{ + numTimersMutex.Lock(); + numTimers++; + numTimersMutex.Unlock(); +} + +void cSchedule::DecNumTimers(void) const +{ + numTimersMutex.Lock(); + numTimers--; + numTimersMutex.Unlock(); +} + cEvent *cSchedule::AddEvent(cEvent *Event) { events.Add(Event); @@ -897,8 +926,6 @@ cEvent *cSchedule::AddEvent(cEvent *Event) void cSchedule::DelEvent(cEvent *Event) { if (Event->schedule == this) { - if (hasRunning && Event->IsRunning()) - ClrRunningStatus(); UnhashEvent(Event); events.Del(Event); } @@ -922,7 +949,7 @@ const cEvent *cSchedule::GetPresentEvent(void) const { const cEvent *pe = NULL; time_t now = time(NULL); - for (cEvent *p = events.First(); p; p = events.Next(p)) { + for (const cEvent *p = events.First(); p; p = events.Next(p)) { if (p->StartTime() <= now) pe = p; else if (p->StartTime() > now + 3600) @@ -962,7 +989,7 @@ const cEvent *cSchedule::GetEventAround(time_t Time) const { const cEvent *pe = NULL; time_t delta = INT_MAX; - for (cEvent *p = events.First(); p; p = events.Next(p)) { + for (const cEvent *p = events.First(); p; p = events.Next(p)) { time_t dt = Time - p->StartTime(); if (dt >= 0 && dt < delta && p->EndTime() >= Time) { delta = dt; @@ -972,7 +999,7 @@ const cEvent *cSchedule::GetEventAround(time_t Time) const return pe; } -void cSchedule::SetRunningStatus(cEvent *Event, int RunningStatus, cChannel *Channel) +void cSchedule::SetRunningStatus(cEvent *Event, int RunningStatus, const cChannel *Channel) { hasRunning = false; for (cEvent *p = events.First(); p; p = events.Next(p)) { @@ -987,6 +1014,7 @@ void cSchedule::SetRunningStatus(cEvent *Event, int RunningStatus, cChannel *Cha if (p->RunningStatus() >= SI::RunningStatusPausing) hasRunning = true; } + SetPresentSeen(); } void cSchedule::ClrRunningStatus(cChannel *Channel) @@ -1019,32 +1047,29 @@ void cSchedule::Sort(void) p->SetRunningStatus(SI::RunningStatusNotRunning); } } + SetModified(); } 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": - if (hasRunning && p->IsRunning()) - ClrRunningStatus(); - UnhashEvent(p); - p->eventID = 0; - p->startTime = 0; - } - } - else - break; - } - } + cEvent *p = events.First(); + while (p) { + cEvent *n = 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. + DelEvent(p); + } + } + else + break; + } + p = n; + } } } @@ -1066,9 +1091,9 @@ void cSchedule::Cleanup(time_t Time) void cSchedule::Dump(FILE *f, const char *Prefix, eDumpMode DumpMode, time_t AtTime) const { - cChannel *channel = Channels.GetByChannelID(channelID, true); - if (channel) { - fprintf(f, "%sC %s %s\n", Prefix, *channel->GetChannelID().ToString(), channel->Name()); + LOCK_CHANNELS_READ; + if (const cChannel *Channel = Channels->GetByChannelID(channelID, true)) { + fprintf(f, "%sC %s %s\n", Prefix, *Channel->GetChannelID().ToString(), Channel->Name()); const cEvent *p; switch (DumpMode) { case dmAll: { @@ -1111,12 +1136,10 @@ bool cSchedule::Read(FILE *f, cSchedules *Schedules) if (*s) { tChannelID channelID = tChannelID::FromString(s); if (channelID.Valid()) { - cSchedule *p = Schedules->AddSchedule(channelID); - if (p) { + if (cSchedule *p = Schedules->AddSchedule(channelID)) { if (!cEvent::Read(f, p)) return false; p->Sort(); - Schedules->SetModified(p); } } else { @@ -1164,12 +1187,12 @@ void cEpgDataWriter::Perform(void) { cMutexLock MutexLock(&mutex); // to make sure fore- and background calls don't cause parellel dumps! { - cSchedulesLock SchedulesLock(true, 1000); - cSchedules *s = (cSchedules *)cSchedules::Schedules(SchedulesLock); - if (s) { + cStateKey StateKey; + if (cSchedules *Schedules = cSchedules::GetSchedulesWrite(StateKey, 1000)) { time_t now = time(NULL); - for (cSchedule *p = s->First(); p; p = s->Next(p)) + for (cSchedule *p = Schedules->First(); p; p = Schedules->Next(p)) p->Cleanup(now); + StateKey.Remove(); } } if (dump) @@ -1178,29 +1201,25 @@ void cEpgDataWriter::Perform(void) static cEpgDataWriter EpgDataWriter; -// --- cSchedulesLock -------------------------------------------------------- - -cSchedulesLock::cSchedulesLock(bool WriteLock, int TimeoutMs) -{ - locked = cSchedules::schedules.rwlock.Lock(WriteLock, TimeoutMs); -} - -cSchedulesLock::~cSchedulesLock() -{ - if (locked) - cSchedules::schedules.rwlock.Unlock(); -} - // --- cSchedules ------------------------------------------------------------ cSchedules cSchedules::schedules; char *cSchedules::epgDataFileName = NULL; time_t cSchedules::lastDump = time(NULL); -time_t cSchedules::modified = 0; -const cSchedules *cSchedules::Schedules(cSchedulesLock &SchedulesLock) +cSchedules::cSchedules(void) +:cList("Schedules") { - return SchedulesLock.Locked() ? &schedules : NULL; +} + +const cSchedules *cSchedules::GetSchedulesRead(cStateKey &StateKey, int TimeoutMs) +{ + return schedules.Lock(StateKey, false, TimeoutMs) ? &schedules : NULL; +} + +cSchedules *cSchedules::GetSchedulesWrite(cStateKey &StateKey, int TimeoutMs) +{ + return schedules.Lock(StateKey, true, TimeoutMs) ? &schedules : NULL; } void cSchedules::SetEpgDataFileName(const char *FileName) @@ -1210,12 +1229,6 @@ void cSchedules::SetEpgDataFileName(const char *FileName) EpgDataWriter.SetDump(epgDataFileName != NULL); } -void cSchedules::SetModified(cSchedule *Schedule) -{ - Schedule->SetModified(); - modified = time(NULL); -} - void cSchedules::Cleanup(bool Force) { if (Force) @@ -1232,83 +1245,59 @@ void cSchedules::Cleanup(bool Force) void cSchedules::ResetVersions(void) { - cSchedulesLock SchedulesLock(true); - cSchedules *s = (cSchedules *)Schedules(SchedulesLock); - if (s) { - for (cSchedule *Schedule = s->First(); Schedule; Schedule = s->Next(Schedule)) - Schedule->ResetVersions(); - } -} - -bool cSchedules::ClearAll(void) -{ - cSchedulesLock SchedulesLock(true, 1000); - cSchedules *s = (cSchedules *)Schedules(SchedulesLock); - if (s) { - for (cTimer *Timer = Timers.First(); Timer; Timer = Timers.Next(Timer)) - Timer->SetEvent(NULL); - for (cSchedule *Schedule = s->First(); Schedule; Schedule = s->Next(Schedule)) - Schedule->Cleanup(INT_MAX); - return true; - } - return false; + LOCK_SCHEDULES_WRITE; + for (cSchedule *Schedule = Schedules->First(); Schedule; Schedule = Schedules->Next(Schedule)) + Schedule->ResetVersions(); } bool cSchedules::Dump(FILE *f, const char *Prefix, eDumpMode DumpMode, time_t AtTime) { - cSchedulesLock SchedulesLock; - cSchedules *s = (cSchedules *)Schedules(SchedulesLock); - if (s) { - cSafeFile *sf = NULL; - if (!f) { - sf = new cSafeFile(epgDataFileName); - if (sf->Open()) - f = *sf; - else { - LOG_ERROR; - delete sf; - return false; - } - } - for (cSchedule *p = s->First(); p; p = s->Next(p)) - p->Dump(f, Prefix, DumpMode, AtTime); - if (sf) { - sf->Close(); + cSafeFile *sf = NULL; + if (!f) { + sf = new cSafeFile(epgDataFileName); + if (sf->Open()) + f = *sf; + else { + LOG_ERROR; delete sf; + return false; } - return true; } - return false; + LOCK_SCHEDULES_READ; + for (const cSchedule *p = Schedules->First(); p; p = Schedules->Next(p)) + p->Dump(f, Prefix, DumpMode, AtTime); + if (sf) { + sf->Close(); + delete sf; + } + return true; } bool cSchedules::Read(FILE *f) { - cSchedulesLock SchedulesLock(true, 1000); - cSchedules *s = (cSchedules *)Schedules(SchedulesLock); - if (s) { - bool OwnFile = f == NULL; - if (OwnFile) { - if (epgDataFileName && access(epgDataFileName, R_OK) == 0) { - dsyslog("reading EPG data from %s", epgDataFileName); - if ((f = fopen(epgDataFileName, "r")) == NULL) { - LOG_ERROR; - return false; - } - } - else + bool OwnFile = f == NULL; + if (OwnFile) { + if (epgDataFileName && access(epgDataFileName, R_OK) == 0) { + dsyslog("reading EPG data from %s", epgDataFileName); + if ((f = fopen(epgDataFileName, "r")) == NULL) { + LOG_ERROR; return false; + } } - bool result = cSchedule::Read(f, s); - if (OwnFile) - fclose(f); - if (result) { - // Initialize the channels' schedule pointers, so that the first WhatsOn menu will come up faster: - for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) - s->GetSchedule(Channel); - } - return result; + else + return false; } - return false; + LOCK_CHANNELS_WRITE; + LOCK_SCHEDULES_WRITE; + bool result = cSchedule::Read(f, Schedules); + if (OwnFile) + fclose(f); + if (result) { + // Initialize the channels' schedule pointers, so that the first WhatsOn menu will come up faster: + for (cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) + Schedules->GetSchedule(Channel); + } + return result; } cSchedule *cSchedules::AddSchedule(tChannelID ChannelID) @@ -1318,9 +1307,6 @@ cSchedule *cSchedules::AddSchedule(tChannelID ChannelID) if (!p) { p = new cSchedule(ChannelID); Add(p); - cChannel *channel = Channels.GetByChannelID(ChannelID); - if (channel) - channel->schedule = p; } return p; } @@ -1328,7 +1314,7 @@ cSchedule *cSchedules::AddSchedule(tChannelID ChannelID) const cSchedule *cSchedules::GetSchedule(tChannelID ChannelID) const { ChannelID.ClrRid(); - for (cSchedule *p = First(); p; p = Next(p)) { + for (const cSchedule *p = First(); p; p = Next(p)) { if (p->ChannelID() == ChannelID) return p; } @@ -1541,18 +1527,18 @@ void cEpgHandlers::DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t Schedule->DropOutdated(SegmentStart, SegmentEnd, TableID, Version); } -void cEpgHandlers::BeginSegmentTransfer(const cChannel *Channel, bool OnlyRunningStatus) +void cEpgHandlers::BeginSegmentTransfer(const cChannel *Channel) { for (cEpgHandler *eh = First(); eh; eh = Next(eh)) { - if (eh->BeginSegmentTransfer(Channel, OnlyRunningStatus)) + if (eh->BeginSegmentTransfer(Channel, false)) return; } } -void cEpgHandlers::EndSegmentTransfer(bool Modified, bool OnlyRunningStatus) +void cEpgHandlers::EndSegmentTransfer(bool Modified) { for (cEpgHandler *eh = First(); eh; eh = Next(eh)) { - if (eh->EndSegmentTransfer(Modified, OnlyRunningStatus)) + if (eh->EndSegmentTransfer(Modified, false)) return; } } diff --git a/epg.h b/epg.h index 65fbcbf9..b6f7d68e 100644 --- a/epg.h +++ b/epg.h @@ -7,7 +7,7 @@ * Original version (as used in VDR before 1.3.0) written by * Robert Schneider and Rolf Hakenes . * - * $Id: epg.h 3.1 2013/08/23 10:50:05 kls Exp $ + * $Id: epg.h 4.1 2015/08/09 11:25:04 kls Exp $ */ #ifndef __EPG_H @@ -66,13 +66,15 @@ public: class cSchedule; -typedef u_int32_t tEventID; +typedef u_int16_t tEventID; class cEvent : public cListObject { friend class cSchedule; private: + static cMutex numTimersMutex; // Protects numTimers, because it might be accessed from parallel read locks // The sequence of these parameters is optimized for minimal memory waste! cSchedule *schedule; // The Schedule this event belongs to + mutable u_int16_t numTimers;// The number of timers that use this event tEventID eventID; // Event ID of this event uchar tableID; // Table ID this event came from uchar version; // Version number of section this event came from @@ -109,7 +111,9 @@ public: time_t Vps(void) const { return vps; } time_t Seen(void) const { return seen; } bool SeenWithin(int Seconds) const { return time(NULL) - seen < Seconds; } - bool HasTimer(void) const; + void IncNumTimers(void) const; + void DecNumTimers(void) const; + bool HasTimer(void) const { return numTimers > 0; } bool IsRunning(bool OrAboutToStart = false) const; static const char *ContentToString(uchar Content); cString GetParentalRatingString(void) const; @@ -120,7 +124,7 @@ public: void SetEventID(tEventID EventID); void SetTableID(uchar TableID); void SetVersion(uchar Version); - void SetRunningStatus(int RunningStatus, cChannel *Channel = NULL); + void SetRunningStatus(int RunningStatus, const cChannel *Channel = NULL); void SetTitle(const char *Title); void SetShortText(const char *ShortText); void SetDescription(const char *Description); @@ -142,28 +146,33 @@ class cSchedules; class cSchedule : public cListObject { private: + static cMutex numTimersMutex; // Protects numTimers, because it might be accessed from parallel read locks tChannelID channelID; cList events; cHash eventsHashID; cHash eventsHashStartTime; + mutable u_int16_t numTimers;// The number of timers that use this schedule bool hasRunning; - time_t modified; + int modified; time_t presentSeen; public: cSchedule(tChannelID ChannelID); tChannelID ChannelID(void) const { return channelID; } - time_t Modified(void) const { return modified; } + bool Modified(int &State) const { bool Result = State != modified; State = modified; return Result; } time_t PresentSeen(void) const { return presentSeen; } bool PresentSeenWithin(int Seconds) const { return time(NULL) - presentSeen < Seconds; } - void SetModified(void) { modified = time(NULL); } + void SetModified(void) { modified++; } void SetPresentSeen(void) { presentSeen = time(NULL); } - void SetRunningStatus(cEvent *Event, int RunningStatus, cChannel *Channel = NULL); + void SetRunningStatus(cEvent *Event, int RunningStatus, const cChannel *Channel = NULL); 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); + void IncNumTimers(void) const; + void DecNumTimers(void) const; + bool HasTimer(void) const { return numTimers > 0; } cEvent *AddEvent(cEvent *Event); void DelEvent(cEvent *Event); void HashEvent(cEvent *Event); @@ -177,35 +186,23 @@ public: static bool Read(FILE *f, cSchedules *Schedules); }; -class cSchedulesLock { -private: - bool locked; -public: - cSchedulesLock(bool WriteLock = false, int TimeoutMs = 0); - ~cSchedulesLock(); - bool Locked(void) { return locked; } - }; - class cSchedules : public cList { friend class cSchedule; - friend class cSchedulesLock; private: - cRwLock rwlock; static cSchedules schedules; static char *epgDataFileName; static time_t lastDump; - static time_t modified; public: + cSchedules(void); + static const cSchedules *GetSchedulesRead(cStateKey &StateKey, int TimeoutMs = 0); + ///< Gets the list of schedules for read access. + ///< See cTimers::GetTimersRead() for details. + static cSchedules *GetSchedulesWrite(cStateKey &StateKey, int TimeoutMs = 0); + ///< Gets the list of schedules for write access. + ///< See cTimers::GetTimersWrite() for details. static void SetEpgDataFileName(const char *FileName); - static const cSchedules *Schedules(cSchedulesLock &SchedulesLock); - ///< Caller must provide a cSchedulesLock which has to survive the entire - ///< time the returned cSchedules is accessed. Once the cSchedules is no - ///< longer used, the cSchedulesLock must be destroyed. - static time_t Modified(void) { return modified; } - static void SetModified(cSchedule *Schedule); static void Cleanup(bool Force = false); static void ResetVersions(void); - static bool ClearAll(void); static bool Dump(FILE *f = NULL, const char *Prefix = "", eDumpMode DumpMode = dmAll, time_t AtTime = 0); static bool Read(FILE *f = NULL); cSchedule *AddSchedule(tChannelID ChannelID); @@ -213,6 +210,17 @@ public: const cSchedule *GetSchedule(const cChannel *Channel, bool AddIfMissing = false) const; }; +// Provide lock controlled access to the list: + +DEF_LIST_LOCK(Schedules); + +// These macros provide a convenient way of locking the global schedules list +// and making sure the lock is released as soon as the current scope is left +// (note that these macros wait forever to obtain the lock!): + +#define LOCK_SCHEDULES_READ USE_LIST_LOCK_READ(Schedules); +#define LOCK_SCHEDULES_WRITE USE_LIST_LOCK_WRITE(Schedules); + class cEpgDataReader : public cThread { public: cEpgDataReader(void); @@ -273,12 +281,14 @@ public: virtual bool DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version) { return false; } ///< Takes a look at all EPG events between SegmentStart and SegmentEnd and ///< drops outdated events. - virtual bool BeginSegmentTransfer(const cChannel *Channel, bool OnlyRunningStatus) { return false; } + virtual bool BeginSegmentTransfer(const cChannel *Channel, bool Dummy) { return false; } // TODO remove obsolete Dummy ///< Called directly after IgnoreChannel() before any other handler method is called. ///< Designed to give handlers the possibility to prepare a database transaction. - virtual bool EndSegmentTransfer(bool Modified, bool OnlyRunningStatus) { return false; } + ///< Dummy is for backward compatibility and may be removed in a future version. + virtual bool EndSegmentTransfer(bool Modified, bool Dummy) { return false; } // TODO remove obsolete Dummy ///< Called after the segment data has been processed. ///< At this point handlers should close/commit/rollback any pending database transactions. + ///< Dummy is for backward compatibility and may be removed in a future version. }; class cEpgHandlers : public cList { @@ -301,8 +311,8 @@ public: void HandleEvent(cEvent *Event); void SortSchedule(cSchedule *Schedule); void DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version); - void BeginSegmentTransfer(const cChannel *Channel, bool OnlyRunningStatus); - void EndSegmentTransfer(bool Modified, bool OnlyRunningStatus); + void BeginSegmentTransfer(const cChannel *Channel); + void EndSegmentTransfer(bool Modified); }; extern cEpgHandlers EpgHandlers; diff --git a/epg2html b/epg2html index 34f95215..efc1e6f6 100755 --- a/epg2html +++ b/epg2html @@ -12,7 +12,7 @@ # See the main source file 'vdr.c' for copyright information and # how to reach the author. # -# $Id: epg2html 3.0 2013/03/04 13:02:20 kls Exp $ +# $Id: epg2html 4.0 2013/03/04 13:02:20 kls Exp $ @Index = (); ($Charset = $ENV{LANG}) =~ s/^[^.]*\.?(.*)/$1/; diff --git a/filter.c b/filter.c index 72590f79..aa91c1f1 100644 --- a/filter.c +++ b/filter.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: filter.c 3.0 2004/01/11 13:31:34 kls Exp $ + * $Id: filter.c 4.2 2015/07/25 10:59:57 kls Exp $ */ #include "filter.h" @@ -19,22 +19,38 @@ cSectionSyncer::cSectionSyncer(void) void cSectionSyncer::Reset(void) { - lastVersion = 0xFF; + currentVersion = -1; + currentSection = -1; synced = false; + complete = false; + memset(sections, 0x00, sizeof(sections)); +} + +void cSectionSyncer::Repeat(void) +{ + SetSectionFlag(currentSection, false); + synced = false; + complete = false; } bool cSectionSyncer::Sync(uchar Version, int Number, int LastNumber) { - if (Version == lastVersion) - return false; + if (Version != currentVersion) { + Reset(); + currentVersion = Version; + } if (!synced) { if (Number != 0) - return false; // sync on first section - synced = true; + return false; + else + synced = true; } + currentSection = Number; + bool Result = !GetSectionFlag(Number); + SetSectionFlag(Number, true); if (Number == LastNumber) - lastVersion = Version; - return synced; + complete = true; + return Result; } // --- cFilterData ----------------------------------------------------------- diff --git a/filter.h b/filter.h index a00ece5e..7cc3ec32 100644 --- a/filter.h +++ b/filter.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: filter.h 3.0 2004/01/11 13:31:59 kls Exp $ + * $Id: filter.h 4.2 2015/07/25 10:03:44 kls Exp $ */ #ifndef __FILTER_H @@ -15,11 +15,18 @@ class cSectionSyncer { private: - int lastVersion; + int currentVersion; + int currentSection; bool synced; + bool complete; + uchar sections[32]; // holds 32 * 8 = 256 bits, as flags for the sections + void SetSectionFlag(uchar Section, bool On) { if (On) sections[Section / 8] |= (1 << (Section % 8)); else sections[Section / 8] &= ~(1 << (Section % 8)); } + bool GetSectionFlag(uchar Section) { return sections[Section / 8] & (1 << (Section % 8)); } public: cSectionSyncer(void); void Reset(void); + void Repeat(void); + bool Complete(void) { return complete; } bool Sync(uchar Version, int Number, int LastNumber); }; diff --git a/font.c b/font.c index cd2c494b..29256e05 100644 --- a/font.c +++ b/font.c @@ -6,7 +6,7 @@ * * BiDi support by Osama Alrawab @2008 Tripoli-Libya. * - * $Id: font.c 3.2 2014/01/07 12:19:45 kls Exp $ + * $Id: font.c 4.1 2015/04/19 11:13:45 kls Exp $ */ #include "font.h" @@ -140,7 +140,7 @@ cFreetypeFont::cFreetypeFont(const char *Name, int CharHeight, int CharWidth) if (!error) { error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); if (!error) { - if (face->glyph->bitmap.rows-face->glyph->bitmap_top > bottom) + if (int(face->glyph->bitmap.rows-face->glyph->bitmap_top) > bottom) bottom = face->glyph->bitmap.rows-face->glyph->bitmap_top; } else diff --git a/font.h b/font.h index cd62b4a9..3932f602 100644 --- a/font.h +++ b/font.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: font.h 3.1 2014/01/07 12:11:55 kls Exp $ + * $Id: font.h 4.0 2014/01/07 12:11:55 kls Exp $ */ #ifndef __FONT_H diff --git a/i18n.c b/i18n.c index f26c96bf..d205ba4c 100644 --- a/i18n.c +++ b/i18n.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: i18n.c 3.0 2012/09/01 10:53:43 kls Exp $ + * $Id: i18n.c 4.0 2012/09/01 10:53:43 kls Exp $ */ /* diff --git a/i18n.h b/i18n.h index c3f1fc7d..f8ad9dea 100644 --- a/i18n.h +++ b/i18n.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: i18n.h 3.0 2012/03/11 14:07:45 kls Exp $ + * $Id: i18n.h 4.0 2012/03/11 14:07:45 kls Exp $ */ #ifndef __I18N_H diff --git a/interface.c b/interface.c index 6bf7ffce..dcb766ca 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 3.1 2015/01/11 13:37:47 kls Exp $ + * $Id: interface.c 4.1 2015/04/28 11:16:06 kls Exp $ */ #include "interface.h" @@ -19,27 +19,19 @@ cInterface *Interface = NULL; -cInterface::cInterface(int SVDRPport) +cInterface::cInterface(void) { interrupted = false; - SVDRP = NULL; - if (SVDRPport) - SVDRP = new cSVDRP(SVDRPport); } cInterface::~cInterface() { - delete SVDRP; } eKeys cInterface::GetKey(bool Wait) { if (!cRemote::HasKeys()) Skins.Flush(); - if (SVDRP) { - if (SVDRP->Process()) - Wait = false; - } if (!cRemote::IsLearning()) return cRemote::Get(Wait ? 1000 : 10); else diff --git a/interface.h b/interface.h index b9082bd5..4131cf3a 100644 --- a/interface.h +++ b/interface.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: interface.h 3.0 2004/05/01 11:11:13 kls Exp $ + * $Id: interface.h 4.1 2015/04/28 11:15:11 kls Exp $ */ #ifndef __INTERFACE_H @@ -13,17 +13,14 @@ #include "config.h" #include "remote.h" #include "skins.h" -#include "svdrp.h" class cInterface { private: bool interrupted; - cSVDRP *SVDRP; bool QueryKeys(cRemote *Remote, cSkinDisplayMenu *DisplayMenu); public: - cInterface(int SVDRPport = 0); + cInterface(void); ~cInterface(); - bool HasSVDRPConnection(void) { return SVDRP && SVDRP->HasConnection(); } void Interrupt(void) { interrupted = true; } eKeys GetKey(bool Wait = true); eKeys Wait(int Seconds = 0, bool KeepChar = false); diff --git a/keys.c b/keys.c index 284858ca..4bf15429 100644 --- a/keys.c +++ b/keys.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: keys.c 3.0 2012/12/04 12:52:52 kls Exp $ + * $Id: keys.c 4.0 2012/12/04 12:52:52 kls Exp $ */ #include "keys.h" diff --git a/keys.h b/keys.h index 5e861a51..59e6c676 100644 --- a/keys.h +++ b/keys.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: keys.h 3.1 2015/01/27 10:45:18 kls Exp $ + * $Id: keys.h 4.0 2015/01/27 10:45:18 kls Exp $ */ #ifndef __KEYS_H diff --git a/libsi/Makefile b/libsi/Makefile index 2221673c..4c7630c3 100644 --- a/libsi/Makefile +++ b/libsi/Makefile @@ -1,7 +1,7 @@ # # Makefile for a libsi # -# $Id: Makefile 3.1 2015/02/11 10:24:54 kls Exp $ +# $Id: Makefile 4.0 2015/02/11 10:24:54 kls Exp $ ### The archiver options: diff --git a/libsi/descriptor.c b/libsi/descriptor.c index f763d62b..5c7fd031 100644 --- a/libsi/descriptor.c +++ b/libsi/descriptor.c @@ -6,7 +6,7 @@ * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * - * $Id: descriptor.c 3.1 2013/10/30 10:16:18 kls Exp $ + * $Id: descriptor.c 4.0 2013/10/30 10:16:18 kls Exp $ * * ***************************************************************************/ diff --git a/libsi/descriptor.h b/libsi/descriptor.h index 30c9f6b1..22d13db8 100644 --- a/libsi/descriptor.h +++ b/libsi/descriptor.h @@ -6,7 +6,7 @@ * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * - * $Id: descriptor.h 3.2 2014/02/08 12:44:17 kls Exp $ + * $Id: descriptor.h 4.0 2014/02/08 12:44:17 kls Exp $ * * ***************************************************************************/ diff --git a/libsi/gendescr b/libsi/gendescr index b0628fb6..5b3cb6c7 100755 --- a/libsi/gendescr +++ b/libsi/gendescr @@ -1,6 +1,6 @@ #!/usr/bin/perl -# $Id: gendescr 3.0 2003/12/13 10:40:53 kls Exp $ +# $Id: gendescr 4.0 2003/12/13 10:40:53 kls Exp $ print "Name (ohne ...Descriptor):"; $name=; diff --git a/libsi/headers.h b/libsi/headers.h index ff5bb5da..ae13d137 100644 --- a/libsi/headers.h +++ b/libsi/headers.h @@ -10,7 +10,7 @@ * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * - * $Id: headers.h 3.1 2013/10/30 10:16:18 kls Exp $ + * $Id: headers.h 4.0 2013/10/30 10:16:18 kls Exp $ * * ***************************************************************************/ diff --git a/libsi/section.c b/libsi/section.c index d7a42927..93bcc061 100644 --- a/libsi/section.c +++ b/libsi/section.c @@ -6,7 +6,7 @@ * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * - * $Id: section.c 3.0 2006/04/14 10:53:44 kls Exp $ + * $Id: section.c 4.0 2006/04/14 10:53:44 kls Exp $ * * ***************************************************************************/ diff --git a/libsi/section.h b/libsi/section.h index 133fba3a..4f1833ba 100644 --- a/libsi/section.h +++ b/libsi/section.h @@ -6,7 +6,7 @@ * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * - * $Id: section.h 3.0 2012/02/26 13:58:26 kls Exp $ + * $Id: section.h 4.0 2012/02/26 13:58:26 kls Exp $ * * ***************************************************************************/ diff --git a/libsi/si.c b/libsi/si.c index e51770ab..4413d857 100644 --- a/libsi/si.c +++ b/libsi/si.c @@ -6,7 +6,7 @@ * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * - * $Id: si.c 3.3 2015/02/10 13:42:41 kls Exp $ + * $Id: si.c 4.0 2015/02/10 13:42:41 kls Exp $ * * ***************************************************************************/ diff --git a/libsi/si.h b/libsi/si.h index e70de685..96885b6f 100644 --- a/libsi/si.h +++ b/libsi/si.h @@ -6,7 +6,7 @@ * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * - * $Id: si.h 3.4 2015/02/10 13:54:28 kls Exp $ + * $Id: si.h 4.0 2015/02/10 13:54:28 kls Exp $ * * ***************************************************************************/ diff --git a/libsi/util.c b/libsi/util.c index 4da19178..8b996f92 100644 --- a/libsi/util.c +++ b/libsi/util.c @@ -6,7 +6,7 @@ * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * - * $Id: util.c 3.0 2006/02/18 11:17:50 kls Exp $ + * $Id: util.c 4.0 2006/02/18 11:17:50 kls Exp $ * * ***************************************************************************/ diff --git a/libsi/util.h b/libsi/util.h index 019c4a86..1125acd8 100644 --- a/libsi/util.h +++ b/libsi/util.h @@ -6,7 +6,7 @@ * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * - * $Id: util.h 3.0 2012/02/26 13:58:26 kls Exp $ + * $Id: util.h 4.0 2012/02/26 13:58:26 kls Exp $ * * ***************************************************************************/ diff --git a/lirc.c b/lirc.c index 2b0c07b1..526cd6d2 100644 --- a/lirc.c +++ b/lirc.c @@ -6,7 +6,7 @@ * * LIRC support added by Carsten Koch 2000-06-16. * - * $Id: lirc.c 3.2 2013/10/29 12:32:12 kls Exp $ + * $Id: lirc.c 4.0 2013/10/29 12:32:12 kls Exp $ */ #include "lirc.h" diff --git a/lirc.h b/lirc.h index 070aa3dd..8d5f71cd 100644 --- a/lirc.h +++ b/lirc.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: lirc.h 3.0 2006/01/27 16:00:19 kls Exp $ + * $Id: lirc.h 4.0 2006/01/27 16:00:19 kls Exp $ */ #ifndef __LIRC_H diff --git a/menu.c b/menu.c index ae61c64d..569900ce 100644 --- a/menu.c +++ b/menu.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.c 3.48 2015/02/10 12:37:06 kls Exp $ + * $Id: menu.c 4.12 2015/09/14 13:22:49 kls Exp $ */ #include "menu.h" @@ -27,6 +27,7 @@ #include "sourceparams.h" #include "sources.h" #include "status.h" +#include "svdrp.h" #include "themes.h" #include "timers.h" #include "transfer.h" @@ -48,8 +49,8 @@ #define NODISKSPACEDELTA 300 // seconds between "Not enough disk space to start recording!" messages #define MAXCHNAMWIDTH 16 // maximum number of characters of channels' short names shown in schedules menus -#define CHNUMWIDTH (numdigits(Channels.MaxNumber()) + 1) -#define CHNAMWIDTH (min(MAXCHNAMWIDTH, Channels.MaxShortChannelNameLength() + 1)) +#define CHNUMWIDTH (numdigits(cChannels::MaxNumber()) + 1) +#define CHNAMWIDTH (min(MAXCHNAMWIDTH, cChannels::MaxShortChannelNameLength() + 1)) // --- cMenuEditCaItem ------------------------------------------------------- @@ -159,20 +160,23 @@ eOSState cMenuEditSrcItem::ProcessKey(eKeys Key) class cMenuEditChannel : public cOsdMenu { private: + cStateKey *channelsStateKey; cChannel *channel; cChannel data; cSourceParam *sourceParam; char name[256]; void Setup(void); public: - cMenuEditChannel(cChannel *Channel, bool New = false); + cMenuEditChannel(cStateKey *ChannelsStateKey, cChannel *Channel, bool New = false); + cChannel *Channel(void) { return channel; } virtual eOSState ProcessKey(eKeys Key); }; -cMenuEditChannel::cMenuEditChannel(cChannel *Channel, bool New) +cMenuEditChannel::cMenuEditChannel(cStateKey *ChannelsStateKey, cChannel *Channel, bool New) :cOsdMenu(tr("Edit channel"), 16) { SetMenuCategory(mcChannelEdit); + channelsStateKey = ChannelsStateKey; channel = Channel; sourceParam = NULL; *name = 0; @@ -239,32 +243,37 @@ eOSState cMenuEditChannel::ProcessKey(eKeys Key) if (state == osUnknown) { if (Key == kOk) { + cChannels *Channels =cChannels::GetChannelsWrite(*channelsStateKey); + bool Modified = false; if (sourceParam) sourceParam->GetData(&data); - if (Channels.HasUniqueChannelID(&data, channel)) { + if (Channels->HasUniqueChannelID(&data, channel)) { data.name = strcpyrealloc(data.name, name); if (channel) { *channel = data; - isyslog("edited channel %d %s", channel->Number(), *data.ToText()); + isyslog("edited channel %d %s", channel->Number(), *channel->ToText()); state = osBack; } else { channel = new cChannel; *channel = data; - Channels.Add(channel); - Channels.ReNumber(); - isyslog("added channel %d %s", channel->Number(), *data.ToText()); + Channels->Add(channel); + Channels->ReNumber(); + isyslog("added channel %d %s", channel->Number(), *channel->ToText()); state = osUser1; } - Channels.SetModified(true); + Channels->SetModifiedByUser(); + Modified = true; } else { Skins.Message(mtError, tr("Channel settings are not unique!")); state = osContinue; } + channelsStateKey->Remove(Modified); } } if (Key != kNone && (data.source & cSource::st_Mask) != (oldSource & cSource::st_Mask)) { + LOCK_CHANNELS_WRITE; if (sourceParam) sourceParam->GetData(&data); Setup(); @@ -279,21 +288,21 @@ public: enum eChannelSortMode { csmNumber, csmName, csmProvider }; private: static eChannelSortMode sortMode; - cChannel *channel; + const cChannel *channel; public: - cMenuChannelItem(cChannel *Channel); + cMenuChannelItem(const cChannel *Channel); static void SetSortMode(eChannelSortMode SortMode) { sortMode = SortMode; } static void IncSortMode(void) { sortMode = eChannelSortMode((sortMode == csmProvider) ? csmNumber : sortMode + 1); } static eChannelSortMode SortMode(void) { return sortMode; } virtual int Compare(const cListObject &ListObject) const; virtual void Set(void); - cChannel *Channel(void) { return channel; } + const cChannel *Channel(void) { return channel; } virtual void SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable); }; cMenuChannelItem::eChannelSortMode cMenuChannelItem::sortMode = csmNumber; -cMenuChannelItem::cMenuChannelItem(cChannel *Channel) +cMenuChannelItem::cMenuChannelItem(const cChannel *Channel) { channel = Channel; if (channel->GroupSep()) @@ -340,11 +349,12 @@ void cMenuChannelItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, boo class cMenuChannels : public cOsdMenu { private: + cStateKey channelsStateKey; int number; cTimeMs numberTimer; - void Setup(void); + void Set(bool Force = false); cChannel *GetChannel(int Index); - void Propagate(void); + void Propagate(cChannels *Channels); protected: eOSState Number(eKeys Key); eOSState Switch(void); @@ -363,38 +373,41 @@ cMenuChannels::cMenuChannels(void) { SetMenuCategory(mcChannel); number = 0; - Setup(); - Channels.IncBeingEdited(); + Set(); } cMenuChannels::~cMenuChannels() { - Channels.DecBeingEdited(); } -void cMenuChannels::Setup(void) +void cMenuChannels::Set(bool Force) { - cChannel *currentChannel = GetChannel(Current()); - if (!currentChannel) - currentChannel = Channels.GetByNumber(cDevice::CurrentChannel()); - cMenuChannelItem *currentItem = NULL; - Clear(); - for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) { - if (!channel->GroupSep() || cMenuChannelItem::SortMode() == cMenuChannelItem::csmNumber && *channel->Name()) { - cMenuChannelItem *item = new cMenuChannelItem(channel); - Add(item); - if (channel == currentChannel) - currentItem = item; + if (Force) + channelsStateKey.Reset(); + if (const cChannels *Channels = cChannels::GetChannelsRead(channelsStateKey)) { + const cChannel *CurrentChannel = GetChannel(Current()); + if (!CurrentChannel) + CurrentChannel = Channels->GetByNumber(cDevice::CurrentChannel()); + cMenuChannelItem *CurrentItem = NULL; + Clear(); + for (const cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) { + if (!Channel->GroupSep() || cMenuChannelItem::SortMode() == cMenuChannelItem::csmNumber && *Channel->Name()) { + cMenuChannelItem *Item = new cMenuChannelItem(Channel); + Add(Item); + if (Channel == CurrentChannel) + CurrentItem = Item; + } } - } - SetMenuSortMode(cMenuChannelItem::SortMode() == cMenuChannelItem::csmName ? msmName : - cMenuChannelItem::SortMode() == cMenuChannelItem::csmProvider ? msmProvider : - msmNumber); - if (cMenuChannelItem::SortMode() != cMenuChannelItem::csmNumber) - Sort(); - SetCurrent(currentItem); - SetHelp(tr("Button$Edit"), tr("Button$New"), tr("Button$Delete"), tr("Button$Mark")); - Display(); + SetMenuSortMode(cMenuChannelItem::SortMode() == cMenuChannelItem::csmName ? msmName : + cMenuChannelItem::SortMode() == cMenuChannelItem::csmProvider ? msmProvider : + msmNumber); + if (cMenuChannelItem::SortMode() != cMenuChannelItem::csmNumber) + Sort(); + SetCurrent(CurrentItem); + SetHelp(tr("Button$Edit"), tr("Button$New"), tr("Button$Delete"), tr("Button$Mark")); + Display(); + channelsStateKey.Remove(); + } } cChannel *cMenuChannels::GetChannel(int Index) @@ -403,13 +416,13 @@ cChannel *cMenuChannels::GetChannel(int Index) return p ? (cChannel *)p->Channel() : NULL; } -void cMenuChannels::Propagate(void) +void cMenuChannels::Propagate(cChannels *Channels) { - Channels.ReNumber(); + Channels->ReNumber(); for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next()) ci->Set(); Display(); - Channels.SetModified(true); + Channels->SetModifiedByUser(); } eOSState cMenuChannels::Number(eKeys Key) @@ -420,9 +433,10 @@ eOSState cMenuChannels::Number(eKeys Key) number = 0; if (!number && Key == k0) { cMenuChannelItem::IncSortMode(); - Setup(); + Set(true); } else { + LOCK_CHANNELS_READ; number = number * 10 + Key - k0; for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next()) { if (!ci->Channel()->GroupSep() && ci->Channel()->Number() == number) { @@ -440,6 +454,7 @@ eOSState cMenuChannels::Switch(void) { if (HasSubMenu()) return osContinue; + LOCK_CHANNELS_READ; cChannel *ch = GetChannel(Current()); if (ch) return cDevice::PrimaryDevice()->SwitchChannel(ch, true) ? osEnd : osContinue; @@ -450,9 +465,10 @@ eOSState cMenuChannels::Edit(void) { if (HasSubMenu() || Count() == 0) return osContinue; + LOCK_CHANNELS_READ; cChannel *ch = GetChannel(Current()); if (ch) - return AddSubMenu(new cMenuEditChannel(ch)); + return AddSubMenu(new cMenuEditChannel(&channelsStateKey, ch)); return osContinue; } @@ -460,79 +476,97 @@ eOSState cMenuChannels::New(void) { if (HasSubMenu()) return osContinue; - return AddSubMenu(new cMenuEditChannel(GetChannel(Current()), true)); + LOCK_CHANNELS_READ; + return AddSubMenu(new cMenuEditChannel(&channelsStateKey, GetChannel(Current()), true)); } eOSState cMenuChannels::Delete(void) { if (!HasSubMenu() && Count() > 0) { - int CurrentChannelNr = cDevice::CurrentChannel(); - cChannel *CurrentChannel = Channels.GetByNumber(CurrentChannelNr); + LOCK_TIMERS_READ; // must lock timers before channels! + cChannels *Channels = cChannels::GetChannelsWrite(channelsStateKey); int Index = Current(); - cChannel *channel = GetChannel(Current()); - int DeletedChannel = channel->Number(); + cChannel *Channel = GetChannel(Current()); + if (!Channels->Contains(Channel)) { + channelsStateKey.Remove(false); + channelsStateKey.Reset(); // makes sure the menu is refreshed + return osContinue; + } + bool Deleted = false; + int CurrentChannelNr = cDevice::CurrentChannel(); + cChannel *CurrentChannel = Channels->GetByNumber(CurrentChannelNr); + int DeletedChannel = Channel->Number(); // Check if there is a timer using this channel: - if (channel->HasTimer()) { + if (Timers->UsesChannel(Channel)) { Skins.Message(mtError, tr("Channel is being used by a timer!")); return osContinue; } if (Interface->Confirm(tr("Delete channel?"))) { - if (CurrentChannel && channel == CurrentChannel) { - int n = Channels.GetNextNormal(CurrentChannel->Index()); + if (CurrentChannel && Channel == CurrentChannel) { + int n = Channels->GetNextNormal(CurrentChannel->Index()); if (n < 0) - n = Channels.GetPrevNormal(CurrentChannel->Index()); - CurrentChannel = Channels.Get(n); + n = Channels->GetPrevNormal(CurrentChannel->Index()); + CurrentChannel = Channels->Get(n); CurrentChannelNr = 0; // triggers channel switch below } - Channels.Del(channel); + Channels->Del(Channel); cOsdMenu::Del(Index); - Propagate(); - Channels.SetModified(true); + Propagate(Channels); + Channels->SetModifiedByUser(); isyslog("channel %d deleted", DeletedChannel); + Deleted = true; if (CurrentChannel && CurrentChannel->Number() != CurrentChannelNr) { if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring()) - Channels.SwitchTo(CurrentChannel->Number()); + Channels->SwitchTo(CurrentChannel->Number()); else cDevice::SetCurrentChannel(CurrentChannel); } } + channelsStateKey.Remove(Deleted); } return osContinue; } void cMenuChannels::Move(int From, int To) { - int CurrentChannelNr = cDevice::CurrentChannel(); - cChannel *CurrentChannel = Channels.GetByNumber(CurrentChannelNr); - cChannel *FromChannel = GetChannel(From); - cChannel *ToChannel = GetChannel(To); - if (FromChannel && ToChannel) { - int FromNumber = FromChannel->Number(); - int ToNumber = ToChannel->Number(); - Channels.Move(FromChannel, ToChannel); - cOsdMenu::Move(From, To); - Propagate(); - Channels.SetModified(true); - isyslog("channel %d moved to %d", FromNumber, ToNumber); - if (CurrentChannel && CurrentChannel->Number() != CurrentChannelNr) { - if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring()) - Channels.SwitchTo(CurrentChannel->Number()); - else - cDevice::SetCurrentChannel(CurrentChannel); + if (cChannels *Channels = cChannels::GetChannelsWrite(channelsStateKey)) { + int CurrentChannelNr = cDevice::CurrentChannel(); + cChannel *CurrentChannel = Channels->GetByNumber(CurrentChannelNr); + cChannel *FromChannel = GetChannel(From); + cChannel *ToChannel = GetChannel(To); + if (FromChannel && ToChannel) { + int FromNumber = FromChannel->Number(); + int ToNumber = ToChannel->Number(); + Channels->Move(FromChannel, ToChannel); + cOsdMenu::Move(From, To); + Propagate(Channels); + Channels->SetModifiedByUser(); + isyslog("channel %d moved to %d", FromNumber, ToNumber); + if (CurrentChannel && CurrentChannel->Number() != CurrentChannelNr) { + if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring()) + Channels->SwitchTo(CurrentChannel->Number()); + else + cDevice::SetCurrentChannel(CurrentChannel); + } } + channelsStateKey.Remove(); } } eOSState cMenuChannels::ProcessKey(eKeys Key) { + if (!HasSubMenu()) + Set(); // react on any changes to the channels list eOSState state = cOsdMenu::ProcessKey(Key); switch (state) { case osUser1: { - cChannel *channel = Channels.Last(); - if (channel) { - Add(new cMenuChannelItem(channel), true); - return CloseSubMenu(); + if (cMenuEditChannel *MenuEditChannel = dynamic_cast(SubMenu())) { + if (cChannel *Channel = MenuEditChannel->Channel()) { + LOCK_CHANNELS_READ; + Add(new cMenuChannelItem(Channel), true); + return CloseSubMenu(); + } } } break; @@ -767,7 +801,7 @@ void cMenuFolder::SetHelpKeys(void) } #define FOLDERDELIMCHARSUBST 0x01 -static void AddRecordingFolders(cList *List, char *Path) +static void AddRecordingFolders(const cRecordings *Recordings, cList *List, char *Path) { if (Path) { char *p = strchr(Path, FOLDERDELIMCHARSUBST); @@ -782,13 +816,12 @@ static void AddRecordingFolders(cList *List, char *Path) List->Add(Folder = new cNestedItem(Path)); if (p) { Folder->SetSubItems(true); - AddRecordingFolders(Folder->SubItems(), p); + AddRecordingFolders(Recordings, Folder->SubItems(), p); } } else { - cThreadLock RecordingsLock(&Recordings); cStringList Dirs; - for (cRecording *Recording = Recordings.First(); Recording; Recording = Recordings.Next(Recording)) { + for (const cRecording *Recording = Recordings->First(); Recording; Recording = Recordings->Next(Recording)) { cString Folder = Recording->Folder(); strreplace((char *)*Folder, FOLDERDELIMCHAR, FOLDERDELIMCHARSUBST); // makes sure parent folders come before subfolders if (Dirs.Find(Folder) < 0) @@ -796,18 +829,21 @@ static void AddRecordingFolders(cList *List, char *Path) } Dirs.Sort(); for (int i = 0; i < Dirs.Size(); i++) { - char *s = Dirs[i]; - if (*s) - AddRecordingFolders(&Folders, s); + if (char *s = Dirs[i]) + AddRecordingFolders(Recordings, &Folders, s); } } } void cMenuFolder::Set(const char *CurrentFolder) { - static int RecordingsState = -1; - if (list == &Folders && Recordings.StateChanged(RecordingsState)) - AddRecordingFolders(&Folders, NULL); + static cStateKey RecordingsStateKey; + if (list == &Folders) { + if (const cRecordings *Recordings = cRecordings::GetRecordingsRead(RecordingsStateKey)) { + AddRecordingFolders(Recordings, &Folders, NULL); + RecordingsStateKey.Remove(); + } + } firstFolder = NULL; Clear(); if (!isempty(dir)) { @@ -937,10 +973,13 @@ eOSState cMenuFolder::ProcessKey(eKeys Key) // --- cMenuEditTimer -------------------------------------------------------- +const cTimer *cMenuEditTimer::addedTimer = NULL; + cMenuEditTimer::cMenuEditTimer(cTimer *Timer, bool New) :cOsdMenu(tr("Edit timer"), 12) { SetMenuCategory(mcTimerEdit); + addedTimer = NULL; file = NULL; day = firstday = NULL; timer = Timer; @@ -960,16 +999,30 @@ cMenuEditTimer::cMenuEditTimer(cTimer *Timer, bool New) Add(new cMenuEditIntItem( tr("Lifetime"), &data.lifetime, 0, MAXLIFETIME)); Add(file = new cMenuEditStrItem( tr("File"), data.file, sizeof(data.file))); SetFirstDayItem(); + if (data.remote) + strn0cpy(remote, data.remote, sizeof(remote)); + else + *remote = 0; + if (GetSVDRPServerNames(&svdrpServerNames)) { + svdrpServerNames.Sort(true); + svdrpServerNames.Insert(strdup("")); + Add(new cMenuEditStrlItem(tr("Record on"), remote, sizeof(remote), &svdrpServerNames)); + } } SetHelpKeys(); - Timers.IncBeingEdited(); } cMenuEditTimer::~cMenuEditTimer() { if (timer && addIfConfirmed) delete timer; // apparently it wasn't confirmed - Timers.DecBeingEdited(); +} + +const cTimer *cMenuEditTimer::AddedTimer(void) +{ + const cTimer *Timer = addedTimer; + addedTimer = NULL; + return Timer; } void cMenuEditTimer::SetHelpKeys(void) @@ -1009,34 +1062,107 @@ eOSState cMenuEditTimer::SetFolder(void) return CloseSubMenu(); } +static bool RemoteTimerError(const cTimer *Timer) +{ + Skins.Message(mtError, cString::sprintf("%s %d@%s!", tr("Error while accessing remote timer"), Timer->Id(), Timer->Remote())); + return false; // convenience return code +} + +static bool HandleRemoteModifications(cTimer *NewTimer, cTimer *OldTimer = NULL) +{ + cStringList Response; + if (!OldTimer || OldTimer->Local() || !OldTimer->Id()) { + if (NewTimer->Local()) { // timer stays local, nothing to do + if (OldTimer && OldTimer->Id()) + isyslog("modified timer %s", *NewTimer->ToDescr()); + else + isyslog("added timer %s", *NewTimer->ToDescr()); + } + else { // timer is new, or moved from local to remote + if (!ExecSVDRPCommand(NewTimer->Remote(), cString::sprintf("NEWT %s", *NewTimer->ToText(true)), &Response) || SVDRPCode(Response[0]) != 250) + return RemoteTimerError(NewTimer); + int RemoteId = atoi(SVDRPValue(Response[0])); + if (RemoteId <= 0) + return RemoteTimerError(NewTimer); + NewTimer->SetId(RemoteId); + if (OldTimer && OldTimer->Id()) { + if (OldTimer->Recording()) + cRecordControls::Stop(OldTimer); + isyslog("moved timer %d to %s", OldTimer->Id(), *NewTimer->ToDescr()); + } + else + isyslog("added timer %s", *NewTimer->ToDescr()); + } + } + else if (NewTimer->Local()) { // timer is moved from remote to local + if (!ExecSVDRPCommand(OldTimer->Remote(), cString::sprintf("DELT %d", OldTimer->Id()), &Response) || SVDRPCode(Response[0]) != 250) + return RemoteTimerError(OldTimer); + NewTimer->SetId(cTimers::NewTimerId()); + NewTimer->ClrFlags(tfRecording); // in case it was recording on the remote machine + isyslog("moved timer %d@%s to %s", OldTimer->Id(), OldTimer->Remote(), *NewTimer->ToDescr()); + } + else if (strcmp(OldTimer->Remote(), NewTimer->Remote()) == 0) { // timer stays remote on same machine + if (!ExecSVDRPCommand(OldTimer->Remote(), cString::sprintf("MODT %d %s", OldTimer->Id(), *NewTimer->ToText(true)), &Response) || SVDRPCode(Response[0]) != 250) + return RemoteTimerError(NewTimer); + isyslog("modified timer %s", *NewTimer->ToDescr()); + } + else { // timer is moved from one remote machine to an other + if (!ExecSVDRPCommand(NewTimer->Remote(), cString::sprintf("NEWT %s", *NewTimer->ToText(true)), &Response) || SVDRPCode(Response[0]) != 250) + return RemoteTimerError(NewTimer); + int RemoteId = atoi(SVDRPValue(Response[0])); + if (RemoteId <= 0) + return RemoteTimerError(NewTimer); + NewTimer->SetId(RemoteId); + if (!ExecSVDRPCommand(OldTimer->Remote(), cString::sprintf("DELT %d", OldTimer->Id()), &Response) || SVDRPCode(Response[0]) != 250) + return RemoteTimerError(OldTimer); + isyslog("moved timer %d@%s to %s", OldTimer->Id(), OldTimer->Remote(), *NewTimer->ToDescr()); + } + return true; +} + eOSState cMenuEditTimer::ProcessKey(eKeys Key) { eOSState state = cOsdMenu::ProcessKey(Key); if (state == osUnknown) { switch (Key) { - case kOk: { - cChannel *ch = Channels.GetByNumber(channel); - if (ch) - data.channel = ch; - else { - Skins.Message(mtError, tr("*** Invalid Channel ***")); - break; - } - if (!*data.file) - strcpy(data.file, data.Channel()->ShortName(true)); - if (timer) { - if (memcmp((void *)timer, &data, sizeof(data)) != 0) - *timer = data; - if (addIfConfirmed) - Timers.Add(timer); - timer->SetEventFromSchedule(); - timer->Matches(); - Timers.SetModified(); - isyslog("timer %s %s (%s)", *timer->ToDescr(), addIfConfirmed ? "added" : "modified", timer->HasFlags(tfActive) ? "active" : "inactive"); - addIfConfirmed = false; - } - } + case kOk: if (timer) { + LOCK_TIMERS_WRITE; + if (!addIfConfirmed && !Timers->Contains(timer)) { + Skins.Message(mtWarning, tr("Timer has been deleted!")); + break; + } + LOCK_CHANNELS_READ; + if (const cChannel *Channel = Channels->GetByNumber(channel)) + data.channel = Channel; + else { + Skins.Message(mtError, tr("*** Invalid Channel ***")); + break; + } + if (!*data.file) + strcpy(data.file, data.Channel()->ShortName(true)); + data.SetRemote(*remote ? remote : NULL); + if (addIfConfirmed) { + *timer = data; + Timers->Add(timer); + addedTimer = timer; + if (!HandleRemoteModifications(timer)) { + // must add the timer before HandleRemoteModifications to get proper log messages with timer ids + Timers->Del(timer); + addedTimer = NULL; + return osContinue; + } + } + else { + if (!HandleRemoteModifications(&data, timer)) + return osContinue; + *timer = data; + } + LOCK_SCHEDULES_READ; + timer->SetEventFromSchedule(Schedules); + timer->Matches(); + addIfConfirmed = false; + } return osBack; case kRed: return AddSubMenu(new cMenuFolder(tr("Select folder"), &Folders, data.file)); case kGreen: if (day) { @@ -1063,16 +1189,16 @@ eOSState cMenuEditTimer::ProcessKey(eKeys Key) class cMenuTimerItem : public cOsdItem { private: - cTimer *timer; + const cTimer *timer; public: - cMenuTimerItem(cTimer *Timer); + cMenuTimerItem(const cTimer *Timer); virtual int Compare(const cListObject &ListObject) const; virtual void Set(void); - cTimer *Timer(void) { return timer; } + const cTimer *Timer(void) { return timer; } virtual void SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable); }; -cMenuTimerItem::cMenuTimerItem(cTimer *Timer) +cMenuTimerItem::cMenuTimerItem(const cTimer *Timer) { timer = Timer; Set(); @@ -1105,7 +1231,7 @@ void cMenuTimerItem::Set(void) File++; else File = timer->File(); - SetText(cString::sprintf("%c\t%d\t%s%s%s\t%02d:%02d\t%02d:%02d\t%s", + SetText(cString::sprintf("%c\t%d\t%s%s%s\t%02d:%02d\t%02d:%02d\t%s%s", !(timer->HasFlags(tfActive)) ? ' ' : timer->FirstDay() ? '!' : timer->Recording() ? '#' : '>', timer->Channel()->Number(), *name, @@ -1115,6 +1241,7 @@ void cMenuTimerItem::Set(void) timer->Start() % 100, timer->Stop() / 100, timer->Stop() % 100, + timer->Remote() ? *cString::sprintf("@%s: ", timer->Remote()) : "", File)); } @@ -1128,13 +1255,15 @@ void cMenuTimerItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool class cMenuTimers : public cOsdMenu { private: + cStateKey timersStateKey; int helpKeys; + void Set(void); eOSState Edit(void); eOSState New(void); eOSState Delete(void); eOSState OnOff(void); eOSState Info(void); - cTimer *CurrentTimer(void); + cTimer *GetTimer(void); void SetHelpKeys(void); public: cMenuTimers(void); @@ -1147,33 +1276,45 @@ cMenuTimers::cMenuTimers(void) { SetMenuCategory(mcTimer); helpKeys = -1; - for (cTimer *timer = Timers.First(); timer; timer = Timers.Next(timer)) { - timer->SetEventFromSchedule(); // make sure the event is current - Add(new cMenuTimerItem(timer)); - } - Sort(); - SetCurrent(First()); - SetHelpKeys(); - Timers.IncBeingEdited(); + cMenuEditTimer::AddedTimer(); // to clear any leftovers + Set(); } cMenuTimers::~cMenuTimers() { - Timers.DecBeingEdited(); } -cTimer *cMenuTimers::CurrentTimer(void) +void cMenuTimers::Set(void) +{ + if (const cTimers *Timers = cTimers::GetTimersRead(timersStateKey)) { + const cTimer *CurrentTimer = GetTimer(); + cMenuTimerItem *CurrentItem = NULL; + Clear(); + for (const cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) { + cMenuTimerItem *Item = new cMenuTimerItem(Timer); + Add(Item); + if (CurrentTimer && Timer->Id() == CurrentTimer->Id() && (!Timer->Remote() && !CurrentTimer->Remote() || Timer->Remote() && CurrentTimer->Remote() && strcmp(Timer->Remote(), CurrentTimer->Remote()) == 0)) + CurrentItem = Item; + } + Sort(); + SetCurrent(CurrentItem ? CurrentItem : First()); + SetHelpKeys(); + Display(); + timersStateKey.Remove(); + } +} + +cTimer *cMenuTimers::GetTimer(void) { cMenuTimerItem *item = (cMenuTimerItem *)Get(Current()); - return item ? item->Timer() : NULL; + return item ? (cTimer *)item->Timer() : NULL; } void cMenuTimers::SetHelpKeys(void) { int NewHelpKeys = 0; - cTimer *timer = CurrentTimer(); - if (timer) { - if (timer->Event()) + if (const cTimer *Timer = GetTimer()) { + if (Timer->Event()) NewHelpKeys = 2; else NewHelpKeys = 1; @@ -1188,18 +1329,25 @@ eOSState cMenuTimers::OnOff(void) { if (HasSubMenu()) return osContinue; - cTimer *timer = CurrentTimer(); - if (timer) { - timer->OnOff(); - timer->SetEventFromSchedule(); + cTimers::GetTimersWrite(timersStateKey); + cTimer *Timer = GetTimer(); + if (Timer) { + Timer->OnOff(); + if (Timer->Remote()) { + cStringList Response; + if (!ExecSVDRPCommand(Timer->Remote(), cString::sprintf("MODT %d %s", Timer->Id(), *Timer->ToText(true)), &Response) || SVDRPCode(Response[0]) != 250) + RemoteTimerError(Timer); + } + LOCK_SCHEDULES_READ; + Timer->SetEventFromSchedule(Schedules); RefreshCurrent(); DisplayCurrent(true); - if (timer->FirstDay()) - isyslog("timer %s first day set to %s", *timer->ToDescr(), *timer->PrintFirstDay()); + if (Timer->FirstDay()) + isyslog("set first day of timer %s to %s", *Timer->ToDescr(), *Timer->PrintFirstDay()); else - isyslog("timer %s %sactivated", *timer->ToDescr(), timer->HasFlags(tfActive) ? "" : "de"); - Timers.SetModified(); + isyslog("%sactivated timer %s", Timer->HasFlags(tfActive) ? "" : "de", *Timer->ToDescr()); } + timersStateKey.Remove(Timer != NULL); return osContinue; } @@ -1207,38 +1355,50 @@ eOSState cMenuTimers::Edit(void) { if (HasSubMenu() || Count() == 0) return osContinue; - isyslog("editing timer %s", *CurrentTimer()->ToDescr()); - return AddSubMenu(new cMenuEditTimer(CurrentTimer())); + return AddSubMenu(new cMenuEditTimer(GetTimer())); } eOSState cMenuTimers::New(void) { if (HasSubMenu()) return osContinue; - return AddSubMenu(new cMenuEditTimer(new cTimer, true)); + cTimer *Timer = new cTimer; + if (*Setup.SVDRPDefaultHost) + Timer->SetRemote(Setup.SVDRPDefaultHost); + return AddSubMenu(new cMenuEditTimer(Timer, true)); } eOSState cMenuTimers::Delete(void) { + cTimers *Timers = cTimers::GetTimersWrite(timersStateKey); // Check if this timer is active: - cTimer *ti = CurrentTimer(); - if (ti) { + cTimer *Timer = GetTimer(); + if (Timer) { if (Interface->Confirm(tr("Delete timer?"))) { - if (ti->Recording()) { + if (Timer->Recording()) { if (Interface->Confirm(tr("Timer still recording - really delete?"))) { - ti->Skip(); - cRecordControls::Process(time(NULL)); + if (!Timer->Remote()) { + Timer->Skip(); + cRecordControls::Process(Timers, time(NULL)); + } } else - return osContinue; + Timer = NULL; + } + if (Timer) { + if (Timer->Remote()) { + cStringList Response; + if (!ExecSVDRPCommand(Timer->Remote(), cString::sprintf("DELT %d", Timer->Id()), &Response) || SVDRPCode(Response[0]) != 250) + RemoteTimerError(Timer); + } + Timers->Del(Timer); + cOsdMenu::Del(Current()); + Display(); + isyslog("deleted timer %s", *Timer->ToDescr()); } - isyslog("deleting timer %s", *ti->ToDescr()); - Timers.Del(ti); - cOsdMenu::Del(Current()); - Timers.SetModified(); - Display(); } } + timersStateKey.Remove(Timer != NULL); return osContinue; } @@ -1246,17 +1406,19 @@ eOSState cMenuTimers::Info(void) { if (HasSubMenu() || Count() == 0) return osContinue; - cTimer *ti = CurrentTimer(); - if (ti && ti->Event()) - return AddSubMenu(new cMenuEvent(ti->Event())); + LOCK_TIMERS_READ; + LOCK_CHANNELS_READ; + cTimer *Timer = GetTimer(); + if (Timer && Timer->Event()) + return AddSubMenu(new cMenuEvent(Timers, Channels, Timer->Event())); return osContinue; } eOSState cMenuTimers::ProcessKey(eKeys Key) { - int TimerNumber = HasSubMenu() ? Count() : -1; + if (!HasSubMenu()) + Set(); eOSState state = cOsdMenu::ProcessKey(Key); - if (state == osUnknown) { switch (Key) { case kOk: return Edit(); @@ -1269,9 +1431,10 @@ eOSState cMenuTimers::ProcessKey(eKeys Key) default: break; } } - if (TimerNumber >= 0 && !HasSubMenu() && Timers.Get(TimerNumber)) { - // a newly created timer was confirmed with Ok - Add(new cMenuTimerItem(Timers.Get(TimerNumber)), true); + if (const cTimer *Timer = cMenuEditTimer::AddedTimer()) { + // a newly created timer was confirmed with Ok and the proper item needs to be added: + LOCK_TIMERS_READ; + Add(new cMenuTimerItem(Timer), true); Display(); } if (Key != kNone) @@ -1281,19 +1444,19 @@ eOSState cMenuTimers::ProcessKey(eKeys Key) // --- cMenuEvent ------------------------------------------------------------ -cMenuEvent::cMenuEvent(const cEvent *Event, bool CanSwitch, bool Buttons) +cMenuEvent::cMenuEvent(const cTimers *Timers, const cChannels *Channels, const cEvent *Event, bool CanSwitch, bool Buttons) :cOsdMenu(tr("Event")) { SetMenuCategory(mcEvent); event = Event; if (event) { - cChannel *channel = Channels.GetByChannelID(event->ChannelID(), true); - if (channel) { - SetTitle(channel->Name()); - eTimerMatch TimerMatch = tmNone; - Timers.GetMatch(event, &TimerMatch); - if (Buttons) + if (const cChannel *Channel = Channels->GetByChannelID(event->ChannelID(), true)) { + SetTitle(Channel->Name()); + if (Buttons) { + eTimerMatch TimerMatch = tmNone; + Timers->GetMatch(event, &TimerMatch); SetHelp(TimerMatch == tmFull ? tr("Button$Timer") : tr("Button$Record"), NULL, NULL, CanSwitch ? tr("Button$Switch") : NULL); + } } } } @@ -1349,24 +1512,24 @@ public: const cChannel *channel; bool withDate; eTimerMatch timerMatch; - cMenuScheduleItem(const cEvent *Event, cChannel *Channel = NULL, bool WithDate = false); + cMenuScheduleItem(const cTimers *Timers, const cEvent *Event, const cChannel *Channel = NULL, bool WithDate = false); static void SetSortMode(eScheduleSortMode SortMode) { sortMode = SortMode; } static void IncSortMode(void) { sortMode = eScheduleSortMode((sortMode == ssmAllAll) ? ssmAllThis : sortMode + 1); } static eScheduleSortMode SortMode(void) { return sortMode; } virtual int Compare(const cListObject &ListObject) const; - bool Update(bool Force = false); + bool Update(const cTimers *Timers, bool Force = false); virtual void SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable); }; cMenuScheduleItem::eScheduleSortMode cMenuScheduleItem::sortMode = ssmAllThis; -cMenuScheduleItem::cMenuScheduleItem(const cEvent *Event, cChannel *Channel, bool WithDate) +cMenuScheduleItem::cMenuScheduleItem(const cTimers *Timers, const cEvent *Event, const cChannel *Channel, bool WithDate) { event = Event; channel = Channel; withDate = WithDate; timerMatch = tmNone; - Update(true); + Update(Timers, true); } int cMenuScheduleItem::Compare(const cListObject &ListObject) const @@ -1382,11 +1545,10 @@ int cMenuScheduleItem::Compare(const cListObject &ListObject) const static const char *TimerMatchChars = " tT"; -bool cMenuScheduleItem::Update(bool Force) +bool cMenuScheduleItem::Update(const cTimers *Timers, bool Force) { - bool result = false; eTimerMatch OldTimerMatch = timerMatch; - Timers.GetMatch(event, &timerMatch); + Timers->GetMatch(event, &timerMatch); if (Force || timerMatch != OldTimerMatch) { cString buffer; char t = TimerMatchChars[timerMatch]; @@ -1401,9 +1563,9 @@ bool cMenuScheduleItem::Update(bool Force) else buffer = cString::sprintf("%.*s\t%s\t%c%c%c\t%s", Utf8SymChars(eds, 6), *eds, *event->GetTimeString(), t, v, r, event->Title()); SetText(buffer); - result = true; + return true; } - return result; + return false; } void cMenuScheduleItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable) @@ -1419,7 +1581,7 @@ private: bool now; bool canSwitch; int helpKeys; - int timerState; + cStateKey timersStateKey; eOSState Record(void); eOSState Switch(void); static int currentChannel; @@ -1427,7 +1589,7 @@ private: bool Update(void); void SetHelpKeys(void); public: - cMenuWhatsOn(const cSchedules *Schedules, bool Now, int CurrentChannelNr); + cMenuWhatsOn(const cTimers *Timers, const cChannels *Channels, const cSchedules *Schedules, bool Now, int CurrentChannelNr); static int CurrentChannel(void) { return currentChannel; } static void SetCurrentChannel(int ChannelNr) { currentChannel = ChannelNr; } static const cEvent *ScheduleEvent(void); @@ -1437,22 +1599,18 @@ public: int cMenuWhatsOn::currentChannel = 0; const cEvent *cMenuWhatsOn::scheduleEvent = NULL; -cMenuWhatsOn::cMenuWhatsOn(const cSchedules *Schedules, bool Now, int CurrentChannelNr) +cMenuWhatsOn::cMenuWhatsOn(const cTimers *Timers, const cChannels *Channels, const cSchedules *Schedules, bool Now, int CurrentChannelNr) :cOsdMenu(Now ? tr("What's on now?") : tr("What's on next?"), CHNUMWIDTH, CHNAMWIDTH, 6, 4) { SetMenuCategory(Now ? mcScheduleNow : mcScheduleNext); now = Now; canSwitch = false; helpKeys = 0; - timerState = 0; - Timers.Modified(timerState); - for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { + for (const cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) { if (!Channel->GroupSep()) { - const cSchedule *Schedule = Schedules->GetSchedule(Channel); - if (Schedule) { - const cEvent *Event = Now ? Schedule->GetPresentEvent() : Schedule->GetFollowingEvent(); - if (Event) - Add(new cMenuScheduleItem(Event, Channel), Channel->Number() == CurrentChannelNr); + if (const cSchedule *Schedule = Schedules->GetSchedule(Channel)) { + if (const cEvent *Event = Now ? Schedule->GetPresentEvent() : Schedule->GetFollowingEvent()) + Add(new cMenuScheduleItem(Timers, Event, Channel), Channel->Number() == CurrentChannelNr); } } } @@ -1464,11 +1622,12 @@ cMenuWhatsOn::cMenuWhatsOn(const cSchedules *Schedules, bool Now, int CurrentCha bool cMenuWhatsOn::Update(void) { bool result = false; - if (Timers.Modified(timerState)) { + if (const cTimers *Timers = cTimers::GetTimersRead(timersStateKey)) { for (cOsdItem *item = First(); item; item = Next(item)) { - if (((cMenuScheduleItem *)item)->Update()) + if (((cMenuScheduleItem *)item)->Update(Timers)) result = true; } + timersStateKey.Remove(); } return result; } @@ -1487,7 +1646,8 @@ void cMenuWhatsOn::SetHelpKeys(void) NewHelpKeys |= 0x04; // "Next" else NewHelpKeys |= 0x08; // "Now" - if (cChannel *Channel = Channels.GetByChannelID(item->event->ChannelID(), true)) { + LOCK_CHANNELS_READ; + if (const cChannel *Channel = Channels->GetByChannelID(item->event->ChannelID(), true)) { if (Channel->Number() != cDevice::CurrentChannel()) { NewHelpKeys |= 0x10; // "Switch" canSwitch = true; @@ -1512,8 +1672,13 @@ eOSState cMenuWhatsOn::Switch(void) { cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current()); if (item) { - cChannel *channel = Channels.GetByChannelID(item->event->ChannelID(), true); - if (channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true)) + LOCK_CHANNELS_READ; + const cChannel *Channel = Channels->GetByChannelID(item->event->ChannelID(), true); + if (Channel) { + if (!cDevice::PrimaryDevice()->SwitchChannel(Channel, true)) + Channel = NULL; + } + if (Channel) return osEnd; } Skins.Message(mtError, tr("Can't switch channel!")); @@ -1522,33 +1687,38 @@ eOSState cMenuWhatsOn::Switch(void) eOSState cMenuWhatsOn::Record(void) { - cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current()); - if (item) { - if (item->timerMatch == tmFull) { - eTimerMatch 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); - Timers.SetModified(); - isyslog("timer %s added (active)", *timer->ToDescr()); - if (timer->Matches(0, false, NEWTIMERLIMIT)) - return AddSubMenu(new cMenuEditTimer(timer)); - if (HasSubMenu()) - CloseSubMenu(); - if (Update()) - Display(); - SetHelpKeys(); - } + if (cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current())) { + { + LOCK_TIMERS_WRITE; + LOCK_SCHEDULES_READ; + Timers->SetExplicitModify(); + if (item->timerMatch == tmFull) { + if (cTimer *Timer = Timers->GetMatch(item->event)) + return AddSubMenu(new cMenuEditTimer(Timer)); + } + cTimer *Timer = new cTimer(item->event); + if (*Setup.SVDRPDefaultHost) + Timer->SetRemote(Setup.SVDRPDefaultHost); + if (cTimer *t = Timers->GetTimer(Timer)) { + delete Timer; + Timer = t; + return AddSubMenu(new cMenuEditTimer(Timer)); + } + if (Timer->Matches(0, false, NEWTIMERLIMIT)) + return AddSubMenu(new cMenuEditTimer(Timer, true)); + Timers->Add(Timer); + Timers->SetModified(); + if (!HandleRemoteModifications(Timer)) { + // must add the timer before HandleRemoteModifications to get proper log messages with timer ids + Timers->Del(Timer); + delete Timer; + } + } + if (HasSubMenu()) + CloseSubMenu(); + if (Update()) + Display(); + SetHelpKeys(); } return osContinue; } @@ -1576,8 +1746,11 @@ eOSState cMenuWhatsOn::ProcessKey(eKeys Key) return Switch(); break; case kInfo: - case kOk: if (Count()) - return AddSubMenu(new cMenuEvent(((cMenuScheduleItem *)Get(Current()))->event, canSwitch, true)); + case kOk: if (Count()) { + LOCK_TIMERS_READ; + LOCK_CHANNELS_READ; + return AddSubMenu(new cMenuEvent(Timers, Channels, ((cMenuScheduleItem *)Get(Current()))->event, canSwitch, true)); + } break; default: break; } @@ -1595,19 +1768,20 @@ eOSState cMenuWhatsOn::ProcessKey(eKeys Key) class cMenuSchedule : public cOsdMenu { private: - cSchedulesLock schedulesLock; - const cSchedules *schedules; + cStateKey timersStateKey; + cStateKey schedulesStateKey; + int scheduleState; bool now, next; bool canSwitch; int helpKeys; - int timerState; + void Set(const cChannel *Channel = NULL, bool Force = false); eOSState Number(void); eOSState Record(void); eOSState Switch(void); - void PrepareScheduleAllThis(const cEvent *Event, const cChannel *Channel); - void PrepareScheduleThisThis(const cEvent *Event, const cChannel *Channel); - void PrepareScheduleThisAll(const cEvent *Event, const cChannel *Channel); - void PrepareScheduleAllAll(const cEvent *Event, const cChannel *Channel); + bool PrepareScheduleAllThis(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel); + bool PrepareScheduleThisThis(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel); + bool PrepareScheduleThisAll(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel); + bool PrepareScheduleAllAll(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel); bool Update(void); void SetHelpKeys(void); public: @@ -1620,19 +1794,13 @@ cMenuSchedule::cMenuSchedule(void) :cOsdMenu("") { SetMenuCategory(mcSchedule); + scheduleState = -1; now = next = false; canSwitch = false; helpKeys = 0; - timerState = 0; - Timers.Modified(timerState); cMenuScheduleItem::SetSortMode(cMenuScheduleItem::ssmAllThis); - cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel()); - if (channel) { - cMenuWhatsOn::SetCurrentChannel(channel->Number()); - schedules = cSchedules::Schedules(schedulesLock); - PrepareScheduleAllThis(NULL, channel); - SetHelpKeys(); - } + cMenuWhatsOn::SetCurrentChannel(cDevice::CurrentChannel()); + Set(NULL, true); } cMenuSchedule::~cMenuSchedule() @@ -1640,87 +1808,131 @@ cMenuSchedule::~cMenuSchedule() cMenuWhatsOn::ScheduleEvent(); // makes sure any posted data is cleared } -void cMenuSchedule::PrepareScheduleAllThis(const cEvent *Event, const cChannel *Channel) +void cMenuSchedule::Set(const cChannel *Channel, bool Force) { - Clear(); - SetCols(7, 6, 4); - SetTitle(cString::sprintf(tr("Schedule - %s"), Channel->Name())); - if (schedules && Channel) { - const cSchedule *Schedule = schedules->GetSchedule(Channel); - if (Schedule) { + if (Force) { + schedulesStateKey.Reset(); + scheduleState = -1; + } + LOCK_TIMERS_READ; + LOCK_CHANNELS_READ; + if (const cSchedules *Schedules = cSchedules::GetSchedulesRead(schedulesStateKey)) { + cMenuScheduleItem *CurrentItem = (cMenuScheduleItem *)Get(Current()); + const cEvent *Event = NULL; + if (!Channel) { + if (CurrentItem) { + Event = CurrentItem->event; + Channel = Channels->GetByChannelID(Event->ChannelID(), true); + } + else + Channel = Channels->GetByNumber(cDevice::CurrentChannel()); + } + bool Refresh = false; + switch (cMenuScheduleItem::SortMode()) { + case cMenuScheduleItem::ssmAllThis: Refresh = PrepareScheduleAllThis(Timers, Schedules, Event, Channel); break; + case cMenuScheduleItem::ssmThisThis: Refresh = PrepareScheduleThisThis(Timers, Schedules, Event, Channel); break; + case cMenuScheduleItem::ssmThisAll: Refresh = Force && PrepareScheduleThisAll(Timers, Schedules, Event, Channel); break; + case cMenuScheduleItem::ssmAllAll: Refresh = Force && PrepareScheduleAllAll(Timers, Schedules, Event, Channel); break; + default: esyslog("ERROR: unknown SortMode %d (%s %d)", cMenuScheduleItem::SortMode(), __FUNCTION__, __LINE__); + } + if (Refresh) { + CurrentItem = (cMenuScheduleItem *)Get(Current()); + Sort(); + SetCurrent(CurrentItem); + SetHelpKeys(); + Display(); + } + schedulesStateKey.Remove(); + } +} + +bool cMenuSchedule::PrepareScheduleAllThis(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel) +{ + if (const cSchedule *Schedule = Schedules->GetSchedule(Channel)) { + if (Schedule->Modified(scheduleState)) { + Clear(); + SetCols(7, 6, 4); + SetTitle(cString::sprintf(tr("Schedule - %s"), Channel->Name())); const cEvent *PresentEvent = Event ? Event : Schedule->GetPresentEvent(); time_t now = time(NULL) - Setup.EPGLinger * 60; for (const cEvent *ev = Schedule->Events()->First(); ev; ev = Schedule->Events()->Next(ev)) { if (ev->EndTime() > now || ev == PresentEvent) - Add(new cMenuScheduleItem(ev), ev == PresentEvent); + Add(new cMenuScheduleItem(Timers, ev), ev == PresentEvent); } + return true; } } + return false; } -void cMenuSchedule::PrepareScheduleThisThis(const cEvent *Event, const cChannel *Channel) +bool cMenuSchedule::PrepareScheduleThisThis(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel) { - Clear(); - SetCols(7, 6, 4); - SetTitle(cString::sprintf(tr("This event - %s"), Channel->Name())); - if (schedules && Channel && Event) { - const cSchedule *Schedule = schedules->GetSchedule(Channel); - if (Schedule) { - time_t now = time(NULL) - Setup.EPGLinger * 60; - for (const cEvent *ev = Schedule->Events()->First(); ev; ev = Schedule->Events()->Next(ev)) { - if ((ev->EndTime() > now || ev == Event) && !strcmp(ev->Title(), Event->Title())) - Add(new cMenuScheduleItem(ev), ev == Event); - } + if (Event) { + if (const cSchedule *Schedule = Schedules->GetSchedule(Channel)) { + if (Schedule->Modified(scheduleState)) { + Clear(); + SetCols(7, 6, 4); + SetTitle(cString::sprintf(tr("This event - %s"), Channel->Name())); + time_t now = time(NULL) - Setup.EPGLinger * 60; + for (const cEvent *ev = Schedule->Events()->First(); ev; ev = Schedule->Events()->Next(ev)) { + if ((ev->EndTime() > now || ev == Event) && !strcmp(ev->Title(), Event->Title())) + Add(new cMenuScheduleItem(Timers, ev), ev == Event); + } + return true; + } } } + return false; } -void cMenuSchedule::PrepareScheduleThisAll(const cEvent *Event, const cChannel *Channel) +bool cMenuSchedule::PrepareScheduleThisAll(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel) { Clear(); SetCols(CHNUMWIDTH, CHNAMWIDTH, 7, 6, 4); SetTitle(tr("This event - all channels")); - if (schedules && Event) { - for (cChannel *ch = Channels.First(); ch; ch = Channels.Next(ch)) { - const cSchedule *Schedule = schedules->GetSchedule(ch); - if (Schedule) { + if (Event) { + LOCK_CHANNELS_READ; + for (const cChannel *ch = Channels->First(); ch; ch = Channels->Next(ch)) { + if (const cSchedule *Schedule = Schedules->GetSchedule(ch)) { time_t now = time(NULL) - Setup.EPGLinger * 60; for (const cEvent *ev = Schedule->Events()->First(); ev; ev = Schedule->Events()->Next(ev)) { if ((ev->EndTime() > now || ev == Event) && !strcmp(ev->Title(), Event->Title())) - Add(new cMenuScheduleItem(ev, ch, true), ev == Event && ch == Channel); + Add(new cMenuScheduleItem(Timers, ev, ch, true), ev == Event && ch == Channel); } } } } + return true; } -void cMenuSchedule::PrepareScheduleAllAll(const cEvent *Event, const cChannel *Channel) +bool cMenuSchedule::PrepareScheduleAllAll(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel) { Clear(); SetCols(CHNUMWIDTH, CHNAMWIDTH, 7, 6, 4); SetTitle(tr("All events - all channels")); - if (schedules) { - for (cChannel *ch = Channels.First(); ch; ch = Channels.Next(ch)) { - const cSchedule *Schedule = schedules->GetSchedule(ch); - if (Schedule) { - time_t now = time(NULL) - Setup.EPGLinger * 60; - for (const cEvent *ev = Schedule->Events()->First(); ev; ev = Schedule->Events()->Next(ev)) { - if (ev->EndTime() > now || ev == Event) - Add(new cMenuScheduleItem(ev, ch, true), ev == Event && ch == Channel); - } - } + LOCK_CHANNELS_READ; + cStateKey StateKey; + for (const cChannel *ch = Channels->First(); ch; ch = Channels->Next(ch)) { + if (const cSchedule *Schedule = Schedules->GetSchedule(ch)) { + time_t now = time(NULL) - Setup.EPGLinger * 60; + for (const cEvent *ev = Schedule->Events()->First(); ev; ev = Schedule->Events()->Next(ev)) { + if (ev->EndTime() > now || ev == Event) + Add(new cMenuScheduleItem(Timers, ev, ch, true), ev == Event && ch == Channel); + } } - } + } + return true; } bool cMenuSchedule::Update(void) { bool result = false; - if (Timers.Modified(timerState)) { + if (const cTimers *Timers = cTimers::GetTimersRead(timersStateKey)) { for (cOsdItem *item = First(); item; item = Next(item)) { - if (((cMenuScheduleItem *)item)->Update()) + if (((cMenuScheduleItem *)item)->Update(Timers)) result = true; } + timersStateKey.Remove(); } return result; } @@ -1735,7 +1947,8 @@ void cMenuSchedule::SetHelpKeys(void) NewHelpKeys |= 0x02; // "Timer" else NewHelpKeys |= 0x01; // "Record" - if (cChannel *Channel = Channels.GetByChannelID(item->event->ChannelID(), true)) { + LOCK_CHANNELS_READ; + if (const cChannel *Channel = Channels->GetByChannelID(item->event->ChannelID(), true)) { if (Channel->Number() != cDevice::CurrentChannel()) { NewHelpKeys |= 0x10; // "Switch" canSwitch = true; @@ -1752,58 +1965,44 @@ void cMenuSchedule::SetHelpKeys(void) eOSState cMenuSchedule::Number(void) { cMenuScheduleItem::IncSortMode(); - cMenuScheduleItem *CurrentItem = (cMenuScheduleItem *)Get(Current()); - const cChannel *Channel = NULL; - const cEvent *Event = NULL; - if (CurrentItem) { - Event = CurrentItem->event; - Channel = Channels.GetByChannelID(Event->ChannelID(), true); - } - else - Channel = Channels.GetByNumber(cDevice::CurrentChannel()); - switch (cMenuScheduleItem::SortMode()) { - case cMenuScheduleItem::ssmAllThis: PrepareScheduleAllThis(Event, Channel); break; - case cMenuScheduleItem::ssmThisThis: PrepareScheduleThisThis(Event, Channel); break; - case cMenuScheduleItem::ssmThisAll: PrepareScheduleThisAll(Event, Channel); break; - case cMenuScheduleItem::ssmAllAll: PrepareScheduleAllAll(Event, Channel); break; - default: esyslog("ERROR: unknown SortMode %d (%s %d)", cMenuScheduleItem::SortMode(), __FUNCTION__, __LINE__); - } - CurrentItem = (cMenuScheduleItem *)Get(Current()); - Sort(); - SetCurrent(CurrentItem); - Display(); + Set(NULL, true); return osContinue; } eOSState cMenuSchedule::Record(void) { - cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current()); - if (item) { - if (item->timerMatch == tmFull) { - eTimerMatch 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); - Timers.SetModified(); - isyslog("timer %s added (active)", *timer->ToDescr()); - if (timer->Matches(0, false, NEWTIMERLIMIT)) - return AddSubMenu(new cMenuEditTimer(timer)); - if (HasSubMenu()) - CloseSubMenu(); - if (Update()) - Display(); - SetHelpKeys(); - } + if (cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current())) { + { + LOCK_TIMERS_WRITE; + LOCK_SCHEDULES_READ; + Timers->SetExplicitModify(); + if (item->timerMatch == tmFull) { + if (cTimer *Timer = Timers->GetMatch(item->event)) + return AddSubMenu(new cMenuEditTimer(Timer)); + } + cTimer *Timer = new cTimer(item->event); + if (*Setup.SVDRPDefaultHost) + Timer->SetRemote(Setup.SVDRPDefaultHost); + if (cTimer *t = Timers->GetTimer(Timer)) { + delete Timer; + Timer = t; + return AddSubMenu(new cMenuEditTimer(Timer)); + } + if (Timer->Matches(0, false, NEWTIMERLIMIT)) + return AddSubMenu(new cMenuEditTimer(Timer, true)); + Timers->Add(Timer); + Timers->SetModified(); + if (!HandleRemoteModifications(Timer)) { + // must add the timer before HandleRemoteModifications to get proper log messages with timer ids + Timers->Del(Timer); + delete Timer; + } + } + if (HasSubMenu()) + CloseSubMenu(); + if (Update()) + Display(); + SetHelpKeys(); } return osContinue; } @@ -1812,10 +2011,14 @@ eOSState cMenuSchedule::Switch(void) { cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current()); if (item) { - if (cChannel *Channel = Channels.GetByChannelID(item->event->ChannelID(), true)) { - if (Channels.SwitchTo(Channel->Number())) - return osEnd; + LOCK_CHANNELS_READ; + const cChannel *Channel = NULL; + if (Channel = Channels->GetByChannelID(item->event->ChannelID(), true)) { + if (!Channels->SwitchTo(Channel->Number())) + Channel = NULL; } + if (Channel) + return osEnd; } Skins.Message(mtError, tr("Can't switch channel!")); return osContinue; @@ -1823,6 +2026,8 @@ eOSState cMenuSchedule::Switch(void) eOSState cMenuSchedule::ProcessKey(eKeys Key) { + if (!HasSubMenu()) + Set(); // react on any changes to the schedules list bool HadSubMenu = HasSubMenu(); eOSState state = cOsdMenu::ProcessKey(Key); @@ -1831,43 +2036,50 @@ eOSState cMenuSchedule::ProcessKey(eKeys Key) case k0: return Number(); case kRecord: case kRed: return Record(); - case kGreen: if (schedules) { - if (!now && !next) { - int ChannelNr = 0; - if (Count()) { - cChannel *channel = Channels.GetByChannelID(((cMenuScheduleItem *)Get(Current()))->event->ChannelID(), true); - if (channel) - ChannelNr = channel->Number(); - } - now = true; - return AddSubMenu(new cMenuWhatsOn(schedules, true, ChannelNr)); - } - now = !now; - next = !next; - return AddSubMenu(new cMenuWhatsOn(schedules, now, cMenuWhatsOn::CurrentChannel())); - } - case kYellow: if (schedules) - return AddSubMenu(new cMenuWhatsOn(schedules, false, cMenuWhatsOn::CurrentChannel())); - break; + case kGreen: { + LOCK_TIMERS_READ; + LOCK_CHANNELS_READ; + LOCK_SCHEDULES_READ; + if (!now && !next) { + int ChannelNr = 0; + if (Count()) { + if (const cChannel *Channel = Channels->GetByChannelID(((cMenuScheduleItem *)Get(Current()))->event->ChannelID(), true)) + ChannelNr = Channel->Number(); + } + now = true; + return AddSubMenu(new cMenuWhatsOn(Timers, Channels, Schedules, now, ChannelNr)); + } + now = !now; + next = !next; + return AddSubMenu(new cMenuWhatsOn(Timers, Channels, Schedules, now, cMenuWhatsOn::CurrentChannel())); + } + case kYellow: { + LOCK_TIMERS_READ; + LOCK_CHANNELS_READ; + LOCK_SCHEDULES_READ; + return AddSubMenu(new cMenuWhatsOn(Timers, Channels, Schedules, false, cMenuWhatsOn::CurrentChannel())); + } case kBlue: if (canSwitch) return Switch(); break; case kInfo: - case kOk: if (Count()) - return AddSubMenu(new cMenuEvent(((cMenuScheduleItem *)Get(Current()))->event, canSwitch, true)); + case kOk: if (Count()) { + LOCK_TIMERS_READ; + LOCK_CHANNELS_READ; + LOCK_SCHEDULES_READ; + return AddSubMenu(new cMenuEvent(Timers, Channels, ((cMenuScheduleItem *)Get(Current()))->event, canSwitch, true)); + } break; default: break; } } else if (!HasSubMenu()) { now = next = false; - const cEvent *ei = cMenuWhatsOn::ScheduleEvent(); - if (ei) { - cChannel *channel = Channels.GetByChannelID(ei->ChannelID(), true); - if (channel) { + if (const cEvent *ei = cMenuWhatsOn::ScheduleEvent()) { + LOCK_CHANNELS_READ; + if (const cChannel *Channel = Channels->GetByChannelID(ei->ChannelID(), true)) { cMenuScheduleItem::SetSortMode(cMenuScheduleItem::ssmAllThis); - PrepareScheduleAllThis(NULL, channel); - Display(); + Set(Channel, true); } } else if (HadSubMenu && Update()) @@ -2219,7 +2431,10 @@ cMenuPathEdit::cMenuPathEdit(const char *Path) else s = path; strn0cpy(name, s, sizeof(name)); - pathIsInUse = Recordings.PathIsInUse(path); + { + LOCK_RECORDINGS_READ; + pathIsInUse = Recordings->PathIsInUse(path); + } cOsdItem *p; Add(p = folderItem = new cMenuEditStrItem(tr("Folder"), folder, sizeof(folder))); p->SetSelectable(!pathIsInUse); @@ -2258,14 +2473,17 @@ eOSState cMenuPathEdit::ApplyChanges(void) cString NewPath = *folder ? cString::sprintf("%s%c%s", folder, FOLDERDELIMCHAR, name) : name; NewPath.CompactChars(FOLDERDELIMCHAR); if (strcmp(NewPath, path)) { - int NumRecordings = Recordings.GetNumRecordingsInPath(path); + LOCK_RECORDINGS_WRITE; + Recordings->SetExplicitModify(); + int NumRecordings = Recordings->GetNumRecordingsInPath(path); if (NumRecordings > 1 && !Interface->Confirm(cString::sprintf(tr("Move entire folder containing %d recordings?"), NumRecordings))) return osContinue; - if (!Recordings.MoveRecordings(path, NewPath)) { + if (!Recordings->MoveRecordings(path, NewPath)) { Skins.Message(mtError, tr("Error while moving folder!")); return osContinue; } cMenuRecordings::SetPath(NewPath); // makes sure the Recordings menu will reposition to the new path + Recordings->SetModified(); return osUser1; } return osBack; @@ -2294,9 +2512,9 @@ eOSState cMenuPathEdit::ProcessKey(eKeys Key) class cMenuRecordingEdit : public cOsdMenu { private: - cRecording *recording; + const cRecording *recording; cString originalFileName; - int recordingsState; + cStateKey recordingsStateKey; char folder[PATH_MAX]; char name[NAME_MAX]; int priority; @@ -2319,17 +2537,16 @@ private: eOSState DeleteMarks(void); eOSState ApplyChanges(void); public: - cMenuRecordingEdit(cRecording *Recording); + cMenuRecordingEdit(const cRecording *Recording); virtual eOSState ProcessKey(eKeys Key); }; -cMenuRecordingEdit::cMenuRecordingEdit(cRecording *Recording) +cMenuRecordingEdit::cMenuRecordingEdit(const cRecording *Recording) :cOsdMenu(tr("Edit recording"), 12) { SetMenuCategory(mcRecordingEdit); recording = Recording; originalFileName = recording->FileName(); - Recordings.StateChanged(recordingsState); // just to get the current state strn0cpy(folder, recording->Folder(), sizeof(folder)); strn0cpy(name, recording->BaseName(), sizeof(name)); priority = recording->Priority(); @@ -2390,13 +2607,15 @@ void cMenuRecordingEdit::SetHelpKeys(void) bool cMenuRecordingEdit::RefreshRecording(void) { - if (Recordings.StateChanged(recordingsState)) { - if ((recording = Recordings.GetByName(originalFileName)) != NULL) + if (const cRecordings *Recordings = cRecordings::GetRecordingsRead(recordingsStateKey)) { + if ((recording = Recordings->GetByName(originalFileName)) != NULL) Set(); else { + recordingsStateKey.Remove(); Skins.Message(mtWarning, tr("Recording vanished!")); return false; } + recordingsStateKey.Remove(); } return true; } @@ -2453,7 +2672,7 @@ eOSState cMenuRecordingEdit::RemoveName(void) eOSState cMenuRecordingEdit::DeleteMarks(void) { if (buttonDeleteMarks && Interface->Confirm(tr("Delete editing marks for this recording?"))) { - if (recording->DeleteMarks()) + if (cMarks::DeleteMarksFile(recording)) SetHelpKeys(); else Skins.Message(mtError, tr("Error while deleting editing marks!")); @@ -2463,10 +2682,18 @@ eOSState cMenuRecordingEdit::DeleteMarks(void) eOSState cMenuRecordingEdit::ApplyChanges(void) { + cStateKey StateKey; + cRecordings *Recordings = cRecordings::GetRecordingsWrite(StateKey); + cRecording *Recording = Recordings->GetByName(recording->FileName()); + if (!Recording) { + Skins.Message(mtWarning, tr("Recording vanished!")); + return osBack; + } bool Modified = false; if (priority != recording->Priority() || lifetime != recording->Lifetime()) { - if (!recording->ChangePriorityLifetime(priority, lifetime)) { + if (!Recording->ChangePriorityLifetime(priority, lifetime)) { Skins.Message(mtError, tr("Error while changing priority/lifetime!")); + StateKey.Remove(Modified); return osContinue; } Modified = true; @@ -2477,17 +2704,21 @@ eOSState cMenuRecordingEdit::ApplyChanges(void) } cString NewName = *folder ? cString::sprintf("%s%c%s", folder, FOLDERDELIMCHAR, name) : name; NewName.CompactChars(FOLDERDELIMCHAR); - if (strcmp(NewName, recording->Name())) { - if (!recording->ChangeName(NewName)) { + if (strcmp(NewName, Recording->Name())) { + if (!Recording->ChangeName(NewName)) { Skins.Message(mtError, tr("Error while changing folder/name!")); + StateKey.Remove(Modified); return osContinue; } Modified = true; } if (Modified) { - cMenuRecordings::SetRecording(recording->FileName()); // makes sure the Recordings menu will reposition to the renamed recording + cMenuRecordings::SetRecording(Recording->FileName()); // makes sure the Recordings menu will reposition to the renamed recording + Recordings->TouchUpdate(); + StateKey.Remove(Modified); return osUser1; } + StateKey.Remove(Modified); return osBack; } @@ -2517,24 +2748,23 @@ eOSState cMenuRecordingEdit::ProcessKey(eKeys Key) class cMenuRecording : public cOsdMenu { private: - cRecording *recording; + const cRecording *recording; cString originalFileName; - int recordingsState; + cStateKey recordingsStateKey; bool withButtons; bool RefreshRecording(void); public: - cMenuRecording(cRecording *Recording, bool WithButtons = false); + cMenuRecording(const cRecording *Recording, bool WithButtons = false); virtual void Display(void); virtual eOSState ProcessKey(eKeys Key); }; -cMenuRecording::cMenuRecording(cRecording *Recording, bool WithButtons) +cMenuRecording::cMenuRecording(const cRecording *Recording, bool WithButtons) :cOsdMenu(tr("Recording info")) { SetMenuCategory(mcRecordingInfo); recording = Recording; originalFileName = recording->FileName(); - Recordings.StateChanged(recordingsState); // just to get the current state withButtons = WithButtons; if (withButtons) SetHelp(tr("Button$Play"), tr("Button$Rewind"), NULL, tr("Button$Edit")); @@ -2542,13 +2772,15 @@ cMenuRecording::cMenuRecording(cRecording *Recording, bool WithButtons) bool cMenuRecording::RefreshRecording(void) { - if (Recordings.StateChanged(recordingsState)) { - if ((recording = Recordings.GetByName(originalFileName)) != NULL) + if (const cRecordings *Recordings = cRecordings::GetRecordingsRead(recordingsStateKey)) { + if ((recording = Recordings->GetByName(originalFileName)) != NULL) Display(); else { + recordingsStateKey.Remove(); Skins.Message(mtWarning, tr("Recording vanished!")); return false; } + recordingsStateKey.Remove(); } return true; } @@ -2611,23 +2843,23 @@ eOSState cMenuRecording::ProcessKey(eKeys Key) class cMenuRecordingItem : public cOsdItem { private: - cRecording *recording; + const cRecording *recording; int level; char *name; int totalEntries, newEntries; public: - cMenuRecordingItem(cRecording *Recording, int Level); + cMenuRecordingItem(const cRecording *Recording, int Level); ~cMenuRecordingItem(); void IncrementCounter(bool New); const char *Name(void) { return name; } int Level(void) { return level; } - cRecording *Recording(void) { return recording; } + const cRecording *Recording(void) { return recording; } bool IsDirectory(void) { return name != NULL; } - void SetRecording(cRecording *Recording) { recording = Recording; } + void SetRecording(const cRecording *Recording) { recording = Recording; } virtual void SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable); }; -cMenuRecordingItem::cMenuRecordingItem(cRecording *Recording, int Level) +cMenuRecordingItem::cMenuRecordingItem(const cRecording *Recording, int Level) { recording = Recording; level = Level; @@ -2669,7 +2901,6 @@ cMenuRecordings::cMenuRecordings(const char *Base, int Level, bool OpenSubMenus, base = Base ? strdup(Base) : NULL; level = Setup.RecordingDirs ? Level : -1; filter = Filter; - Recordings.StateChanged(recordingsState); // just to get the current state helpKeys = -1; Display(); // this keeps the higher level menus from showing up briefly when pressing 'Back' during replay Set(); @@ -2717,50 +2948,54 @@ void cMenuRecordings::SetHelpKeys(void) void cMenuRecordings::Set(bool Refresh) { - const char *CurrentRecording = *fileName ? *fileName : cReplayControl::LastReplayed(); - cMenuRecordingItem *LastItem = NULL; - cThreadLock RecordingsLock(&Recordings); - if (Refresh) { - if (cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current())) - CurrentRecording = ri->Recording()->FileName(); - } - Clear(); - GetRecordingsSortMode(DirectoryName()); - Recordings.Sort(); - for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) { - if ((!filter || filter->Filter(recording)) && (!base || (strstr(recording->Name(), base) == recording->Name() && recording->Name()[strlen(base)] == FOLDERDELIMCHAR))) { - cMenuRecordingItem *Item = new cMenuRecordingItem(recording, level); - cMenuRecordingItem *LastDir = NULL; - if (Item->IsDirectory()) { - // Sorting may ignore non-alphanumeric characters, so we need to explicitly handle directories in case they only differ in such characters: - for (cMenuRecordingItem *p = LastItem; p; p = dynamic_cast(p->Prev())) { - if (p->Name() && strcmp(p->Name(), Item->Name()) == 0) { - LastDir = p; - break; + if (cRecordings::GetRecordingsRead(recordingsStateKey)) { + recordingsStateKey.Remove(); + const char *CurrentRecording = *fileName ? *fileName : cReplayControl::LastReplayed(); + cRecordings *Recordings = cRecordings::GetRecordingsWrite(recordingsStateKey); // write access is necessary for sorting! + cMenuRecordingItem *LastItem = NULL; + if (Refresh) { + if (cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current())) + CurrentRecording = ri->Recording()->FileName(); + } + Clear(); + GetRecordingsSortMode(DirectoryName()); + Recordings->Sort(); + for (const cRecording *Recording = Recordings->First(); Recording; Recording = Recordings->Next(Recording)) { + if ((!filter || filter->Filter(Recording)) && (!base || (strstr(Recording->Name(), base) == Recording->Name() && Recording->Name()[strlen(base)] == FOLDERDELIMCHAR))) { + cMenuRecordingItem *Item = new cMenuRecordingItem(Recording, level); + cMenuRecordingItem *LastDir = NULL; + if (Item->IsDirectory()) { + // Sorting may ignore non-alphanumeric characters, so we need to explicitly handle directories in case they only differ in such characters: + for (cMenuRecordingItem *p = LastItem; p; p = dynamic_cast(p->Prev())) { + if (p->Name() && strcmp(p->Name(), Item->Name()) == 0) { + LastDir = p; + break; + } } - } - } - if (*Item->Text() && !LastDir) { - Add(Item); - LastItem = Item; - if (Item->IsDirectory()) - LastDir = Item; - } - else - delete Item; - if (LastItem || LastDir) { - if (*path) { - if (strcmp(path, recording->Folder()) == 0) + } + if (*Item->Text() && !LastDir) { + Add(Item); + LastItem = Item; + if (Item->IsDirectory()) + LastDir = Item; + } + else + delete Item; + if (LastItem || LastDir) { + if (*path) { + if (strcmp(path, Recording->Folder()) == 0) + SetCurrent(LastDir ? LastDir : LastItem); + } + else if (CurrentRecording && strcmp(CurrentRecording, Recording->FileName()) == 0) SetCurrent(LastDir ? LastDir : LastItem); } - else if (CurrentRecording && strcmp(CurrentRecording, recording->FileName()) == 0) - SetCurrent(LastDir ? LastDir : LastItem); + if (LastDir) + LastDir->IncrementCounter(Recording->IsNew()); } - if (LastDir) - LastDir->IncrementCounter(recording->IsNew()); } - } - SetMenuSortMode(RecordingsSortMode == rsmName ? msmName : msmTime); + SetMenuSortMode(RecordingsSortMode == rsmName ? msmName : msmTime); + recordingsStateKey.Remove(false); // sorting doesn't count as a real modification + } if (Refresh) Display(); } @@ -2837,50 +3072,60 @@ eOSState cMenuRecordings::Delete(void) cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current()); if (ri && !ri->IsDirectory()) { if (Interface->Confirm(tr("Delete recording?"))) { - cRecordControl *rc = cRecordControls::GetRecordControl(ri->Recording()->FileName()); - if (rc) { + if (cRecordControl *rc = cRecordControls::GetRecordControl(ri->Recording()->FileName())) { if (Interface->Confirm(tr("Timer still recording - really delete?"))) { - cTimer *timer = rc->Timer(); - if (timer) { - timer->Skip(); - cRecordControls::Process(time(NULL)); - if (timer->IsSingleEvent()) { - isyslog("deleting timer %s", *timer->ToDescr()); - Timers.Del(timer); + if (cTimer *Timer = rc->Timer()) { + LOCK_TIMERS_WRITE; + Timer->Skip(); + cRecordControls::Process(Timers, time(NULL)); + if (Timer->IsSingleEvent()) { + Timers->Del(Timer); + isyslog("deleted timer %s", *Timer->ToDescr()); } - Timers.SetModified(); } } else return osContinue; } - cRecording *recording = ri->Recording(); - cString FileName = recording->FileName(); + cRecordings *Recordings = cRecordings::GetRecordingsWrite(recordingsStateKey); + Recordings->SetExplicitModify(); + cRecording *Recording = Recordings->GetByName(ri->Recording()->FileName()); + if (!Recording) { + Skins.Message(mtWarning, tr("Recording vanished!")); + recordingsStateKey.Remove(); + return osContinue; + } + cString FileName = Recording->FileName(); if (RecordingsHandler.GetUsage(FileName)) { if (Interface->Confirm(tr("Recording is being edited - really delete?"))) { RecordingsHandler.Del(FileName); - recording = Recordings.GetByName(FileName); // RecordingsHandler.Del() might have deleted it if it was the edited version - // we continue with the code below even if recording is NULL, + Recording = Recordings->GetByName(FileName); // RecordingsHandler.Del() might have deleted it if it was the edited version + // we continue with the code below even if Recording is NULL, // in order to have the menu updated etc. } - else + else { + recordingsStateKey.Remove(); return osContinue; + } } if (cReplayControl::NowReplaying() && strcmp(cReplayControl::NowReplaying(), FileName) == 0) cControl::Shutdown(); - if (!recording || recording->Delete()) { + if (!Recording || Recording->Delete()) { cReplayControl::ClearLastReplayed(FileName); - Recordings.DelByName(FileName); + Recordings->DelByName(FileName); cOsdMenu::Del(Current()); SetHelpKeys(); cVideoDiskUsage::ForceCheck(); Display(); + Recordings->SetModified(); + recordingsStateKey.Remove(); if (!Count()) return osBack; return osUser2; } else Skins.Message(mtError, tr("Error while deleting recording!")); + recordingsStateKey.Remove(); } } return osContinue; @@ -2919,12 +3164,15 @@ eOSState cMenuRecordings::Sort(void) if (HasSubMenu()) return osContinue; IncRecordingsSortMode(DirectoryName()); + recordingsStateKey.Reset(); Set(true); return osContinue; } eOSState cMenuRecordings::ProcessKey(eKeys Key) { + if (!HasSubMenu()) + Set(); // react on any changes to the recordings list bool HadSubMenu = HasSubMenu(); eOSState state = cOsdMenu::ProcessKey(Key); @@ -2940,9 +3188,6 @@ eOSState cMenuRecordings::ProcessKey(eKeys Key) case kBlue: return Info(); case k0: return Sort(); case k1...k9: return Commands(Key); - case kNone: if (Recordings.StateChanged(recordingsState)) - Set(true); - break; default: break; } } @@ -3006,6 +3251,7 @@ void cMenuSetupBase::Store(void) class cMenuSetupOSD : public cMenuSetupBase { private: const char *useSmallFontTexts[3]; + const char *recSortModeTexts[2]; const char *keyColorTexts[4]; int osdLanguageIndex; int numSkins; @@ -3058,6 +3304,8 @@ void cMenuSetupOSD::Set(void) useSmallFontTexts[0] = tr("never"); useSmallFontTexts[1] = tr("skin dependent"); useSmallFontTexts[2] = tr("always"); + recSortModeTexts[0] = tr("by name"); + recSortModeTexts[1] = tr("by time"); keyColorTexts[0] = tr("Key$Red"); keyColorTexts[1] = tr("Key$Green"); keyColorTexts[2] = tr("Key$Yellow"); @@ -3091,6 +3339,7 @@ void cMenuSetupOSD::Set(void) Add(new cMenuEditBoolItem(tr("Setup.OSD$Recording directories"), &data.RecordingDirs)); Add(new cMenuEditBoolItem(tr("Setup.OSD$Folders in timer menu"), &data.FoldersInTimerMenu)); Add(new cMenuEditBoolItem(tr("Setup.OSD$Always sort folders first"), &data.AlwaysSortFoldersFirst)); + Add(new cMenuEditStraItem(tr("Setup.OSD$Default sort mode for recordings"), &data.DefaultSortModeRec, 2, recSortModeTexts)); Add(new cMenuEditBoolItem(tr("Setup.OSD$Number keys for characters"), &data.NumberKeysForChars)); Add(new cMenuEditStraItem(tr("Setup.OSD$Color key 0"), &data.ColorKey0, 4, keyColorTexts)); Add(new cMenuEditStraItem(tr("Setup.OSD$Color key 1"), &data.ColorKey1, 4, keyColorTexts)); @@ -3132,8 +3381,10 @@ eOSState cMenuSetupOSD::ProcessKey(eKeys Key) ModifiedAppearance = true; if (strcmp(data.FontFix, Setup.FontFix) || !DoubleEqual(data.FontFixSizeP, Setup.FontFixSizeP)) ModifiedAppearance = true; - if (data.AlwaysSortFoldersFirst != Setup.AlwaysSortFoldersFirst || data.RecordingDirs != Setup.RecordingDirs) - Recordings.ClearSortNames(); + if (data.AlwaysSortFoldersFirst != Setup.AlwaysSortFoldersFirst || data.RecordingDirs != Setup.RecordingDirs) { + LOCK_RECORDINGS_WRITE; + Recordings->ClearSortNames(); + } } int oldSkinIndex = skinIndex; @@ -3507,6 +3758,7 @@ cMenuSetupCAMItem::cMenuSetupCAMItem(cCamSlot *CamSlot) bool cMenuSetupCAMItem::Changed(void) { + cString AssignedDevice(""); const char *Activating = ""; const char *CamName = camSlot->GetCamName(); if (!CamName) { @@ -3520,7 +3772,9 @@ bool cMenuSetupCAMItem::Changed(void) else if (camSlot->IsActivating()) // TRANSLATORS: note the leading blank! Activating = tr(" (activating)"); - cString buffer = cString::sprintf(" %d %s%s", camSlot->SlotNumber(), CamName, Activating); + if (cDevice *Device = camSlot->Device()) + AssignedDevice = cString::sprintf(" %s %d", tr("@ device"), Device->CardIndex() + 1); + cString buffer = cString::sprintf(" %d %s%s%s", camSlot->SlotNumber(), CamName, *AssignedDevice, Activating); if (strcmp(buffer, Text()) != 0) { SetText(buffer); return true; @@ -3607,7 +3861,8 @@ eOSState cMenuSetupCAM::Activate(void) CamSlot->CancelActivation(); else if (CamSlot->CanActivate()) { if (CamSlot->Priority() < LIVEPRIORITY) { // don't interrupt recordings - if (cChannel *Channel = Channels.GetByNumber(cDevice::CurrentChannel())) { + LOCK_CHANNELS_READ; + if (const cChannel *Channel = Channels->GetByNumber(cDevice::CurrentChannel())) { for (int i = 0; i < cDevice::NumDevices(); i++) { if (cDevice *Device = cDevice::GetDevice(i)) { if (Device->ProvidesChannel(Channel)) { @@ -3672,6 +3927,7 @@ eOSState cMenuSetupCAM::ProcessKey(eKeys Key) class cMenuSetupRecord : public cMenuSetupBase { private: + const char *recordKeyHandlingTexts[3]; const char *pauseKeyHandlingTexts[3]; const char *delTimeshiftRecTexts[3]; public: @@ -3681,6 +3937,9 @@ public: cMenuSetupRecord::cMenuSetupRecord(void) { SetMenuCategory(mcSetupRecord); + recordKeyHandlingTexts[0] = tr("no instant recording"); + recordKeyHandlingTexts[1] = tr("confirm instant recording"); + recordKeyHandlingTexts[2] = tr("record instantly"); pauseKeyHandlingTexts[0] = tr("do not pause live video"); pauseKeyHandlingTexts[1] = tr("confirm pause live video"); pauseKeyHandlingTexts[2] = tr("pause live video"); @@ -3692,6 +3951,7 @@ cMenuSetupRecord::cMenuSetupRecord(void) Add(new cMenuEditIntItem( tr("Setup.Recording$Margin at stop (min)"), &data.MarginStop)); Add(new cMenuEditIntItem( tr("Setup.Recording$Default priority"), &data.DefaultPriority, 0, MAXPRIORITY)); Add(new cMenuEditIntItem( tr("Setup.Recording$Default lifetime (d)"), &data.DefaultLifetime, 0, MAXLIFETIME)); + Add(new cMenuEditStraItem(tr("Setup.Recording$Record key handling"), &data.RecordKeyHandling, 3, recordKeyHandlingTexts)); Add(new cMenuEditStraItem(tr("Setup.Recording$Pause key handling"), &data.PauseKeyHandling, 3, pauseKeyHandlingTexts)); Add(new cMenuEditIntItem( tr("Setup.Recording$Pause priority"), &data.PausePriority, 0, MAXPRIORITY)); Add(new cMenuEditIntItem( tr("Setup.Recording$Pause lifetime (d)"), &data.PauseLifetime, 0, MAXLIFETIME)); @@ -3738,25 +3998,47 @@ cMenuSetupReplay::cMenuSetupReplay(void) void cMenuSetupReplay::Store(void) { - if (Setup.ResumeID != data.ResumeID) - Recordings.ResetResume(); + if (Setup.ResumeID != data.ResumeID) { + LOCK_RECORDINGS_WRITE; + Recordings->ResetResume(); + } cMenuSetupBase::Store(); } // --- cMenuSetupMisc -------------------------------------------------------- class cMenuSetupMisc : public cMenuSetupBase { +private: + cStringList svdrpServerNames; + void Set(void); public: cMenuSetupMisc(void); + virtual eOSState ProcessKey(eKeys Key); }; cMenuSetupMisc::cMenuSetupMisc(void) { SetMenuCategory(mcSetupMisc); SetSection(tr("Miscellaneous")); + Set(); +} + +void cMenuSetupMisc::Set(void) +{ + int current = Current(); + Clear(); Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Min. event timeout (min)"), &data.MinEventTimeout)); Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Min. user inactivity (min)"), &data.MinUserInactivity)); Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$SVDRP timeout (s)"), &data.SVDRPTimeout)); + Add(new cMenuEditBoolItem(tr("Setup.Miscellaneous$SVDRP peering"), &data.SVDRPPeering)); + if (data.SVDRPPeering) { + Add(new cMenuEditStrItem( tr("Setup.Miscellaneous$SVDRP host name"), data.SVDRPHostName, sizeof(data.SVDRPHostName))); + if (GetSVDRPServerNames(&svdrpServerNames)) { + svdrpServerNames.Sort(true); + svdrpServerNames.Insert(strdup("")); + Add(new cMenuEditStrlItem(tr("Setup.Miscellaneous$SVDRP default host"), data.SVDRPDefaultHost, sizeof(data.SVDRPDefaultHost), &svdrpServerNames)); + } + } Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Zap timeout (s)"), &data.ZapTimeout)); Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Channel entry timeout (ms)"), &data.ChannelEntryTimeout, 0)); Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Remote control repeat delay (ms)"), &data.RcRepeatDelay, 0)); @@ -3768,6 +4050,33 @@ cMenuSetupMisc::cMenuSetupMisc(void) Add(new cMenuEditBoolItem(tr("Setup.Miscellaneous$Channels wrap"), &data.ChannelsWrap)); Add(new cMenuEditBoolItem(tr("Setup.Miscellaneous$Show channel names with source"), &data.ShowChannelNamesWithSource)); Add(new cMenuEditBoolItem(tr("Setup.Miscellaneous$Emergency exit"), &data.EmergencyExit)); + SetCurrent(Get(current)); + Display(); +} + +eOSState cMenuSetupMisc::ProcessKey(eKeys Key) +{ + bool OldSVDRPPeering = data.SVDRPPeering; + bool ModifiedSVDRPSettings = false; + if (Key == kOk) + ModifiedSVDRPSettings = data.SVDRPPeering != Setup.SVDRPPeering | strcmp(data.SVDRPHostName, Setup.SVDRPHostName); + eOSState state = cMenuSetupBase::ProcessKey(Key); + if (data.SVDRPPeering != OldSVDRPPeering) + Set(); + if (ModifiedSVDRPSettings) { + StopSVDRPClientHandler(); + StopSVDRPServerHandler(); + StartSVDRPServerHandler(); + if (data.SVDRPPeering) + StartSVDRPClientHandler(); + else { + LOCK_TIMERS_WRITE; + Timers->SetExplicitModify(); + if (Timers->DelRemoteTimers()) + Timers->SetModified(); + } + } + return state; } // --- cMenuSetupPluginItem -------------------------------------------------- @@ -4018,7 +4327,7 @@ bool cMenuMain::Update(bool Force) stopReplayItem = NULL; } // Color buttons: - SetHelp(!replaying ? tr("Button$Record") : NULL, tr("Button$Audio"), replaying || !Setup.PauseKeyHandling ? NULL : tr("Button$Pause"), replaying ? tr("Button$Stop") : cReplayControl::LastReplayed() ? tr("Button$Resume") : tr("Button$Play")); + SetHelp(!replaying && Setup.RecordKeyHandling ? tr("Button$Record") : NULL, tr("Button$Audio"), replaying || !Setup.PauseKeyHandling ? NULL : tr("Button$Pause"), replaying ? tr("Button$Stop") : cReplayControl::LastReplayed() ? tr("Button$Resume") : tr("Button$Play")); result = true; } @@ -4071,8 +4380,7 @@ eOSState cMenuMain::ProcessKey(eKeys Key) case osSetup: return AddSubMenu(new cMenuSetup); case osCommands: return AddSubMenu(new cMenuCommands(tr("Commands"), &Commands)); case osStopRecord: if (Interface->Confirm(tr("Stop recording?"))) { - cOsdItem *item = Get(Current()); - if (item) { + if (cOsdItem *item = Get(Current())) { cRecordControls::Stop(item->Text() + strlen(tr(STOP_RECORDING))); return osEnd; } @@ -4105,7 +4413,7 @@ eOSState cMenuMain::ProcessKey(eKeys Key) default: switch (Key) { case kRecord: case kRed: if (!HadSubMenu) - state = replaying ? osContinue : osRecord; + state = replaying || !Setup.RecordKeyHandling ? osContinue : osRecord; break; case kGreen: if (!HadSubMenu) { cRemote::Put(kAudio, true); @@ -4139,25 +4447,20 @@ static void SetTrackDescriptions(int LiveChannel) { cDevice::PrimaryDevice()->ClrAvailableTracks(true); const cComponents *Components = NULL; - cSchedulesLock SchedulesLock; if (LiveChannel) { - cChannel *Channel = Channels.GetByNumber(LiveChannel); - if (Channel) { - const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock); - if (Schedules) { - const cSchedule *Schedule = Schedules->GetSchedule(Channel); - if (Schedule) { - const cEvent *Present = Schedule->GetPresentEvent(); - if (Present) - Components = Present->Components(); - } + LOCK_CHANNELS_READ; + if (const cChannel *Channel = Channels->GetByNumber(LiveChannel)) { + LOCK_SCHEDULES_READ; + if (const cSchedule *Schedule = Schedules->GetSchedule(Channel)) { + const cEvent *Present = Schedule->GetPresentEvent(); + if (Present) + Components = Present->Components(); } } } else if (cReplayControl::NowReplaying()) { - cThreadLock RecordingsLock(&Recordings); - cRecording *Recording = Recordings.GetByName(cReplayControl::NowReplaying()); - if (Recording) + LOCK_RECORDINGS_READ; + if (const cRecording *Recording = Recordings->GetByName(cReplayControl::NowReplaying())) Components = Recording->Info()->Components(); } if (Components) { @@ -4197,7 +4500,9 @@ cDisplayChannel::cDisplayChannel(int Number, bool Switched) timeout = Switched || Setup.TimeoutRequChInfo; cOsdProvider::OsdSizeChanged(osdState); // just to get the current state positioner = NULL; - channel = Channels.GetByNumber(Number); + channel = NULL; + LOCK_CHANNELS_READ; + channel = Channels->GetByNumber(Number); lastPresent = lastFollowing = NULL; if (channel) { DisplayChannel(); @@ -4219,7 +4524,9 @@ cDisplayChannel::cDisplayChannel(eKeys FirstKey) withInfo = Setup.ShowInfoOnChSwitch; displayChannel = Skins.Current()->DisplayChannel(withInfo); positioner = NULL; - channel = Channels.GetByNumber(cDevice::CurrentChannel()); + channel = NULL; + LOCK_CHANNELS_READ; + channel = Channels->GetByNumber(cDevice::CurrentChannel()); ProcessKey(FirstKey); } @@ -4240,20 +4547,16 @@ void cDisplayChannel::DisplayChannel(void) void cDisplayChannel::DisplayInfo(void) { if (withInfo && channel) { - cSchedulesLock SchedulesLock; - const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock); - if (Schedules) { - const cSchedule *Schedule = Schedules->GetSchedule(channel); - if (Schedule) { - const cEvent *Present = Schedule->GetPresentEvent(); - const cEvent *Following = Schedule->GetFollowingEvent(); - if (Present != lastPresent || Following != lastFollowing) { - SetTrackDescriptions(channel->Number()); - displayChannel->SetEvents(Present, Following); - cStatus::MsgOsdProgramme(Present ? Present->StartTime() : 0, Present ? Present->Title() : NULL, Present ? Present->ShortText() : NULL, Following ? Following->StartTime() : 0, Following ? Following->Title() : NULL, Following ? Following->ShortText() : NULL); - lastPresent = Present; - lastFollowing = Following; - } + LOCK_SCHEDULES_READ; + if (const cSchedule *Schedule = Schedules->GetSchedule(channel)) { + const cEvent *Present = Schedule->GetPresentEvent(); + const cEvent *Following = Schedule->GetFollowingEvent(); + if (Present != lastPresent || Following != lastFollowing) { + SetTrackDescriptions(channel->Number()); + displayChannel->SetEvents(Present, Following); + cStatus::MsgOsdProgramme(Present ? Present->StartTime() : 0, Present ? Present->Title() : NULL, Present ? Present->ShortText() : NULL, Following ? Following->StartTime() : 0, Following ? Following->Title() : NULL, Following ? Following->ShortText() : NULL); + lastPresent = Present; + lastFollowing = Following; } } } @@ -4265,13 +4568,14 @@ void cDisplayChannel::Refresh(void) displayChannel->SetEvents(NULL, NULL); } -cChannel *cDisplayChannel::NextAvailableChannel(cChannel *Channel, int Direction) +const cChannel *cDisplayChannel::NextAvailableChannel(const cChannel *Channel, int Direction) { if (Direction) { + LOCK_CHANNELS_READ; while (Channel) { - Channel = Direction > 0 ? Channels.Next(Channel) : Channels.Prev(Channel); + Channel = Direction > 0 ? Channels->Next(Channel) : Channels->Prev(Channel); if (!Channel && Setup.ChannelsWrap) - Channel = Direction > 0 ? Channels.First() : Channels.Last(); + Channel = Direction > 0 ? Channels->First() : Channels->Last(); if (Channel && !Channel->GroupSep() && cDevice::GetDevice(Channel, LIVEPRIORITY, true, true)) return Channel; } @@ -4285,7 +4589,7 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key) delete displayChannel; displayChannel = Skins.Current()->DisplayChannel(withInfo); } - cChannel *NewChannel = NULL; + const cChannel *NewChannel = NULL; if (Key != kNone) lastTime.Set(); switch (int(Key)) { @@ -4298,18 +4602,19 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key) case k1 ... k9: group = -1; if (number >= 0) { - if (number > Channels.MaxNumber()) + if (number > cChannels::MaxNumber()) number = Key - k0; else number = number * 10 + Key - k0; - channel = Channels.GetByNumber(number); + LOCK_CHANNELS_READ + channel = Channels->GetByNumber(number); Refresh(); withInfo = false; // Lets see if there can be any useful further input: int n = channel ? number * 10 : 0; int m = 10; - cChannel *ch = channel; - while (ch && (ch = Channels.Next(ch)) != NULL) { + const cChannel *ch = channel; + while (ch && (ch = Channels->Next(ch)) != NULL) { if (!ch->GroupSep()) { if (n <= ch->Number() && ch->Number() < n + m) { n = 0; @@ -4337,23 +4642,23 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key) case kNext|k_Repeat: case kNext: case kPrev|k_Repeat: - case kPrev: + case kPrev: { withInfo = false; number = 0; + LOCK_CHANNELS_READ; if (group < 0) { - cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel()); - if (channel) - group = channel->Index(); + if (const cChannel *Channel = Channels->GetByNumber(cDevice::CurrentChannel())) + group = Channel->Index(); } if (group >= 0) { int SaveGroup = group; if (NORMALKEY(Key) == kRight || NORMALKEY(Key) == kNext) - group = Channels.GetNextGroup(group) ; + group = Channels->GetNextGroup(group) ; else - group = Channels.GetPrevGroup(group < 1 ? 1 : group); + group = Channels->GetPrevGroup(group < 1 ? 1 : group); if (group < 0) group = SaveGroup; - channel = Channels.Get(group); + channel = Channels->Get(group); if (channel) { Refresh(); if (!channel->GroupSep()) @@ -4361,6 +4666,7 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key) } } break; + } case kUp|k_Repeat: case kUp: case kDown|k_Repeat: @@ -4370,9 +4676,8 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key) case kChanDn|k_Repeat: case kChanDn: { eKeys k = NORMALKEY(Key); - cChannel *ch = NextAvailableChannel(channel, (k == kUp || k == kChanUp) ? 1 : -1); - if (ch) - channel = ch; + if (const cChannel *Channel = NextAvailableChannel(channel, (k == kUp || k == kChanUp) ? 1 : -1)) + channel = Channel; else if (channel && channel->Number() != cDevice::CurrentChannel()) Key = k; // immediately switches channel when hitting the beginning/end of the channel list with k_Repeat } @@ -4392,7 +4697,8 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key) break; case kNone: if (number && Setup.ChannelEntryTimeout && int(lastTime.Elapsed()) > Setup.ChannelEntryTimeout) { - channel = Channels.GetByNumber(number); + LOCK_CHANNELS_READ; + channel = Channels->GetByNumber(number); if (channel) NewChannel = channel; withInfo = true; @@ -4404,9 +4710,10 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key) //TODO //XXX case kGreen: return osEventNow; //XXX case kYellow: return osEventNext; - case kOk: + case kOk: { + LOCK_CHANNELS_READ; if (group >= 0) { - channel = Channels.Get(Channels.GetNextNormal(group)); + channel = Channels->Get(Channels->GetNextNormal(group)); if (channel) NewChannel = channel; withInfo = true; @@ -4414,15 +4721,17 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key) Refresh(); } else if (number > 0) { - channel = Channels.GetByNumber(number); + channel = Channels->GetByNumber(number); if (channel) NewChannel = channel; withInfo = true; number = 0; Refresh(); } - else + else { return osEnd; + } + } break; default: if ((Key & (k_Repeat | k_Release)) == 0) { @@ -4431,16 +4740,17 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key) } }; if (positioner || !timeout || lastTime.Elapsed() < (uint64_t)(Setup.ChannelInfoTime * 1000)) { + LOCK_CHANNELS_READ; if (Key == kNone && !number && group < 0 && !NewChannel && channel && channel->Number() != cDevice::CurrentChannel()) { // makes sure a channel switch through the SVDRP CHAN command is displayed - channel = Channels.GetByNumber(cDevice::CurrentChannel()); + channel = Channels->GetByNumber(cDevice::CurrentChannel()); Refresh(); lastTime.Set(); } DisplayInfo(); if (NewChannel) { SetTrackDescriptions(NewChannel->Number()); // to make them immediately visible in the channel display - Channels.SwitchTo(NewChannel->Number()); + Channels->SwitchTo(NewChannel->Number()); SetTrackDescriptions(NewChannel->Number()); // switching the channel has cleared them channel = NewChannel; } @@ -4751,14 +5061,13 @@ eOSState cDisplaySubtitleTracks::ProcessKey(eKeys Key) // --- cRecordControl -------------------------------------------------------- -cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause) +cRecordControl::cRecordControl(cDevice *Device, cTimers *Timers, cTimer *Timer, bool Pause) { // Whatever happens here, the timers will be modified in some way... - Timers.SetModified(); - // We're going to manipulate an event here, so we need to prevent + Timers->SetModified(); + // We're going to work with an event here, so we need to prevent // others from modifying any EPG data: - cSchedulesLock SchedulesLock; - cSchedules::Schedules(SchedulesLock); + LOCK_SCHEDULES_READ; event = NULL; fileName = NULL; @@ -4768,7 +5077,7 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause) timer = Timer; if (!timer) { timer = new cTimer(true, Pause); - Timers.Add(timer); + Timers->Add(timer); instantId = cString::sprintf(cDevice::NumDevices() > 1 ? "%s - %d" : "%s", timer->Channel()->Name(), device->CardIndex() + 1); } timer->SetPending(true); @@ -4789,7 +5098,7 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause) timer->OnOff(); } else { - Timers.Del(timer); + Timers->Del(timer); if (!cReplayControl::LastReplayed()) // an instant recording, maybe from cRecordControls::PauseLiveVideo() cReplayControl::SetRecording(fileName); } @@ -4807,21 +5116,8 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause) cStatus::MsgRecording(device, Recording.Name(), Recording.FileName(), true); if (!Timer && !cReplayControl::LastReplayed()) // an instant recording, maybe from cRecordControls::PauseLiveVideo() cReplayControl::SetRecording(fileName); - Recordings.AddByName(fileName); - if (Timer && !Timer->IsSingleEvent()) { - char *Directory = strdup(fileName); - // going up two directory levels to get the series folder - if (char *p = strrchr(Directory, '/')) { - while (p > Directory && *--p != '/') - ; - *p = 0; - if (!HasRecordingsSortMode(Directory)) { - dsyslog("setting %s to be sorted by time", Directory); - SetRecordingsSortMode(Directory, rsmTime); - } - } - free(Directory); - } + LOCK_RECORDINGS_WRITE; + Recordings->AddByName(fileName); return; } else @@ -4830,7 +5126,7 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause) else timer->SetDeferred(DEFERTIMER); if (!Timer) { - Timers.Del(timer); + Timers->Del(timer); timer = NULL; } } @@ -4845,21 +5141,17 @@ cRecordControl::~cRecordControl() bool cRecordControl::GetEvent(void) { - const cChannel *channel = timer->Channel(); + const cChannel *Channel = timer->Channel(); time_t Time = timer->HasFlags(tfInstant) ? timer->StartTime() + INSTANT_REC_EPG_LOOKAHEAD : timer->StartTime() + (timer->StopTime() - timer->StartTime()) / 2; for (int seconds = 0; seconds <= MAXWAIT4EPGINFO; seconds++) { { - cSchedulesLock SchedulesLock; - const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock); - if (Schedules) { - const cSchedule *Schedule = Schedules->GetSchedule(channel); - if (Schedule) { - event = Schedule->GetEventAround(Time); - if (event) { - if (seconds > 0) - dsyslog("got EPG info after %d seconds", seconds); - return true; - } + LOCK_SCHEDULES_READ; + if (const cSchedule *Schedule = Schedules->GetSchedule(Channel)) { + event = Schedule->GetEventAround(Time); + if (event) { + if (seconds > 0) + dsyslog("got EPG info after %d seconds", seconds); + return true; } } } @@ -4880,7 +5172,6 @@ void cRecordControl::Stop(bool ExecuteUserCommand) cStatus::MsgRecording(device, NULL, fileName, false); if (ExecuteUserCommand) cRecordingUserCommand::InvokeCommand(RUC_AFTERRECORDING, fileName); - Timers.SetModified(); } } @@ -4900,7 +5191,7 @@ bool cRecordControl::Process(time_t t) cRecordControl *cRecordControls::RecordControls[MAXRECORDCONTROLS] = { NULL }; int cRecordControls::state = 0; -bool cRecordControls::Start(cTimer *Timer, bool Pause) +bool cRecordControls::Start(cTimers *Timers, cTimer *Timer, bool Pause) { static time_t LastNoDiskSpaceMessage = 0; int FreeMB = 0; @@ -4920,29 +5211,28 @@ bool cRecordControls::Start(cTimer *Timer, bool Pause) LastNoDiskSpaceMessage = 0; ChangeState(); + LOCK_CHANNELS_READ; int ch = Timer ? Timer->Channel()->Number() : cDevice::CurrentChannel(); - cChannel *channel = Channels.GetByNumber(ch); - - if (channel) { + if (const cChannel *Channel = Channels->GetByNumber(ch)) { int Priority = Timer ? Timer->Priority() : Pause ? Setup.PausePriority : Setup.DefaultPriority; - cDevice *device = cDevice::GetDevice(channel, Priority, false); + cDevice *device = cDevice::GetDevice(Channel, Priority, false); if (device) { - dsyslog("switching device %d to channel %d (%s)", device->DeviceNumber() + 1, channel->Number(), channel->Name()); - if (!device->SwitchChannel(channel, false)) { + dsyslog("switching device %d to channel %d (%s)", device->DeviceNumber() + 1, Channel->Number(), Channel->Name()); + if (!device->SwitchChannel(Channel, false)) { ShutdownHandler.RequestEmergencyExit(); return false; } if (!Timer || Timer->Matches()) { for (int i = 0; i < MAXRECORDCONTROLS; i++) { if (!RecordControls[i]) { - RecordControls[i] = new cRecordControl(device, Timer, Pause); + RecordControls[i] = new cRecordControl(device, Timers, Timer, Pause); return RecordControls[i]->Process(time(NULL)); } } } } else if (!Timer || !Timer->Pending()) { - isyslog("no free DVB device to record channel %d (%s)!", ch, channel->Name()); + isyslog("no free DVB device to record channel %d (%s)!", ch, Channel->Name()); Skins.Message(mtError, tr("No free DVB device to record!")); } } @@ -4951,19 +5241,25 @@ bool cRecordControls::Start(cTimer *Timer, bool Pause) return false; } +bool cRecordControls::Start(bool Pause) +{ + LOCK_TIMERS_WRITE; + return Start(Timers, NULL, Pause); +} + void cRecordControls::Stop(const char *InstantId) { + LOCK_TIMERS_WRITE; ChangeState(); for (int i = 0; i < MAXRECORDCONTROLS; i++) { if (RecordControls[i]) { const char *id = RecordControls[i]->InstantId(); if (id && strcmp(id, InstantId) == 0) { - cTimer *timer = RecordControls[i]->Timer(); + cTimer *Timer = RecordControls[i]->Timer(); RecordControls[i]->Stop(); - if (timer) { - isyslog("deleting timer %s", *timer->ToDescr()); - Timers.Del(timer); - Timers.SetModified(); + if (Timer) { + Timers->Del(Timer); + isyslog("deleted timer %s", *Timer->ToDescr()); } break; } @@ -4971,11 +5267,24 @@ void cRecordControls::Stop(const char *InstantId) } } +void cRecordControls::Stop(cTimer *Timer) +{ + for (int i = 0; i < MAXRECORDCONTROLS; i++) { + if (RecordControls[i]) { + if (RecordControls[i]->Timer() == Timer) { + DELETENULL(RecordControls[i]); + ChangeState(); + break; + } + } + } +} + bool cRecordControls::PauseLiveVideo(void) { Skins.Message(mtStatus, tr("Pausing live video...")); cReplayControl::SetRecording(NULL); // make sure the new cRecordControl will set cReplayControl::LastReplayed() - if (Start(NULL, true)) { + if (Start(true)) { cReplayControl *rc = new cReplayControl(true); cControl::Launch(rc); cControl::Attach(); @@ -5019,19 +5328,22 @@ cRecordControl *cRecordControls::GetRecordControl(const cTimer *Timer) return NULL; } -void cRecordControls::Process(time_t t) +bool cRecordControls::Process(cTimers *Timers, time_t t) { + bool Result = false; for (int i = 0; i < MAXRECORDCONTROLS; i++) { if (RecordControls[i]) { if (!RecordControls[i]->Process(t)) { DELETENULL(RecordControls[i]); ChangeState(); + Result = true; } } } + return Result; } -void cRecordControls::ChannelDataModified(cChannel *Channel) +void cRecordControls::ChannelDataModified(const cChannel *Channel) { for (int i = 0; i < MAXRECORDCONTROLS; i++) { if (RecordControls[i]) { @@ -5153,19 +5465,25 @@ void cReplayControl::Stop(void) if (rc && rc->InstantId()) { if (Active()) { if (Setup.DelTimeshiftRec == 2 || Interface->Confirm(tr("Delete timeshift recording?"))) { - cTimer *timer = rc->Timer(); - rc->Stop(false); // don't execute user command - if (timer) { - isyslog("deleting timer %s", *timer->ToDescr()); - Timers.Del(timer); - Timers.SetModified(); - } + { + LOCK_TIMERS_WRITE; + Timers->SetExplicitModify(); + cTimer *Timer = rc->Timer(); + rc->Stop(false); // don't execute user command + if (Timer) { + Timers->Del(Timer); + Timers->SetModified(); + isyslog("deleted timer %s", *Timer->ToDescr()); + } + } cDvbPlayerControl::Stop(); - cRecording *recording = Recordings.GetByName(fileName); - if (recording) { - if (recording->Delete()) { - Recordings.DelByName(fileName); + LOCK_RECORDINGS_WRITE; + Recordings->SetExplicitModify(); + if (cRecording *Recording = Recordings->GetByName(fileName)) { + if (Recording->Delete()) { + Recordings->DelByName(fileName); ClearLastReplayed(fileName); + Recordings->SetModified(); } else Skins.Message(mtError, tr("Error while deleting recording!")); @@ -5191,7 +5509,8 @@ const char *cReplayControl::NowReplaying(void) const char *cReplayControl::LastReplayed(void) { - if (!Recordings.GetByName(fileName)) + LOCK_RECORDINGS_READ; + if (!Recordings->GetByName(fileName)) fileName = NULL; return fileName; } @@ -5276,7 +5595,8 @@ bool cReplayControl::ShowProgress(bool Initial) } if (Initial) { if (*fileName) { - if (cRecording *Recording = Recordings.GetByName(fileName)) + LOCK_RECORDINGS_READ; + if (const cRecording *Recording = Recordings->GetByName(fileName)) displayReplay->SetRecording(Recording); } lastCurrent = lastTotal = -1; @@ -5399,15 +5719,12 @@ void cReplayControl::MarkToggle(void) int Current, Total; if (GetIndex(Current, Total, true)) { lastCurrent = -1; // triggers redisplay - if (cMark *m = marks.Get(Current)) { - marks.Lock(); + cStateKey StateKey; + marks.Lock(StateKey); + if (cMark *m = marks.Get(Current)) marks.Del(m); - marks.Unlock(); - } else { - marks.Lock(); marks.Add(Current); - marks.Unlock(); bool Play, Forward; int Speed; if (Setup.PauseOnMarkSet || GetReplayMode(Play, Forward, Speed) && !Play) { @@ -5415,6 +5732,7 @@ void cReplayControl::MarkToggle(void) displayFrames = true; } } + StateKey.Remove(); ShowTimed(2); marksModified = true; } @@ -5522,15 +5840,16 @@ void cReplayControl::EditTest(void) cOsdObject *cReplayControl::GetInfo(void) { - cRecording *Recording = Recordings.GetByName(cReplayControl::LastReplayed()); - if (Recording) + LOCK_RECORDINGS_READ; + if (const cRecording *Recording = Recordings->GetByName(cReplayControl::LastReplayed())) return new cMenuRecording(Recording, false); return NULL; } const cRecording *cReplayControl::GetRecording(void) { - if (const cRecording *Recording = Recordings.GetByName(LastReplayed())) + LOCK_RECORDINGS_READ; + if (const cRecording *Recording = Recordings->GetByName(LastReplayed())) return Recording; return NULL; } diff --git a/menu.h b/menu.h index fad27afa..3f2878b3 100644 --- a/menu.h +++ b/menu.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.h 3.8 2015/02/06 09:47:30 kls Exp $ + * $Id: menu.h 4.4 2015/09/13 14:17:56 kls Exp $ */ #ifndef __MENU_H @@ -72,10 +72,13 @@ public: class cMenuEditTimer : public cOsdMenu { private: + static const cTimer *addedTimer; cTimer *timer; cTimer data; int channel; bool addIfConfirmed; + cStringList svdrpServerNames; + char remote[HOST_NAME_MAX]; cMenuEditStrItem *file; cMenuEditDateItem *day; cMenuEditDateItem *firstday; @@ -86,13 +89,14 @@ public: cMenuEditTimer(cTimer *Timer, bool New = false); virtual ~cMenuEditTimer(); virtual eOSState ProcessKey(eKeys Key); + static const cTimer *AddedTimer(void); }; class cMenuEvent : public cOsdMenu { private: const cEvent *event; public: - cMenuEvent(const cEvent *Event, bool CanSwitch = false, bool Buttons = false); + cMenuEvent(const cTimers *Timers, const cChannels *Channels, const cEvent *Event, bool CanSwitch = false, bool Buttons = false); virtual void Display(void); virtual eOSState ProcessKey(eKeys Key); }; @@ -123,14 +127,14 @@ private: bool timeout; int osdState; const cPositioner *positioner; - cChannel *channel; + const cChannel *channel; const cEvent *lastPresent; const cEvent *lastFollowing; static cDisplayChannel *currentDisplayChannel; void DisplayChannel(void); void DisplayInfo(void); void Refresh(void); - cChannel *NextAvailableChannel(cChannel *Channel, int Direction); + const cChannel *NextAvailableChannel(const cChannel *Channel, int Direction); public: cDisplayChannel(int Number, bool Switched); cDisplayChannel(eKeys FirstKey); @@ -205,7 +209,7 @@ class cMenuRecordings : public cOsdMenu { private: char *base; int level; - int recordingsState; + cStateKey recordingsStateKey; int helpKeys; const cRecordingFilter *filter; static cString path; @@ -239,7 +243,7 @@ private: char *fileName; bool GetEvent(void); public: - cRecordControl(cDevice *Device, cTimer *Timer = NULL, bool Pause = false); + cRecordControl(cDevice *Device, cTimers *Timers, cTimer *Timer = NULL, bool Pause = false); virtual ~cRecordControl(); bool Process(time_t t); cDevice *Device(void) { return device; } @@ -254,16 +258,18 @@ private: static cRecordControl *RecordControls[]; static int state; public: - static bool Start(cTimer *Timer = NULL, bool Pause = false); + static bool Start(cTimers *Timers, cTimer *Timer, bool Pause = false); + static bool Start(bool Pause = false); static void Stop(const char *InstantId); + static void Stop(cTimer *Timer); static bool PauseLiveVideo(void); static const char *GetInstantId(const char *LastInstantId); static cRecordControl *GetRecordControl(const char *FileName); static cRecordControl *GetRecordControl(const cTimer *Timer); ///< Returns the cRecordControl for the given Timer. ///< If there is no cRecordControl for Timer, NULL is returned. - static void Process(time_t t); - static void ChannelDataModified(cChannel *Channel); + static bool Process(cTimers *Timers, time_t t); + static void ChannelDataModified(const cChannel *Channel); static bool Active(void); static void Shutdown(void); static void ChangeState(void) { state++; } diff --git a/menuitems.c b/menuitems.c index d0a068d5..659c1a58 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 3.3 2015/02/09 11:53:10 kls Exp $ + * $Id: menuitems.c 4.2 2015/09/08 10:25:23 kls Exp $ */ #include "menuitems.h" @@ -774,10 +774,30 @@ void cMenuEditStraItem::Set(void) SetValue(strings[*value]); } +// --- cMenuEditStrlItem ----------------------------------------------------- + +cMenuEditStrlItem::cMenuEditStrlItem(const char *Name, char *Value, int Length, const cStringList *Strings) +:cMenuEditIntItem(Name, &index, 0, Strings->Size() - 1) +{ + strings = Strings; + value = Value; + length = Length; + index = strings->Find(value); + if (index < 0) + index = 0; + Set(); +} + +void cMenuEditStrlItem::Set(void) +{ + strn0cpy(value, strings->At(index), length); + SetValue(value); +} + // --- cMenuEditChanItem ----------------------------------------------------- cMenuEditChanItem::cMenuEditChanItem(const char *Name, int *Value, const char *NoneString) -:cMenuEditIntItem(Name, Value, NoneString ? 0 : 1, Channels.MaxNumber()) +:cMenuEditIntItem(Name, Value, NoneString ? 0 : 1, cChannels::MaxNumber()) { channelID = NULL; noneString = NoneString; @@ -786,12 +806,13 @@ cMenuEditChanItem::cMenuEditChanItem(const char *Name, int *Value, const char *N } cMenuEditChanItem::cMenuEditChanItem(const char *Name, cString *ChannelID, const char *NoneString) -:cMenuEditIntItem(Name, &dummyValue, NoneString ? 0 : 1, Channels.MaxNumber()) +:cMenuEditIntItem(Name, &dummyValue, NoneString ? 0 : 1, cChannels::MaxNumber()) { channelID = ChannelID; noneString = NoneString; - cChannel *channel = Channels.GetByChannelID(tChannelID::FromString(*ChannelID)); - dummyValue = channel ? channel->Number() : 0; + LOCK_CHANNELS_READ; + const cChannel *Channel = Channels->GetByChannelID(tChannelID::FromString(*ChannelID)); + dummyValue = Channel ? Channel->Number() : 0; Set(); } @@ -799,11 +820,12 @@ void cMenuEditChanItem::Set(void) { if (*value > 0) { char buf[255]; - cChannel *channel = Channels.GetByNumber(*value); - snprintf(buf, sizeof(buf), "%d %s", *value, channel ? channel->Name() : ""); + LOCK_CHANNELS_READ; + const cChannel *Channel = Channels->GetByNumber(*value); + snprintf(buf, sizeof(buf), "%d %s", *value, Channel ? Channel->Name() : ""); SetValue(buf); if (channelID) - *channelID = channel ? channel->GetChannelID().ToString() : ""; + *channelID = Channel ? Channel->GetChannelID().ToString() : ""; } else if (noneString) { SetValue(noneString); @@ -822,13 +844,14 @@ eOSState cMenuEditChanItem::ProcessKey(eKeys Key) case kRight|k_Repeat: case kRight: { - cChannel *channel = Channels.GetByNumber(*value + delta, delta); - if (channel) - *value = channel->Number(); + LOCK_CHANNELS_READ + const cChannel *Channel = Channels->GetByNumber(*value + delta, delta); + if (Channel) + *value = Channel->Number(); else if (delta < 0 && noneString) *value = 0; if (channelID) - *channelID = channel ? channel->GetChannelID().ToString() : ""; + *channelID = Channel ? Channel->GetChannelID().ToString() : ""; Set(); } break; @@ -845,13 +868,14 @@ cMenuEditTranItem::cMenuEditTranItem(const char *Name, int *Value, int *Source) number = 0; source = Source; transponder = Value; - cChannel *channel = Channels.First(); - while (channel) { - if (!channel->GroupSep() && *source == channel->Source() && ISTRANSPONDER(channel->Transponder(), *Value)) { - number = channel->Number(); + LOCK_CHANNELS_READ; + const cChannel *Channel = Channels->First(); + while (Channel) { + if (!Channel->GroupSep() && *source == Channel->Source() && ISTRANSPONDER(Channel->Transponder(), *Value)) { + number = Channel->Number(); break; } - channel = (cChannel *)channel->Next(); + Channel = Channels->Next(Channel); } Set(); } @@ -859,10 +883,10 @@ cMenuEditTranItem::cMenuEditTranItem(const char *Name, int *Value, int *Source) eOSState cMenuEditTranItem::ProcessKey(eKeys Key) { eOSState state = cMenuEditChanItem::ProcessKey(Key); - cChannel *channel = Channels.GetByNumber(number); - if (channel) { - *source = channel->Source(); - *transponder = channel->Transponder(); + LOCK_CHANNELS_READ + if (const cChannel *Channel = Channels->GetByNumber(number)) { + *source = Channel->Source(); + *transponder = Channel->Transponder(); } else { *source = 0; diff --git a/menuitems.h b/menuitems.h index 2a30b465..44f6fec5 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 3.1 2013/05/24 10:19:55 kls Exp $ + * $Id: menuitems.h 4.1 2015/09/06 10:38:37 kls Exp $ */ #ifndef __MENUITEMS_H @@ -146,6 +146,18 @@ public: cMenuEditStraItem(const char *Name, int *Value, int NumStrings, const char * const *Strings); }; +class cMenuEditStrlItem : public cMenuEditIntItem { +private: + const cStringList *strings; + int index; + char *value; + int length; +protected: + virtual void Set(void); +public: + cMenuEditStrlItem(const char *Name, char *Value, int Length, const cStringList *Strings); + }; + class cMenuEditChanItem : public cMenuEditIntItem { protected: const char *noneString; diff --git a/newplugin b/newplugin index 8f7087e9..d8e9243c 100755 --- a/newplugin +++ b/newplugin @@ -12,7 +12,7 @@ # See the main source file 'vdr.c' for copyright information and # how to reach the author. # -# $Id: newplugin 3.2 2014/01/01 13:29:54 kls Exp $ +# $Id: newplugin 4.1 2015/09/10 11:11:14 kls Exp $ $PLUGIN_NAME = $ARGV[0] || die "Usage: newplugin \n"; @@ -330,6 +330,7 @@ $PLUGINDIR = "$PLUGINS_SRC/$PLUGIN_NAME"; die "The directory $PLUGINS_SRC doesn't exist!\n" unless (-d "$PLUGINS_SRC"); die "A plugin named '$PLUGIN_NAME' already exists in $PLUGINS_SRC!\n" if (-e "$PLUGINDIR"); mkdir("$PLUGINDIR") || die "$!"; +mkdir("$PLUGINDIR/po") || die "$!"; CreateFile("README", $README); CreateFile("HISTORY", $HISTORY); diff --git a/nit.c b/nit.c index 13183755..864fdac2 100644 --- a/nit.c +++ b/nit.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: nit.c 3.5 2015/02/04 09:13:54 kls Exp $ + * $Id: nit.c 4.3 2015/07/26 09:24:36 kls Exp $ */ #include "nit.h" @@ -19,19 +19,22 @@ #define DVB_SYSTEM_1 0 // see also dvbdevice.c #define DVB_SYSTEM_2 1 +#define MAXNETWORKNAME Utf8BufSize(256) + +// Set to 'true' for debug output: +static bool DebugNit = false; + +#define dbgnit(a...) if (DebugNit) fprintf(stderr, a) + cNitFilter::cNitFilter(cSdtFilter *SdtFilter) { sdtFilter = SdtFilter; - numNits = 0; - networkId = 0; - Set(0x10, 0x40); // NIT + Set(0x10, SI::TableIdNIT); } void cNitFilter::SetStatus(bool On) { cFilter::SetStatus(On); - numNits = 0; - networkId = 0; sectionSyncer.Reset(); } @@ -40,63 +43,31 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length SI::NIT nit(Data, false); if (!nit.CheckCRCAndParse()) return; - // Some broadcasters send more than one NIT, with no apparent way of telling which - // one is the right one to use. This is an attempt to find the NIT that contains - // the transponder it was transmitted on and use only that one: - int ThisNIT = -1; - if (!networkId) { - for (int i = 0; i < numNits; i++) { - if (nits[i].networkId == nit.getNetworkId()) { - if (nit.getSectionNumber() == 0) { - // all NITs have passed by - for (int j = 0; j < numNits; j++) { - if (nits[j].hasTransponder) { - networkId = nits[j].networkId; - //printf("taking NIT with network ID %d\n", networkId); - //XXX what if more than one NIT contains this transponder??? - break; - } - } - if (!networkId) { - //printf("none of the NITs contains transponder %d\n", Transponder()); - return; - } - } - else { - ThisNIT = i; - break; - } - } - } - if (!networkId && ThisNIT < 0 && numNits < MAXNITS) { - if (nit.getSectionNumber() == 0) { - *nits[numNits].name = 0; - SI::Descriptor *d; - for (SI::Loop::Iterator it; (d = nit.commonDescriptors.getNext(it)); ) { - switch (d->getDescriptorTag()) { - case SI::NetworkNameDescriptorTag: { - SI::NetworkNameDescriptor *nnd = (SI::NetworkNameDescriptor *)d; - nnd->name.getText(nits[numNits].name, MAXNETWORKNAME); - } - break; - default: ; - } - delete d; - } - nits[numNits].networkId = nit.getNetworkId(); - nits[numNits].hasTransponder = false; - //printf("NIT[%d] %5d '%s'\n", numNits, nits[numNits].networkId, nits[numNits].name); - ThisNIT = numNits; - numNits++; + if (!sectionSyncer.Sync(nit.getVersionNumber(), nit.getSectionNumber(), nit.getLastSectionNumber())) + return; + if (DebugNit) { + char NetworkName[MAXNETWORKNAME] = ""; + SI::Descriptor *d; + for (SI::Loop::Iterator it; (d = nit.commonDescriptors.getNext(it)); ) { + switch (d->getDescriptorTag()) { + case SI::NetworkNameDescriptorTag: { + SI::NetworkNameDescriptor *nnd = (SI::NetworkNameDescriptor *)d; + nnd->name.getText(NetworkName, MAXNETWORKNAME); + } + break; + default: ; } - } + delete d; + } + dbgnit("NIT: %02X %2d %2d %2d %s %d %d '%s'\n", Tid, nit.getVersionNumber(), nit.getSectionNumber(), nit.getLastSectionNumber(), *cSource::ToString(Source()), nit.getNetworkId(), Transponder(), NetworkName); } - else if (networkId != nit.getNetworkId()) - return; // ignore all other NITs - else if (!sectionSyncer.Sync(nit.getVersionNumber(), nit.getSectionNumber(), nit.getLastSectionNumber())) - return; - if (!Channels.Lock(true, 10)) + cStateKey StateKey; + cChannels *Channels = cChannels::GetChannelsWrite(StateKey, 10); + if (!Channels) { + sectionSyncer.Repeat(); // let's not miss any section of the NIT return; + } + bool ChannelsModified = false; SI::NIT::TransportStream ts; for (SI::Loop::Iterator it; nit.transportStreamLoop.getNext(ts, it); ) { SI::Descriptor *d; @@ -118,6 +89,7 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length default: ; } Frequencies[n++] = f; + dbgnit(" Frequencies[%d] = %d\n", n - 1, f); } } else @@ -142,20 +114,11 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length static int RollOffs[] = { ROLLOFF_35, ROLLOFF_25, ROLLOFF_20, ROLLOFF_AUTO }; dtp.SetRollOff(sd->getModulationSystem() ? RollOffs[sd->getRollOff()] : ROLLOFF_AUTO); int SymbolRate = BCD2INT(sd->getSymbolRate()) / 10; - if (ThisNIT >= 0) { - for (int n = 0; n < NumFrequencies; n++) { - if (ISTRANSPONDER(cChannel::Transponder(Frequencies[n], dtp.Polarization()), Transponder())) { - nits[ThisNIT].hasTransponder = true; - //printf("has transponder %d\n", Transponder()); - break; - } - } - break; - } + dbgnit(" %s %d %c %d %d\n", *cSource::ToString(Source), Frequency, Polarizations[sd->getPolarization()], SymbolRate, cChannel::Transponder(Frequency, Polarizations[sd->getPolarization()])); if (Setup.UpdateChannels >= 5) { bool found = false; bool forceTransponderUpdate = false; - for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { + for (cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) { if (!Channel->GroupSep() && Channel->Source() == Source && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) { int transponder = Channel->Transponder(); found = true; @@ -168,7 +131,7 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length } } if (ISTRANSPONDER(cChannel::Transponder(Frequency, dtp.Polarization()), Transponder())) // only modify channels if we're actually receiving this transponder - Channel->SetTransponderData(Source, Frequency, SymbolRate, dtp.ToString('S')); + ChannelsModified |= Channel->SetTransponderData(Source, Frequency, SymbolRate, dtp.ToString('S')); else if (Channel->Srate() != SymbolRate || strcmp(Channel->Parameters(), dtp.ToString('S'))) forceTransponderUpdate = true; // get us receiving this transponder } @@ -176,7 +139,7 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length if (!found || forceTransponderUpdate) { for (int n = 0; n < NumFrequencies; n++) { cChannel *Channel = new cChannel; - Channel->SetId(ts.getOriginalNetworkId(), ts.getTransportStreamId(), 0, 0); + Channel->SetId(NULL, ts.getOriginalNetworkId(), ts.getTransportStreamId(), 0, 0); if (Channel->SetTransponderData(Source, Frequencies[n], SymbolRate, dtp.ToString('S'))) EITScanner.AddTransponder(Channel); else @@ -184,18 +147,19 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length } } } - sdtFilter->Trigger(Source); + if (ISTRANSPONDER(cChannel::Transponder(Frequency, dtp.Polarization()), Transponder())) + sdtFilter->Trigger(Source); } break; case SI::S2SatelliteDeliverySystemDescriptorTag: { if (Setup.UpdateChannels >= 5) { - for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { + for (cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) { if (!Channel->GroupSep() && cSource::IsSat(Channel->Source()) && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) { SI::S2SatelliteDeliverySystemDescriptor *sd = (SI::S2SatelliteDeliverySystemDescriptor *)d; cDvbTransponderParameters dtp(Channel->Parameters()); dtp.SetSystem(DVB_SYSTEM_2); dtp.SetStreamId(sd->getInputStreamIdentifier()); - Channel->SetTransponderData(Channel->Source(), Channel->Frequency(), Channel->Srate(), dtp.ToString('S')); + ChannelsModified |= Channel->SetTransponderData(Channel->Source(), Channel->Frequency(), Channel->Srate(), dtp.ToString('S')); break; } } @@ -213,20 +177,11 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length static int Modulations[] = { QPSK, QAM_16, QAM_32, QAM_64, QAM_128, QAM_256, QAM_AUTO }; dtp.SetModulation(Modulations[min(sd->getModulation(), 6)]); int SymbolRate = BCD2INT(sd->getSymbolRate()) / 10; - if (ThisNIT >= 0) { - for (int n = 0; n < NumFrequencies; n++) { - if (ISTRANSPONDER(Frequencies[n] / 1000, Transponder())) { - nits[ThisNIT].hasTransponder = true; - //printf("has transponder %d\n", Transponder()); - break; - } - } - break; - } + dbgnit(" %s %d %d %d %d\n", *cSource::ToString(Source), Frequency, CodeRates[sd->getFecInner()], Modulations[min(sd->getModulation(), 6)], SymbolRate); if (Setup.UpdateChannels >= 5) { bool found = false; bool forceTransponderUpdate = false; - for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { + for (cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) { if (!Channel->GroupSep() && Channel->Source() == Source && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) { int transponder = Channel->Transponder(); found = true; @@ -239,7 +194,7 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length } } if (ISTRANSPONDER(Frequency / 1000, Transponder())) // only modify channels if we're actually receiving this transponder - Channel->SetTransponderData(Source, Frequency, SymbolRate, dtp.ToString('C')); + ChannelsModified |= Channel->SetTransponderData(Source, Frequency, SymbolRate, dtp.ToString('C')); else if (Channel->Srate() != SymbolRate || strcmp(Channel->Parameters(), dtp.ToString('C'))) forceTransponderUpdate = true; // get us receiving this transponder } @@ -247,7 +202,7 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length if (!found || forceTransponderUpdate) { for (int n = 0; n < NumFrequencies; n++) { cChannel *Channel = new cChannel; - Channel->SetId(ts.getOriginalNetworkId(), ts.getTransportStreamId(), 0, 0); + Channel->SetId(NULL, ts.getOriginalNetworkId(), ts.getTransportStreamId(), 0, 0); if (Channel->SetTransponderData(Source, Frequencies[n], SymbolRate, dtp.ToString('C'))) EITScanner.AddTransponder(Channel); else @@ -255,7 +210,8 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length } } } - sdtFilter->Trigger(Source); + if (ISTRANSPONDER(Frequency / 1000, Transponder())) + sdtFilter->Trigger(Source); } break; case SI::TerrestrialDeliverySystemDescriptorTag: { @@ -277,20 +233,11 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length dtp.SetGuard(GuardIntervals[sd->getGuardInterval()]); static int TransmissionModes[] = { TRANSMISSION_MODE_2K, TRANSMISSION_MODE_8K, TRANSMISSION_MODE_4K, TRANSMISSION_MODE_AUTO }; dtp.SetTransmission(TransmissionModes[sd->getTransmissionMode()]); - if (ThisNIT >= 0) { - for (int n = 0; n < NumFrequencies; n++) { - if (ISTRANSPONDER(Frequencies[n] / 1000000, Transponder())) { - nits[ThisNIT].hasTransponder = true; - //printf("has transponder %d\n", Transponder()); - break; - } - } - break; - } + dbgnit(" %s %d %d %d %d %d %d %d %d\n", *cSource::ToString(Source), Frequency, Bandwidths[sd->getBandwidth()], Constellations[sd->getConstellation()], Hierarchies[sd->getHierarchy()], CodeRates[sd->getCodeRateHP()], CodeRates[sd->getCodeRateLP()], GuardIntervals[sd->getGuardInterval()], TransmissionModes[sd->getTransmissionMode()]); if (Setup.UpdateChannels >= 5) { bool found = false; bool forceTransponderUpdate = false; - for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { + for (cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) { if (!Channel->GroupSep() && Channel->Source() == Source && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) { int transponder = Channel->Transponder(); found = true; @@ -303,7 +250,7 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length } } if (ISTRANSPONDER(Frequency / 1000000, Transponder())) // only modify channels if we're actually receiving this transponder - Channel->SetTransponderData(Source, Frequency, 0, dtp.ToString('T')); + ChannelsModified |= Channel->SetTransponderData(Source, Frequency, 0, dtp.ToString('T')); else if (strcmp(Channel->Parameters(), dtp.ToString('T'))) forceTransponderUpdate = true; // get us receiving this transponder } @@ -311,7 +258,7 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length if (!found || forceTransponderUpdate) { for (int n = 0; n < NumFrequencies; n++) { cChannel *Channel = new cChannel; - Channel->SetId(ts.getOriginalNetworkId(), ts.getTransportStreamId(), 0, 0); + Channel->SetId(NULL, ts.getOriginalNetworkId(), ts.getTransportStreamId(), 0, 0); if (Channel->SetTransponderData(Source, Frequencies[n], 0, dtp.ToString('T'))) EITScanner.AddTransponder(Channel); else @@ -319,7 +266,8 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length } } } - sdtFilter->Trigger(Source); + if (ISTRANSPONDER(Frequency / 1000000, Transponder())) + sdtFilter->Trigger(Source); } break; case SI::ExtensionDescriptorTag: { @@ -327,7 +275,7 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length switch (sd->getExtensionDescriptorTag()) { case SI::T2DeliverySystemDescriptorTag: { if (Setup.UpdateChannels >= 5) { - for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { + for (cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) { int Source = cSource::FromData(cSource::stTerr); if (!Channel->GroupSep() && Channel->Source() == Source && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) { SI::T2DeliverySystemDescriptor *td = (SI::T2DeliverySystemDescriptor *)d; @@ -347,7 +295,7 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length dtp.SetTransmission(T2TransmissionModes[td->getTransmissionMode()]); //TODO add parsing of frequencies } - Channel->SetTransponderData(Source, Frequency, SymbolRate, dtp.ToString('T')); + ChannelsModified |= Channel->SetTransponderData(Source, Frequency, SymbolRate, dtp.ToString('T')); } } } @@ -365,9 +313,9 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length int lcn = LogicalChannel.getLogicalChannelNumber(); int sid = LogicalChannel.getServiceId(); if (LogicalChannel.getVisibleServiceFlag()) { - for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { + for (cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) { if (!Channel->GroupSep() && Channel->Sid() == sid && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) { - Channel->SetLcn(lcn); + ChannelsModified |= Channel->SetLcn(lcn); break; } } @@ -383,9 +331,9 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length int lcn = HdSimulcastLogicalChannel.getLogicalChannelNumber(); int sid = HdSimulcastLogicalChannel.getServiceId(); if (HdSimulcastLogicalChannel.getVisibleServiceFlag()) { - for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { + for (cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) { if (!Channel->GroupSep() && Channel->Sid() == sid && Channel->Nid() == ts.getOriginalNetworkId() && Channel->Tid() == ts.getTransportStreamId()) { - Channel->SetLcn(lcn); + ChannelsModified |= Channel->SetLcn(lcn); break; } } @@ -398,5 +346,5 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length delete d; } } - Channels.Unlock(); + StateKey.Remove(ChannelsModified); } diff --git a/nit.h b/nit.h index 4673f960..52676c0d 100644 --- a/nit.h +++ b/nit.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: nit.h 3.1 2014/03/10 14:12:05 kls Exp $ + * $Id: nit.h 4.1 2015/03/16 12:41:38 kls Exp $ */ #ifndef __NIT_H @@ -13,24 +13,10 @@ #include "filter.h" #include "sdt.h" -#define MAXNITS 16 -#define MAXNETWORKNAME Utf8BufSize(256) - class cNitFilter : public cFilter { private: - - class cNit { - public: - u_short networkId; - char name[MAXNETWORKNAME]; - bool hasTransponder; - }; - cSectionSyncer sectionSyncer; cSdtFilter *sdtFilter; - cNit nits[MAXNITS]; - u_short networkId; - int numNits; protected: virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length); public: diff --git a/osd.c b/osd.c index a475488e..524700ae 100644 --- a/osd.c +++ b/osd.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osd.c 3.5 2015/02/11 09:48:02 kls Exp $ + * $Id: osd.c 4.3 2015/09/10 14:12:06 kls Exp $ */ #include "osd.h" @@ -1641,6 +1641,7 @@ int cOsd::osdLeft = 0; int cOsd::osdTop = 0; int cOsd::osdWidth = 0; int cOsd::osdHeight = 0; +cSize cOsd::maxPixmapSize(2048, 2048); cVector cOsd::Osds; cMutex cOsd::mutex; @@ -1705,6 +1706,11 @@ cBitmap *cOsd::GetBitmap(int Area) return Area < numBitmaps ? (isTrueColor ? bitmaps[0] : bitmaps[Area]) : NULL; } +const cSize &cOsd::MaxPixmapSize(void) const +{ + return maxPixmapSize; +} + cPixmap *cOsd::CreatePixmap(int Layer, const cRect &ViewPort, const cRect &DrawPort) { if (isTrueColor) { @@ -1836,7 +1842,10 @@ eOsdError cOsd::SetAreas(const tArea *Areas, int NumAreas) width = Areas[0].x2 - Areas[0].x1 + 1; height = Areas[0].y2 - Areas[0].y1 + 1; cPixmap *Pixmap = CreatePixmap(0, cRect(Areas[0].x1, Areas[0].y1, width, height)); - Pixmap->Clear(); + if (Pixmap) + Pixmap->Clear(); + else + Result = oeOutOfMemory; bitmaps[numBitmaps++] = new cBitmap(10, 10, 8); // dummy bitmap for GetBitmap() } else { @@ -2033,8 +2042,8 @@ void cOsdProvider::UpdateOsdSize(bool Force) if (Width != oldWidth || Height != oldHeight || !DoubleEqual(Aspect, oldAspect) || Force) { Setup.OSDLeft = int(round(Width * Setup.OSDLeftP)); Setup.OSDTop = int(round(Height * Setup.OSDTopP)); - Setup.OSDWidth = int(round(Width * Setup.OSDWidthP)) & ~0x07; // OSD width must be a multiple of 8 - Setup.OSDHeight = int(round(Height * Setup.OSDHeightP)); + Setup.OSDWidth = min(Width - Setup.OSDLeft, int(round(Width * Setup.OSDWidthP))) & ~0x07; // OSD width must be a multiple of 8 + Setup.OSDHeight = min(Height - Setup.OSDTop, int(round(Height * Setup.OSDHeightP))); Setup.OSDAspect = Aspect; Setup.FontOsdSize = int(round(Height * Setup.FontOsdSizeP)); Setup.FontFixSize = int(round(Height * Setup.FontFixSizeP)); diff --git a/osd.h b/osd.h index 9ef32ac7..61b318d1 100644 --- a/osd.h +++ b/osd.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osd.h 3.6 2015/02/11 09:48:02 kls Exp $ + * $Id: osd.h 4.4 2015/04/19 12:18:25 kls Exp $ */ #ifndef __OSD_H @@ -510,8 +510,10 @@ public: ///< In order to allow devices that can handle only a limited number of layers, ///< the Layer parameter must be less than 8 (MAXPIXMAPLAYERS). ///< ViewPort defines the rectangle in which this pixmap will be rendered on - ///< the OSD. If no DrawPort is given, it defaults to the same size as the - ///< ViewPort, with its upper left corner set to (0, 0). + ///< the OSD. The coordinate (0, 0) corresponds to the upper left corner of the + ///< OSD. If no DrawPort is given, it defaults to the same size as the + ///< ViewPort, with its upper left corner set to (0, 0). The DrawPort's origin + ///< is relative to the ViewPort's origin. ///< All drawing operations will be executed relative to the origin of the ///< DrawPort rectangle, and will be clipped to the size of this rectangle. ///< The DrawPort may have a different size than the ViewPort. If it is smaller @@ -522,6 +524,8 @@ public: ///< intersects with the ViewPort will be visible on the OSD. ///< The drawing area of a newly created cPixmap is not initialized and may ///< contain random data. + ///< See cOsd::MaxPixmapSize() for information on the maximum size of pixmaps + ///< supported by the system. static void Lock(void) { mutex.Lock(); } ///< All public member functions of cPixmap set locks as necessary to make sure ///< they are thread-safe (unless noted otherwise). If several cPixmap member @@ -722,6 +726,7 @@ class cOsd { private: static int osdLeft, osdTop, osdWidth, osdHeight; static cVector Osds; + static cSize maxPixmapSize; static cMutex mutex; bool isTrueColor; cBitmap *savedBitmap; @@ -780,6 +785,18 @@ protected: ///< If there are no dirty pixmaps, or if this is not a true color OSD, ///< this function returns NULL. ///< The caller must call DestroyPixmap() for the returned pixmap after use. +//#define DEPRECATED_GETBITMAP +#ifdef DEPRECATED_GETBITMAP +public: +#endif + cBitmap *GetBitmap(int Area); + ///< Returns a pointer to the bitmap for the given Area, or NULL if no + ///< such bitmap exists. + ///< If this is a true color OSD, a pointer to a dummy bitmap with 8bpp + ///< is returned. This is done so that skins that call this function + ///< in order to preset the bitmap's palette won't crash. + ///< Use of this function outside of derived classes is deprecated and it + ///< may be made 'protected' in a future version. public: virtual ~cOsd(); ///< Shuts down the OSD. @@ -812,14 +829,8 @@ public: ///< requested colors. By default the palette assumes there will be ///< 10 fixed colors and 10 color combinations. ///< If this is a true color OSD, this function does nothing. - cBitmap *GetBitmap(int Area); - ///< Returns a pointer to the bitmap for the given Area, or NULL if no - ///< such bitmap exists. - ///< If this is a true color OSD, a pointer to a dummy bitmap with 8bpp - ///< is returned. This is done so that skins that call this function - ///< in order to preset the bitmap's palette won't crash. - ///< Use of this function outside of derived classes is deprecated and it - ///< may be made 'protected' in a future version. + virtual const cSize &MaxPixmapSize(void) const; + ///< Returns the maximum possible size of a pixmap this OSD can create. virtual cPixmap *CreatePixmap(int Layer, const cRect &ViewPort, const cRect &DrawPort = cRect::Null); ///< Creates a new true color pixmap on this OSD (see cPixmap for details). ///< The caller must not delete the returned object, it will be deleted when diff --git a/osdbase.c b/osdbase.c index e03f2d07..52ab240e 100644 --- a/osdbase.c +++ b/osdbase.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osdbase.c 3.3 2015/01/15 10:11:11 kls Exp $ + * $Id: osdbase.c 4.1 2015/09/10 11:23:07 kls Exp $ */ #include "osdbase.h" @@ -87,6 +87,7 @@ cOsdMenu::cOsdMenu(const char *Title, int c0, int c1, int c2, int c3, int c4) title = NULL; menuCategory = mcUnknown; menuSortMode = msmUnknown; + menuOrientation = moVertical; SetTitle(Title); SetCols(c0, c1, c2, c3, c4); first = 0; @@ -231,6 +232,7 @@ void cOsdMenu::Display(void) if (menuCategory != displayMenu->MenuCategory()) displayMenu->SetMenuCategory(menuCategory); displayMenu->SetMenuSortMode(menuSortMode); + menuOrientation = displayMenu->MenuOrientation(); displayMenuItems = displayMenu->MaxItems(); displayMenu->SetTabs(cols[0], cols[1], cols[2], cols[3], cols[4]);//XXX displayMenu->SetTitle(title); @@ -541,13 +543,13 @@ eOSState cOsdMenu::ProcessKey(eKeys Key) case k0: return osUnknown; case k1...k9: return hasHotkeys ? HotKey(Key) : osUnknown; case kUp|k_Repeat: - case kUp: CursorUp(); break; + case kUp: if (menuOrientation == moHorizontal) PageUp(); else CursorUp(); break; case kDown|k_Repeat: - case kDown: CursorDown(); break; + case kDown: if (menuOrientation == moHorizontal) PageDown(); else CursorDown(); break; case kLeft|k_Repeat: - case kLeft: PageUp(); break; + case kLeft: if (menuOrientation == moHorizontal) CursorUp(); else PageUp(); break; case kRight|k_Repeat: - case kRight: PageDown(); break; + case kRight: if (menuOrientation == moHorizontal) CursorDown(); else PageDown(); break; case kBack: return osBack; case kOk: if (marked >= 0) { SetStatus(NULL); diff --git a/osdbase.h b/osdbase.h index 5b8ae1cc..5e807e76 100644 --- a/osdbase.h +++ b/osdbase.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osdbase.h 3.2 2015/01/15 10:09:18 kls Exp $ + * $Id: osdbase.h 4.1 2015/09/10 11:17:52 kls Exp $ */ #ifndef __OSDBASE_H @@ -93,6 +93,7 @@ private: int first, current, marked; eMenuCategory menuCategory; eMenuSortMode menuSortMode; + eMenuOrientation menuOrientation; cOsdMenu *subMenu; const char *helpRed, *helpGreen, *helpYellow, *helpBlue; bool helpDisplayed; diff --git a/pat.c b/pat.c index 98d306eb..beb5609a 100644 --- a/pat.c +++ b/pat.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: pat.c 3.5 2015/01/04 13:14:01 kls Exp $ + * $Id: pat.c 4.1 2015/08/17 08:46:55 kls Exp $ */ #include "pat.h" @@ -99,8 +99,8 @@ cCaDescriptors::cCaDescriptors(int Source, int Transponder, int ServiceId, int P bool cCaDescriptors::operator== (const cCaDescriptors &arg) const { - cCaDescriptor *ca1 = caDescriptors.First(); - cCaDescriptor *ca2 = arg.caDescriptors.First(); + const cCaDescriptor *ca1 = caDescriptors.First(); + const cCaDescriptor *ca2 = arg.caDescriptors.First(); while (ca1 && ca2) { if (!(*ca1 == *ca2)) return false; @@ -396,11 +396,14 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length SwitchToNextPmtPid(); return; } - if (!Channels.Lock(true, 10)) + cStateKey StateKey; + cChannels *Channels = cChannels::GetChannelsWrite(StateKey, 10); + if (!Channels) return; + bool ChannelsModified = false; PmtVersionChanged(Pid, pmt.getTableIdExtension(), pmt.getVersionNumber(), true); SwitchToNextPmtPid(); - cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), pmt.getServiceId()); + cChannel *Channel = Channels->GetByServiceID(Source(), Transponder(), pmt.getServiceId()); if (Channel) { SI::CaDescriptor *d; cCaDescriptors *CaDescriptors = new cCaDescriptors(Channel->Source(), Channel->Transponder(), Channel->Sid(), Pid); @@ -629,13 +632,13 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length } } if (Setup.UpdateChannels >= 2) { - Channel->SetPids(Vpid, Ppid, Vtype, Apids, Atypes, ALangs, Dpids, Dtypes, DLangs, Spids, SLangs, Tpid); - Channel->SetCaIds(CaDescriptors->CaIds()); - Channel->SetSubtitlingDescriptors(SubtitlingTypes, CompositionPageIds, AncillaryPageIds); + ChannelsModified |= Channel->SetPids(Vpid, Ppid, Vtype, Apids, Atypes, ALangs, Dpids, Dtypes, DLangs, Spids, SLangs, Tpid); + ChannelsModified |= Channel->SetCaIds(CaDescriptors->CaIds()); + ChannelsModified |= Channel->SetSubtitlingDescriptors(SubtitlingTypes, CompositionPageIds, AncillaryPageIds); } - Channel->SetCaDescriptors(CaDescriptorHandler.AddCaDescriptors(CaDescriptors)); + ChannelsModified |= Channel->SetCaDescriptors(CaDescriptorHandler.AddCaDescriptors(CaDescriptors)); } - Channels.Unlock(); + StateKey.Remove(ChannelsModified); } if (timer.TimedOut()) { if (pmtIndex >= 0) diff --git a/pat.h b/pat.h index 19e60dcf..557d599c 100644 --- a/pat.h +++ b/pat.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: pat.h 3.4 2015/01/04 13:17:22 kls Exp $ + * $Id: pat.h 4.0 2015/01/04 13:17:22 kls Exp $ */ #ifndef __PAT_H diff --git a/player.c b/player.c index 5fc4dbf4..e10b3d01 100644 --- a/player.c +++ b/player.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: player.c 3.0 2012/04/28 11:52:50 kls Exp $ + * $Id: player.c 4.0 2012/04/28 11:52:50 kls Exp $ */ #include "player.h" diff --git a/player.h b/player.h index 9ec47faa..d9ed964a 100644 --- a/player.h +++ b/player.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: player.h 3.1 2013/12/25 13:25:02 kls Exp $ + * $Id: player.h 4.0 2013/12/25 13:25:02 kls Exp $ */ #ifndef __PLAYER_H diff --git a/plugin.c b/plugin.c index 650f9231..223d709d 100644 --- a/plugin.c +++ b/plugin.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: plugin.c 3.0 2012/09/01 13:10:27 kls Exp $ + * $Id: plugin.c 4.1 2015/04/18 14:51:20 kls Exp $ */ #include "plugin.h" @@ -318,23 +318,23 @@ void cPluginManager::SetDirectory(const char *Directory) void cPluginManager::AddPlugin(const char *Args) { if (strcmp(Args, "*") == 0) { - cReadDir d(directory); - struct dirent *e; - while ((e = d.Next()) != NULL) { - if (strstr(e->d_name, LIBVDR_PREFIX) == e->d_name) { - char *p = strstr(e->d_name, SO_INDICATOR); - if (p) { - *p = 0; - p += strlen(SO_INDICATOR); - if (strcmp(p, APIVERSION) == 0) { - char *name = e->d_name + strlen(LIBVDR_PREFIX); - if (strcmp(name, "*") != 0) { // let's not get into a loop! - AddPlugin(e->d_name + strlen(LIBVDR_PREFIX)); - } - } - } - } - } + cFileNameList Files(directory); + for (int i = 0; i < Files.Size(); i++) { + char *FileName = Files.At(i); + if (strstr(FileName, LIBVDR_PREFIX) == FileName) { + char *p = strstr(FileName, SO_INDICATOR); + if (p) { + *p = 0; + p += strlen(SO_INDICATOR); + if (strcmp(p, APIVERSION) == 0) { + char *name = FileName + strlen(LIBVDR_PREFIX); + if (strcmp(name, "*") != 0) { // let's not get into a loop! + AddPlugin(FileName + strlen(LIBVDR_PREFIX)); + } + } + } + } + } return; } char *s = strdup(skipspace(Args)); diff --git a/plugin.h b/plugin.h index d04274a8..fde78b55 100644 --- a/plugin.h +++ b/plugin.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: plugin.h 3.0 2012/09/01 13:08:54 kls Exp $ + * $Id: plugin.h 4.0 2012/09/01 13:08:54 kls Exp $ */ #ifndef __PLUGIN_H diff --git a/po/ar.po b/po/ar.po index d26ccd9e..b8221b4f 100644 --- a/po/ar.po +++ b/po/ar.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2008-10-16 11:16-0400\n" "Last-Translator: Osama Alrawab \n" "Language-Team: Arabic \n" @@ -664,6 +664,9 @@ msgstr "معدل البقاء" msgid "File" msgstr "ملف" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "Folder" @@ -676,6 +679,12 @@ msgstr "Repeating" msgid "First day" msgstr "اليوم الاول" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "Select folder" @@ -851,6 +860,12 @@ msgstr "متوقف على نوع الثوب" msgid "always" msgstr "دائما" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "عرص على الشاشة" @@ -938,6 +953,9 @@ msgstr "Folders in timer menu" msgid "Setup.OSD$Always sort folders first" msgstr "قم دائما بسرد المجلدات اولا" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "Number keys for characters" @@ -1122,6 +1140,9 @@ msgstr "الكامة جاهزة" msgid " (activating)" msgstr "" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "الكامة" @@ -1152,6 +1173,15 @@ msgstr "الكامة مستخدمة الان هل تريد اعادة تشغيل msgid "Can't reset CAM!" msgstr "تعذر اعادة تشغيل الكامة" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "لا تقم بايقاف الفديو الحى" @@ -1182,6 +1212,9 @@ msgstr "الاولويات الافتراضية" msgid "Setup.Recording$Default lifetime (d)" msgstr "معدل البقاء باليوم" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "Pause key handling" @@ -1281,6 +1314,15 @@ msgstr "اقل مدة لعدم تفاعل المستخدم بالدقيقة" msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "SVDRP timeout (s)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "Zap timeout (s)" @@ -1534,6 +1576,9 @@ msgstr "التسجيل القادم" msgid "Pause live video?" msgstr "ايقاف الفديو الحى" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "ابتداء التسجيل" diff --git a/po/ca_ES.po b/po/ca_ES.po index 8ea7bd3d..e251db25 100644 --- a/po/ca_ES.po +++ b/po/ca_ES.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2008-03-02 19:02+0100\n" "Last-Translator: Luca Olivetti \n" "Language-Team: Catalan \n" @@ -663,6 +663,9 @@ msgstr "Durada" msgid "File" msgstr "Arxiu" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "Carpeta" @@ -675,6 +678,12 @@ msgstr "Repetitiu" msgid "First day" msgstr "Primer dia" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "Seleccioni carpeta" @@ -850,6 +859,12 @@ msgstr "dep msgid "always" msgstr "sempre" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "Informaci en pantalla" @@ -937,6 +952,9 @@ msgstr "Carpetes en men msgid "Setup.OSD$Always sort folders first" msgstr "Sempre ordenar primer carpetes" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "Tecles numriques per a carcters" @@ -1121,6 +1139,9 @@ msgstr "CAM preparat" msgid " (activating)" msgstr "" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "CAM" @@ -1151,6 +1172,15 @@ msgstr "CAM en msgid "Can't reset CAM!" msgstr "No puc reiniciar la CAM!" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "no pausar emissi en directe" @@ -1181,6 +1211,9 @@ msgstr "Prioritat per defecte" msgid "Setup.Recording$Default lifetime (d)" msgstr "Durada predefinida" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "Gesti tecla pausa" @@ -1280,6 +1313,15 @@ msgstr "Temps m msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "SVDRP Timeout (s)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "Temps d'espera Zap (s)" @@ -1533,6 +1575,9 @@ msgstr "Gravaci msgid "Pause live video?" msgstr "Pausar emissi en directe?" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "Gravaci comenada" diff --git a/po/cs_CZ.po b/po/cs_CZ.po index 6c5ac1b9..541ee353 100644 --- a/po/cs_CZ.po +++ b/po/cs_CZ.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2010-05-06 11:00+0200\n" "Last-Translator: Aleš Juřík \n" "Language-Team: Czech \n" @@ -663,6 +663,9 @@ msgstr "Životnost" msgid "File" msgstr "Soubor" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "Složka" @@ -675,6 +678,12 @@ msgstr "S opakováním" msgid "First day" msgstr "První den" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "Vybrat složku" @@ -850,6 +859,12 @@ msgstr "podle vzhledu" msgid "always" msgstr "vždy" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "OSD" @@ -937,6 +952,9 @@ msgstr "Složky v menu časovače" msgid "Setup.OSD$Always sort folders first" msgstr "Adresáře řadit vždy na začátek" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "Psát písmena pomocí číselných kláves" @@ -1121,6 +1139,9 @@ msgstr "CAM připraven" msgid " (activating)" msgstr " (aktivuji)" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "CAM" @@ -1151,6 +1172,15 @@ msgstr "CAM se používá - opravdu restartovat?" msgid "Can't reset CAM!" msgstr "CAM modul nelze restartovat!" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "nepřerusovat běžící program" @@ -1181,6 +1211,9 @@ msgstr "Výchozí priorita" msgid "Setup.Recording$Default lifetime (d)" msgstr "Výchozí životnost" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "Chování klávesy Přerušení" @@ -1280,6 +1313,15 @@ msgstr "Časový limit neaktivnosti (min)" msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "Časový limit SVDRP (s)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "Časový limit Zap (s)" @@ -1533,6 +1575,9 @@ msgstr "Brzo začne nahrávání!" msgid "Pause live video?" msgstr "Přerušit běžící program?" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "Začalo nahrávání" diff --git a/po/da_DK.po b/po/da_DK.po index d0fbbf80..0861ad76 100644 --- a/po/da_DK.po +++ b/po/da_DK.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2007-08-12 14:17+0200\n" "Last-Translator: Mogens Elneff \n" "Language-Team: Danish \n" @@ -660,6 +660,9 @@ msgstr "Levetid" msgid "File" msgstr "Fil" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "" @@ -672,6 +675,12 @@ msgstr "" msgid "First day" msgstr "Frste dag" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "" @@ -847,6 +856,12 @@ msgstr "skin afh msgid "always" msgstr "altid" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "OSD" @@ -934,6 +949,9 @@ msgstr "" msgid "Setup.OSD$Always sort folders first" msgstr "" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "" @@ -1118,6 +1136,9 @@ msgstr "CAM klar" msgid " (activating)" msgstr "" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "CAM" @@ -1148,6 +1169,15 @@ msgstr "CAM er i brug - virkelig nulstille?" msgid "Can't reset CAM!" msgstr "Kan ikke nulstille CAM!" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "" @@ -1178,6 +1208,9 @@ msgstr "Standard prioritet" msgid "Setup.Recording$Default lifetime (d)" msgstr "Standard levetid (d)" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "" @@ -1277,6 +1310,15 @@ msgstr "Min. bruger inaktivitet (min)" msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "SVDRP timeout (s)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "Zap timeout (s)" @@ -1530,6 +1572,9 @@ msgstr "Optagelse starter snart!" msgid "Pause live video?" msgstr "" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "Optagelse startet" diff --git a/po/de_DE.po b/po/de_DE.po index 762c6fa3..773be425 100644 --- a/po/de_DE.po +++ b/po/de_DE.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2015-02-10 13:45+0100\n" "Last-Translator: Klaus Schmidinger \n" "Language-Team: German \n" @@ -661,6 +661,9 @@ msgstr "Lebensdauer" msgid "File" msgstr "Datei" +msgid "Record on" +msgstr "Aufnehmen auf" + msgid "Button$Folder" msgstr "Verzeichnis" @@ -673,6 +676,12 @@ msgstr "Wiederholend" msgid "First day" msgstr "Erster Tag" +msgid "Error while accessing remote timer" +msgstr "Fehler beim Ansprechen des fernen Timers" + +msgid "Timer has been deleted!" +msgstr "Timer wurde gelscht!" + msgid "Select folder" msgstr "Verzeichnis whlen" @@ -848,6 +857,12 @@ msgstr "je nach Oberfl msgid "always" msgstr "immer" +msgid "by name" +msgstr "nach Namen" + +msgid "by time" +msgstr "nach Zeit" + msgid "OSD" msgstr "OSD" @@ -935,6 +950,9 @@ msgstr "Verzeichnisse im Timer-Men msgid "Setup.OSD$Always sort folders first" msgstr "Verzeichnisse immer zuerst einsortieren" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "Standard Sortierreihenfolge fr Aufnahmen" + msgid "Setup.OSD$Number keys for characters" msgstr "Nummerntasten fr Zeichen" @@ -1119,6 +1137,9 @@ msgstr "CAM bereit" msgid " (activating)" msgstr " (wird aktiviert)" +msgid "@ device" +msgstr "@ Empfnger" + msgid "CAM" msgstr "CAM" @@ -1149,6 +1170,15 @@ msgstr "CAM wird benutzt - wirklich zur msgid "Can't reset CAM!" msgstr "Zurcksetzen des CAM fehlgeschlagen!" +msgid "no instant recording" +msgstr "keine Sofortaufnahme" + +msgid "confirm instant recording" +msgstr "Sofortaufnahme besttigen" + +msgid "record instantly" +msgstr "sofort aufnehmen" + msgid "do not pause live video" msgstr "Live-Signal nicht anhalten" @@ -1179,6 +1209,9 @@ msgstr "Default-Priorit msgid "Setup.Recording$Default lifetime (d)" msgstr "Default-Lebensdauer (d)" +msgid "Setup.Recording$Record key handling" +msgstr "Funktion der Aufnehmen-Taste" + msgid "Setup.Recording$Pause key handling" msgstr "Funktion der Pause-Taste" @@ -1278,6 +1311,15 @@ msgstr "VDR ausschalten bei Inaktivit msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "SVDRP trennen bei Inaktivitt (s)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "SVDRP Verbindung" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "SVDRP Maschinenname" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "SVDRP Standardmaschine" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "Mindestzeit fr vorherigen Kanal (s)" @@ -1531,6 +1573,9 @@ msgstr "Aufnahme beginnt in K msgid "Pause live video?" msgstr "Live-Signal anhalten?" +msgid "Start recording?" +msgstr "Aufnahme starten?" + msgid "Recording started" msgstr "Aufzeichnung gestartet" diff --git a/po/el_GR.po b/po/el_GR.po index a131cd7c..1660877c 100644 --- a/po/el_GR.po +++ b/po/el_GR.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2007-08-12 14:17+0200\n" "Last-Translator: Dimitrios Dimitrakos \n" "Language-Team: Greek \n" @@ -660,6 +660,9 @@ msgstr " msgid "File" msgstr "" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "" @@ -672,6 +675,12 @@ msgstr "" msgid "First day" msgstr " " +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "" @@ -847,6 +856,12 @@ msgstr " msgid "always" msgstr "" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "OSD" @@ -934,6 +949,9 @@ msgstr "" msgid "Setup.OSD$Always sort folders first" msgstr "" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "" @@ -1118,6 +1136,9 @@ msgstr "" msgid " (activating)" msgstr "" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "CAM" @@ -1148,6 +1169,15 @@ msgstr "" msgid "Can't reset CAM!" msgstr " CAM" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "" @@ -1178,6 +1208,9 @@ msgstr " msgid "Setup.Recording$Default lifetime (d)" msgstr " ()" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "" @@ -1277,6 +1310,15 @@ msgstr " msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "SVDRP ()" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr " ()" @@ -1530,6 +1572,9 @@ msgstr "" msgid "Pause live video?" msgstr "" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "" diff --git a/po/es_ES.po b/po/es_ES.po index b2827b5d..3a763f1b 100644 --- a/po/es_ES.po +++ b/po/es_ES.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2015-02-19 23:00+0100\n" "Last-Translator: Gabriel Bonich \n" "Language-Team: Spanish \n" @@ -661,6 +661,9 @@ msgstr "Duraci msgid "File" msgstr "Fichero" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "Carpeta" @@ -673,6 +676,12 @@ msgstr "Peri msgid "First day" msgstr "Primer da" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "Seleccionar carpeta" @@ -848,6 +857,12 @@ msgstr "seg msgid "always" msgstr "siempre" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "Mens en pantalla" @@ -935,6 +950,9 @@ msgstr "Carpetas en men msgid "Setup.OSD$Always sort folders first" msgstr "Siempre ordenar primero carpetas" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "Teclas numricas para caracteres" @@ -1119,6 +1137,9 @@ msgstr "CAM preparado" msgid " (activating)" msgstr " (activando)" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "CAM" @@ -1149,6 +1170,15 @@ msgstr "CAM en uso - msgid "Can't reset CAM!" msgstr "No se puede reiniciar CAM!" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "no pausar emisin en directo" @@ -1179,6 +1209,9 @@ msgstr "Prioridad por defecto" msgid "Setup.Recording$Default lifetime (d)" msgstr "Duracin por defecto (das)" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "Gestin tecla de pausa" @@ -1278,6 +1311,15 @@ msgstr "Tiempo m msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "Tiempo de espera de SVDRP (sg)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "Considerar canal como visto (sg)" @@ -1531,6 +1573,9 @@ msgstr " msgid "Pause live video?" msgstr "Pausar emisin en directo?" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "Iniciando grabacin" diff --git a/po/et_EE.po b/po/et_EE.po index 8bf931a4..2f6e0de0 100644 --- a/po/et_EE.po +++ b/po/et_EE.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2007-08-12 14:17+0200\n" "Last-Translator: Arthur Konovalov \n" "Language-Team: Estonian \n" @@ -660,6 +660,9 @@ msgstr "Eluiga" msgid "File" msgstr "Fail" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "Kaust" @@ -672,6 +675,12 @@ msgstr "Korduv" msgid "First day" msgstr "1. päev" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "Kausta valik" @@ -847,6 +856,12 @@ msgstr "kestast sõltuv" msgid "always" msgstr "alati" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "Ekraanikuva" @@ -934,6 +949,9 @@ msgstr "Kaustad taimeri menüüs" msgid "Setup.OSD$Always sort folders first" msgstr "Sorteerida kaustad alati ette" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "Teksti sisestamine numbriklahvidega" @@ -1118,6 +1136,9 @@ msgstr "CAM töövalmis" msgid " (activating)" msgstr " (aktiveerimine)" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "CAM" @@ -1148,6 +1169,15 @@ msgstr "CAM on kasutuses - taaskäivitada?" msgid "Can't reset CAM!" msgstr "CAM mooduli taaskäivitus ebaõnnestus!" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "mitte peatada" @@ -1178,6 +1208,9 @@ msgstr "Vaikimisi prioriteet" msgid "Setup.Recording$Default lifetime (d)" msgstr "Salvestuse eluiga (päevi)" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "Pausi klahvi käsitlemine" @@ -1277,6 +1310,15 @@ msgstr "Min. kasutaja tegevusetus (min)" msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "SVDRP ooteaeg (s)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "Kanalivahetuse ooteaeg (s)" @@ -1530,6 +1572,9 @@ msgstr "Salvestamine tulekul!" msgid "Pause live video?" msgstr "Peatada otseülekanne?" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "Salvestamine käivitatud" diff --git a/po/fi_FI.po b/po/fi_FI.po index 05d4b0f9..769981f3 100644 --- a/po/fi_FI.po +++ b/po/fi_FI.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2007-08-15 15:52+0200\n" "Last-Translator: Matti Lehtimäki \n" "Language-Team: Finnish \n" @@ -664,6 +664,9 @@ msgstr "Elinikä" msgid "File" msgstr "Tiedosto" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "Kansio" @@ -676,6 +679,12 @@ msgstr "Toistuva" msgid "First day" msgstr "1. päivä" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "Valitse kansio" @@ -851,6 +860,12 @@ msgstr "ulkoasun mukaan" msgid "always" msgstr "aina" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "Kuvaruutunäyttö" @@ -938,6 +953,9 @@ msgstr "Näytä kansiot ajastinvalikossa" msgid "Setup.OSD$Always sort folders first" msgstr "Näytä kansiot ensin" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "Käytä numeronäppäimiä tekstisyötteessä" @@ -1122,6 +1140,9 @@ msgstr "CAM valmis" msgid " (activating)" msgstr " (aktivoidaan)" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "CAM" @@ -1152,6 +1173,15 @@ msgstr "CA-moduuli käytössä - nollataanko?" msgid "Can't reset CAM!" msgstr "CA-moduulin nollaus epäonnistui!" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "älä pysäytä lähetystä" @@ -1182,6 +1212,9 @@ msgstr "Tallenteen oletusprioriteetti" msgid "Setup.Recording$Default lifetime (d)" msgstr "Tallenteen oletuselinikä (d)" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "Taukonäppäimen toiminta" @@ -1281,6 +1314,15 @@ msgstr "Käyttäjätoiminnon odotusaika (min)" msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "SVDRP-komennon odotusaika (s)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "Kanavavalinnan odotusaika (s)" @@ -1534,6 +1576,9 @@ msgstr "Tallennus on alkamassa!" msgid "Pause live video?" msgstr "Pysäytetäänkö lähetys?" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "Tallennus aloitettu" diff --git a/po/fr_FR.po b/po/fr_FR.po index 06bb1255..5e4348d2 100644 --- a/po/fr_FR.po +++ b/po/fr_FR.po @@ -18,7 +18,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-11 11:02+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2015-02-18 20:16+0100\n" "Last-Translator: Bernard Jaulin \n" "Language-Team: French \n" @@ -671,6 +671,9 @@ msgstr "Durée de vie" msgid "File" msgstr "Fichier" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "Dossier" @@ -683,6 +686,12 @@ msgstr "Périodique" msgid "First day" msgstr "Premier jour" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "Sélectionner le dossier" @@ -858,6 +867,12 @@ msgstr "dépendant du skin" msgid "always" msgstr "toujours" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "Affichage à l'écran" @@ -945,6 +960,9 @@ msgstr "Dossiers dans menu programmation" msgid "Setup.OSD$Always sort folders first" msgstr "Toujours trier les dossiers en premier" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "Touches numériques pour caractères" @@ -1129,6 +1147,9 @@ msgstr "CAM prêt" msgid " (activating)" msgstr " (activation en cours)" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "CAM" @@ -1159,6 +1180,15 @@ msgstr "CAM en cours d'utilisation - confirmer réinitialisation ?" msgid "Can't reset CAM!" msgstr "Impossible de réinitialiser le CAM !" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "ne pas mettre en pause le direct" @@ -1189,6 +1219,9 @@ msgstr "Priorité par défaut" msgid "Setup.Recording$Default lifetime (d)" msgstr "Durée de vie par défaut (j)" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "Fonction de la touche Pause" @@ -1288,6 +1321,15 @@ msgstr "Inactivité de l'utilisateur (min)" msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "Temps maxi SVDRP (s)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "Prise en compte chaîne (s)" @@ -1541,6 +1583,9 @@ msgstr "L'enregistrement va commencer !" msgid "Pause live video?" msgstr "Arrêter le direct ?" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "L'enregistrement a commencé" diff --git a/po/hr_HR.po b/po/hr_HR.po index 0424a446..38d6fff8 100644 --- a/po/hr_HR.po +++ b/po/hr_HR.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2008-03-17 19:00+0100\n" "Last-Translator: Adrian Caval \n" "Language-Team: Croatian \n" @@ -662,6 +662,9 @@ msgstr "Trajanje" msgid "File" msgstr "Datoteka" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "" @@ -674,6 +677,12 @@ msgstr "" msgid "First day" msgstr "Prvi dan" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "" @@ -849,6 +858,12 @@ msgstr "ovisno o povr msgid "always" msgstr "uvijek" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "OSD" @@ -936,6 +951,9 @@ msgstr "" msgid "Setup.OSD$Always sort folders first" msgstr "" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "" @@ -1120,6 +1138,9 @@ msgstr "CAM spreman" msgid " (activating)" msgstr "" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "CAM" @@ -1150,6 +1171,15 @@ msgstr "CAM se koristi - ponovno pokrenuti unato msgid "Can't reset CAM!" msgstr "Ponovno pokretanje CAM-a neuspjeno!" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "" @@ -1180,6 +1210,9 @@ msgstr "Zadani prioritet" msgid "Setup.Recording$Default lifetime (d)" msgstr "Zadano trajanje (d)" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "" @@ -1279,6 +1312,15 @@ msgstr "Minimalno vrijeme neaktivnosti (min)" msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "SVDRP vrijeme neaktivnosti" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "Zap istjee (s)" @@ -1532,6 +1574,9 @@ msgstr "Obnovljena snimka!" msgid "Pause live video?" msgstr "" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "Snimanje zapoelo" diff --git a/po/hu_HU.po b/po/hu_HU.po index c34bc0e3..5af4a8c7 100644 --- a/po/hu_HU.po +++ b/po/hu_HU.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-01-30 13:14+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2015-02-13 09:36+0200\n" "Last-Translator: István Füley \n" "Language-Team: Hungarian \n" @@ -665,6 +665,9 @@ msgstr "Élettartam" msgid "File" msgstr "Fájl" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "Könyvtár" @@ -677,6 +680,12 @@ msgstr "Ismétlődő" msgid "First day" msgstr "Első nap" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "Könyvtár kiválasztása" @@ -852,6 +861,12 @@ msgstr "Menü nézetétől függően" msgid "always" msgstr "mindig" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "OSD" @@ -939,6 +954,9 @@ msgstr "Könyvtárválasztás az időzítő menüben" msgid "Setup.OSD$Always sort folders first" msgstr "Könyvtárakat rendezd előre" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "Betűk a számgombokon" @@ -1123,6 +1141,9 @@ msgstr "CAM működik" msgid " (activating)" msgstr " (aktiválás)" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "CAM" @@ -1153,6 +1174,15 @@ msgstr "CAM használatban - valóban újraindítani?" msgid "Can't reset CAM!" msgstr "CAM újraindítása nem sikerült!" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "élőkép leállítása tiltva" @@ -1183,6 +1213,9 @@ msgstr "Alapértelmezett prioritás" msgid "Setup.Recording$Default lifetime (d)" msgstr "Alapértelmezett élettartam (n)" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "Szünet gomb működése" @@ -1282,6 +1315,15 @@ msgstr "VDR leáll használat nélkül (p)" msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "SVDRP szétkapcsol használat nélkül(mp)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "Előző csatorna időkorlátja (mp)" @@ -1535,6 +1577,9 @@ msgstr "Felvétel rögtön indul!" msgid "Pause live video?" msgstr "Megállítsam az élő képet?" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "Felvétel elindítva" diff --git a/po/it_IT.po b/po/it_IT.po index 79946ffd..d54a4a39 100644 --- a/po/it_IT.po +++ b/po/it_IT.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2015-02-12 19:31+0100\n" "Last-Translator: Diego Pierotto \n" "Language-Team: Italian \n" @@ -666,6 +666,9 @@ msgstr "Scadenza" msgid "File" msgstr "Nome" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "Cartella" @@ -678,6 +681,12 @@ msgstr "Repliche" msgid "First day" msgstr "1° giorno" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "Seleziona cartella" @@ -853,6 +862,12 @@ msgstr "in base allo stile interfaccia" msgid "always" msgstr "sempre" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "OSD" @@ -940,6 +955,9 @@ msgstr "Cartelle nel menu timer" msgid "Setup.OSD$Always sort folders first" msgstr "Ordina sempre per prima le cartelle" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "Tasti numerici per i caratteri" @@ -1124,6 +1142,9 @@ msgstr "La CAM è pronta" msgid " (activating)" msgstr " (attivazione)" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "Accesso condizionato CAM" @@ -1154,6 +1175,15 @@ msgstr "La CAM è in uso - vuoi reimpostarla?" msgid "Can't reset CAM!" msgstr "Impossibile reimpostare il modulo CAM!" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "non mettere in pausa il video dal vivo" @@ -1184,6 +1214,9 @@ msgstr "Priorità predefinita" msgid "Setup.Recording$Default lifetime (d)" msgstr "Scadenza predefinita (gg)" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "Gestione tasto Pausa" @@ -1283,6 +1316,15 @@ msgstr "Periodo min. inattività (min)" msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "Scadenza SVDRP (s)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "Scadenza Zapping (s)" @@ -1536,6 +1578,9 @@ msgstr "Registrazione imminente!" msgid "Pause live video?" msgstr "Pausare video dal vivo?" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "Registrazione avviata" diff --git a/po/lt_LT.po b/po/lt_LT.po index a77dfcc7..b19538b0 100644 --- a/po/lt_LT.po +++ b/po/lt_LT.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2015-02-11 14:02+0200\n" "Last-Translator: Valdemaras Pipiras \n" "Language-Team: Lithuanian \n" @@ -660,6 +660,9 @@ msgstr "Galiojimas" msgid "File" msgstr "Failas" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "Katalogas" @@ -672,6 +675,12 @@ msgstr "Kartotinas" msgid "First day" msgstr "Pirma diena" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "Pasirinkite katalogą" @@ -847,6 +856,12 @@ msgstr "priklauso nuo stiliaus" msgid "always" msgstr "visada" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "OSD (ekrano užsklanda)" @@ -934,6 +949,9 @@ msgstr "Katalogai esantys laikmačių meniu" msgid "Setup.OSD$Always sort folders first" msgstr "Visada pirmiau rūšiuoti katalogus" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "Skaičių mygtukai simboliams" @@ -1118,6 +1136,9 @@ msgstr "Dekodavimo modulis (CAM) paruoštas darbui" msgid " (activating)" msgstr " (aktyvuojama)" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "Dekodavimo modulis (CAM)" @@ -1148,6 +1169,15 @@ msgstr "Dekodavimo modulis šiuo metu naudojamas - tikrai perkrauti?" msgid "Can't reset CAM!" msgstr "Negali perkrauti dekodavimo modulio!" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "nepristabdyti 'gyvo' video srauto" @@ -1178,6 +1208,9 @@ msgstr "Numatytas laikmačio prioritetas" msgid "Setup.Recording$Default lifetime (d)" msgstr "Numatytasis įrašo saugojimo laikas (d)" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "Pauzės mygtuko interpretavimas" @@ -1277,6 +1310,15 @@ msgstr "Min. įvesties laukimo laikas (min)" msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "SVDRP užlaikymas (s)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "Kanalo perjungimo užlaikymas (s)" @@ -1530,6 +1572,9 @@ msgstr "Tuoj įsijungs įrašinėjimas!" msgid "Pause live video?" msgstr "Sustabdyti 'gyvą' video srautą?" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "Įrašymas prasidėjo" diff --git a/po/mk_MK.po b/po/mk_MK.po index 3e4e7e4a..6e81944b 100644 --- a/po/mk_MK.po +++ b/po/mk_MK.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2015-02-08 15:18+0100\n" "Last-Translator: Dimitar Petrovski \n" "Language-Team: Macedonian \n" @@ -661,6 +661,9 @@ msgstr "Траење" msgid "File" msgstr "Датотека" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "Директориум" @@ -673,6 +676,12 @@ msgstr "Периодичен" msgid "First day" msgstr "Прв ден" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "Избери директориум" @@ -848,6 +857,12 @@ msgstr "зависно од фасада" msgid "always" msgstr "секогаш" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "OSD" @@ -935,6 +950,9 @@ msgstr "Директориуми во менито за тајмер" msgid "Setup.OSD$Always sort folders first" msgstr "Секогаш сортирај прво папки" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "Нумерички копчиња за букви" @@ -1119,6 +1137,9 @@ msgstr "CAM спремен" msgid " (activating)" msgstr " (активирање)" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "CAM" @@ -1149,6 +1170,15 @@ msgstr "CAM е во употреба - рестартирај?" msgid "Can't reset CAM!" msgstr "Неуспешно рестартирање на CAM-от!" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "не паузирај пренос во живо" @@ -1179,6 +1209,9 @@ msgstr "Зададен приоритет" msgid "Setup.Recording$Default lifetime (d)" msgstr "Стандардно веметраење (денови)" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "Подесување на копчето пауза" @@ -1278,6 +1311,15 @@ msgstr "Минимум неактивност на корисникот (мин) msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "SVDRP тајмаут (сек)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "Зап тајмаут (сек)" @@ -1531,6 +1573,9 @@ msgstr "Претстои снимање!" msgid "Pause live video?" msgstr "Паузирај пренос во живо?" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "Снимањето почна" diff --git a/po/nl_NL.po b/po/nl_NL.po index ab3fabd2..6621e393 100644 --- a/po/nl_NL.po +++ b/po/nl_NL.po @@ -13,7 +13,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-11 10:51+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2015-02-10 19:43+0100\n" "Last-Translator: Erik Oomen \n" "Language-Team: Dutch \n" @@ -666,6 +666,9 @@ msgstr "Bewaarduur" msgid "File" msgstr "Bestandnaam" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "Map" @@ -678,6 +681,12 @@ msgstr "Herhalen" msgid "First day" msgstr "Eerste dag" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "Kies map" @@ -853,6 +862,12 @@ msgstr "skin afhankelijk" msgid "always" msgstr "altijd" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "OSD" @@ -940,6 +955,9 @@ msgstr "Mappen in timermenu" msgid "Setup.OSD$Always sort folders first" msgstr "Altijd mappen eerst sorteren" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "Nummertoetsen voor karakters" @@ -1124,6 +1142,9 @@ msgstr "CAM gereed" msgid " (activating)" msgstr " (Activeren)" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "CAM" @@ -1154,6 +1175,15 @@ msgstr "CAM wordt gebruikt - werkelijk herstarten?" msgid "Can't reset CAM!" msgstr "Kan CAM niet herstarten!" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "live video niet pauzeren" @@ -1184,6 +1214,9 @@ msgstr "Standaard prioriteit" msgid "Setup.Recording$Default lifetime (d)" msgstr "Standaard levensduur (d)" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "Gedrag pauze toets" @@ -1283,6 +1316,15 @@ msgstr "Minimum gebruikers inactiviteit (min)" msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "SVDRP Timeout (s)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "Zap timeout (s)" @@ -1536,6 +1578,9 @@ msgstr "Opname start binnenkort!" msgid "Pause live video?" msgstr "Pauzeer live video?" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "Opname is gestart!" diff --git a/po/nn_NO.po b/po/nn_NO.po index ba20fc6e..d90bedb8 100644 --- a/po/nn_NO.po +++ b/po/nn_NO.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2007-08-12 14:17+0200\n" "Last-Translator: Truls Slevigen \n" "Language-Team: Norwegian Nynorsk \n" @@ -661,6 +661,9 @@ msgstr "Levetid" msgid "File" msgstr "Filnavn" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "" @@ -673,6 +676,12 @@ msgstr "" msgid "First day" msgstr "Frste dag" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "" @@ -848,6 +857,12 @@ msgstr "" msgid "always" msgstr "" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "OSD" @@ -935,6 +950,9 @@ msgstr "" msgid "Setup.OSD$Always sort folders first" msgstr "" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "" @@ -1119,6 +1137,9 @@ msgstr "" msgid " (activating)" msgstr "" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "CAM" @@ -1149,6 +1170,15 @@ msgstr "" msgid "Can't reset CAM!" msgstr "" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "" @@ -1179,6 +1209,9 @@ msgstr "Normal prioritet (Timer)" msgid "Setup.Recording$Default lifetime (d)" msgstr "Normal levetid timer (d)" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "" @@ -1278,6 +1311,15 @@ msgstr "Minimumstid med inaktivitet (min)" msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "Ubrukt SVDRP-levetid (s)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "" @@ -1531,6 +1573,9 @@ msgstr "" msgid "Pause live video?" msgstr "" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "" diff --git a/po/pl_PL.po b/po/pl_PL.po index becb8cb2..7b4ab888 100644 --- a/po/pl_PL.po +++ b/po/pl_PL.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2015-02-12 00:59+0100\n" "Last-Translator: Tomasz Maciej Nowak \n" "Language-Team: Polish \n" @@ -663,6 +663,9 @@ msgstr "Czas msgid "File" msgstr "Plik" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "Katalog" @@ -675,6 +678,12 @@ msgstr "Powtarzanie" msgid "First day" msgstr "Pierwszy dzie" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "Wybierz katalog" @@ -850,6 +859,12 @@ msgstr "zal. od sk msgid "always" msgstr "zawsze" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "OSD" @@ -937,6 +952,9 @@ msgstr "Katalogi w menu timera" msgid "Setup.OSD$Always sort folders first" msgstr "Sortuj najpierw katalogi" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "Klawisze numeryczne dla liter" @@ -1121,6 +1139,9 @@ msgstr "CAM gotowy" msgid " (activating)" msgstr " (aktywuj)" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "CAM" @@ -1151,6 +1172,15 @@ msgstr "CAM jest w u msgid "Can't reset CAM!" msgstr "Nie mona zresetowa CAM!" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "nie wstrzymuj transmisji na ywo" @@ -1181,6 +1211,9 @@ msgstr "Domy msgid "Setup.Recording$Default lifetime (d)" msgstr "Domylny czas ycia (d)" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "Obsuga klawisza pauzy" @@ -1280,6 +1313,15 @@ msgstr "Minimalny czas nieaktywno msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "Czas oczekiwania na SVDRP (s)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "Czas oczekiwania na zap (s)" @@ -1533,6 +1575,9 @@ msgstr "Wkr msgid "Pause live video?" msgstr "Zatrzyma transmisj na ywo?" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "Rozpoczto nagrywanie" diff --git a/po/pt_PT.po b/po/pt_PT.po index 9a0f7924..ecf72602 100644 --- a/po/pt_PT.po +++ b/po/pt_PT.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2010-03-28 22:49+0100\n" "Last-Translator: Cris Silva \n" "Language-Team: Portuguese \n" @@ -661,6 +661,9 @@ msgstr "Validade" msgid "File" msgstr "Ficheiro" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "Pasta" @@ -673,6 +676,12 @@ msgstr "" msgid "First day" msgstr "1 dia" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "Seleccione a pasta" @@ -848,6 +857,12 @@ msgstr "dependente do tema" msgid "always" msgstr "sempre" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "OSD" @@ -935,6 +950,9 @@ msgstr "Pastas no menu de grava msgid "Setup.OSD$Always sort folders first" msgstr "" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "Teclas numricas para caracteres" @@ -1119,6 +1137,9 @@ msgstr "CAM pronta" msgid " (activating)" msgstr "" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "CAM" @@ -1149,6 +1170,15 @@ msgstr "CAM em uso - reiniciar mesmo?" msgid "Can't reset CAM!" msgstr "Impossvel reiniciar a CAM!" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "no pausar emisso" @@ -1179,6 +1209,9 @@ msgstr "PPrioridade padr msgid "Setup.Recording$Default lifetime (d)" msgstr "Validade padro (d)" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "Comportamento da tecla Pausa" @@ -1278,6 +1311,15 @@ msgstr "Tempo m msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "Tempo de espera SVDRP (s)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "Tempo de espera entre mudana de canais (s)" @@ -1531,6 +1573,9 @@ msgstr "Grava msgid "Pause live video?" msgstr "Pausar emisso?" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "Gravao iniciada" diff --git a/po/ro_RO.po b/po/ro_RO.po index c88dd0a8..157acf3c 100644 --- a/po/ro_RO.po +++ b/po/ro_RO.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2015-02-11 22:26+0100\n" "Last-Translator: Lucian Muresan \n" "Language-Team: Romanian \n" @@ -662,6 +662,9 @@ msgstr "Timp de păstrare" msgid "File" msgstr "Fişier" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "Director" @@ -674,6 +677,12 @@ msgstr "Repetitiv" msgid "First day" msgstr "Prima zi" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "Selectează directorul" @@ -849,6 +858,12 @@ msgstr "dependent de skin" msgid "always" msgstr "întotdeauna" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "OSD" @@ -936,6 +951,9 @@ msgstr "Directoare în meniul de timer-e" msgid "Setup.OSD$Always sort folders first" msgstr "Sortează întotdeauna directoarele la început" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "Caractere pe tastele numerice" @@ -1120,6 +1138,9 @@ msgstr "CAM pregătit" msgid " (activating)" msgstr " (activez)" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "CAM" @@ -1150,6 +1171,15 @@ msgstr "CAM-ul este in folosinţă - totuşi resetez?" msgid "Can't reset CAM!" msgstr "Nu pot reseta CAM" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "nu înregistra emisiunea" @@ -1180,6 +1210,9 @@ msgstr "Prioritate implicită" msgid "Setup.Recording$Default lifetime (d)" msgstr "Timp de păstrare predefinit (zile)" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "Funcţia tastei 'pauză'" @@ -1279,6 +1312,15 @@ msgstr "Durata minimă de inactivitate (min)" msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "Timeout SVDRP (sec)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "Interval zapping (s)" @@ -1532,6 +1574,9 @@ msgstr "Urmează o înregistrare!" msgid "Pause live video?" msgstr "Înregistrez emisiunea?" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "A început înregistrarea" diff --git a/po/ru_RU.po b/po/ru_RU.po index 3e5057d7..eea8b8de 100644 --- a/po/ru_RU.po +++ b/po/ru_RU.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2013-03-10 17:13+0100\n" "Last-Translator: Oleg Roitburd \n" "Language-Team: Russian \n" @@ -661,6 +661,9 @@ msgstr " msgid "File" msgstr "" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "" @@ -673,6 +676,12 @@ msgstr " msgid "First day" msgstr " " +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr " " @@ -848,6 +857,12 @@ msgstr " msgid "always" msgstr "" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "" @@ -935,6 +950,9 @@ msgstr " msgid "Setup.OSD$Always sort folders first" msgstr " " +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr " " @@ -1119,6 +1137,9 @@ msgstr "CAM msgid " (activating)" msgstr "" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr " " @@ -1149,6 +1170,15 @@ msgstr "CAM msgid "Can't reset CAM!" msgstr " CAM-!" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr " live video" @@ -1179,6 +1209,9 @@ msgstr " msgid "Setup.Recording$Default lifetime (d)" msgstr " ()" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr " " @@ -1278,6 +1311,15 @@ msgstr " msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr " . SVDRP ()" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr " ()" @@ -1531,6 +1573,9 @@ msgstr " msgid "Pause live video?" msgstr " live video?" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr " " diff --git a/po/sk_SK.po b/po/sk_SK.po index cfc9bdee..fcee4824 100644 --- a/po/sk_SK.po +++ b/po/sk_SK.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2015-02-17 18:59+0100\n" "Last-Translator: Milan Hrala \n" "Language-Team: Slovak \n" @@ -661,6 +661,9 @@ msgstr " msgid "File" msgstr "Sbor" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "Zloka" @@ -673,6 +676,12 @@ msgstr "s opakovan msgid "First day" msgstr "Odo da" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "Vybra zloku" @@ -848,6 +857,12 @@ msgstr "pod msgid "always" msgstr "vdy" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "OSD (Menu na obrazovke)" @@ -935,6 +950,9 @@ msgstr "Zlo msgid "Setup.OSD$Always sort folders first" msgstr "Zloky vdy najprv usporiada" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "Psa znaky selnmi tlaidlami" @@ -1119,6 +1137,9 @@ msgstr "CAM pripraven msgid " (activating)" msgstr " (aktivovan)" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "CAM (modul podmienenho prstupu)" @@ -1149,6 +1170,15 @@ msgstr "CAM sa pou msgid "Can't reset CAM!" msgstr "CAM modul nejde retartova!" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "nepozastavova iv vysielanie" @@ -1179,6 +1209,9 @@ msgstr "Predvolen msgid "Setup.Recording$Default lifetime (d)" msgstr "Predvolen ivotnos" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "Chovanie klvesy pozastavenia" @@ -1278,6 +1311,15 @@ msgstr "Vymedzi msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "asov limit odpovede SVDRP (s)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "pamta predol kanl po ... (s)" @@ -1531,6 +1573,9 @@ msgstr "Onedlho za msgid "Pause live video?" msgstr "Prerui iv vysielanie?" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "Zaalo nahrvanie" diff --git a/po/sl_SI.po b/po/sl_SI.po index d12ccb21..048ce54e 100644 --- a/po/sl_SI.po +++ b/po/sl_SI.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2013-03-04 12:46+0100\n" "Last-Translator: Matjaz Thaler \n" "Language-Team: Slovenian \n" @@ -661,6 +661,9 @@ msgstr "Veljavnost" msgid "File" msgstr "Datoteka" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "Direktorij" @@ -673,6 +676,12 @@ msgstr "Ponavljajo msgid "First day" msgstr "Prvi dan" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "Izberi direkotrij" @@ -848,6 +857,12 @@ msgstr "odvisno od preobleke" msgid "always" msgstr "vedno" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "OSD" @@ -935,6 +950,9 @@ msgstr "Direktoriji v meniju urnika" msgid "Setup.OSD$Always sort folders first" msgstr "" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "tevilo tipk za znake" @@ -1119,6 +1137,9 @@ msgstr "CAM pripravljen" msgid " (activating)" msgstr "" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "CAM" @@ -1149,6 +1170,15 @@ msgstr "CAM je v uporabi - zares resetiraj?" msgid "Can't reset CAM!" msgstr "Ne morem resetirati CAM-a!" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "ne ustavi videa v ivo" @@ -1179,6 +1209,9 @@ msgstr "Privzeta prioriteta" msgid "Setup.Recording$Default lifetime (d)" msgstr "Privzeti ivljenski as (d)" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "Gumb za pavzo" @@ -1278,6 +1311,15 @@ msgstr "Najmanj msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "SVDRP as neaktivnosti (s)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "as uglaevanja (s)" @@ -1531,6 +1573,9 @@ msgstr "Sledi snemanje!" msgid "Pause live video?" msgstr "Zaustavi video v ivo" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "Snemanje se je prielo" diff --git a/po/sr_RS.po b/po/sr_RS.po index 646607f7..c12e296a 100644 --- a/po/sr_RS.po +++ b/po/sr_RS.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2013-03-16 15:05+0100\n" "Last-Translator: Zoran Turalija \n" "Language-Team: Serbian \n" @@ -661,6 +661,9 @@ msgstr "Trajanje" msgid "File" msgstr "Datoteka" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "Direktorijum" @@ -673,6 +676,12 @@ msgstr "Ponavljaju msgid "First day" msgstr "Prvi dan" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "Izaberi direktorijum" @@ -848,6 +857,12 @@ msgstr "zavisi od stila (skin-a)" msgid "always" msgstr "uvek" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "OSD" @@ -935,6 +950,9 @@ msgstr "Direktorijumi u meniju tajmera" msgid "Setup.OSD$Always sort folders first" msgstr "Uvek sortiraj direktorijume prve" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "Piite pomou numerikih dugmia (kao SMS)" @@ -1119,6 +1137,9 @@ msgstr "CAM spreman" msgid " (activating)" msgstr "" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "CAM" @@ -1149,6 +1170,15 @@ msgstr "CAM u upotrebi - stvarno ponovno pokrenuti?" msgid "Can't reset CAM!" msgstr "Ponovno pokretanje CAM-a neuspeno!" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "ne zaustavljaj signal uivo" @@ -1179,6 +1209,9 @@ msgstr "Podrazumevani prioritet" msgid "Setup.Recording$Default lifetime (d)" msgstr "Podrazumevano trajanje (d)" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "Funkcija dugme za pauzu" @@ -1278,6 +1311,15 @@ msgstr "Min. vreme neaktivnosti pre ga msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "Gaenje SVDRP-a usled neaktivnosti (s)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "Zap istie (s)" @@ -1531,6 +1573,9 @@ msgstr "Uskoro po msgid "Pause live video?" msgstr "Zaustavi signal uivo?" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "Snimanje zapoelo" diff --git a/po/sv_SE.po b/po/sv_SE.po index c164fa1d..79723c87 100644 --- a/po/sv_SE.po +++ b/po/sv_SE.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2015-02-12 21:58+0100\n" "Last-Translator: Magnus Sirvi \n" "Language-Team: Swedish \n" @@ -665,6 +665,9 @@ msgstr "Livstid" msgid "File" msgstr "Filnamn" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "Mapp" @@ -677,6 +680,12 @@ msgstr "Repeterande" msgid "First day" msgstr "Frsta dag" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "Vlj mapp" @@ -852,6 +861,12 @@ msgstr "skin-beroende" msgid "always" msgstr "alltid" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "OSD" @@ -939,6 +954,9 @@ msgstr "Visa mappar i timermenyn" msgid "Setup.OSD$Always sort folders first" msgstr "Sortera alltid mappar frst" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "Anvnd sifferknappar fr bokstavsinmatning" @@ -1123,6 +1141,9 @@ msgstr "CAM klar" msgid " (activating)" msgstr " (aktiverar)" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "CAM" @@ -1153,6 +1174,15 @@ msgstr "CAM upptagen, vill du verkligen msgid "Can't reset CAM!" msgstr "Kan inte terstlla CAM!" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "pausa inte pgende program" @@ -1183,6 +1213,9 @@ msgstr "Normal prioritet" msgid "Setup.Recording$Default lifetime (d)" msgstr "Normal livstid (dagar)" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "Hantering av pausknapp" @@ -1282,6 +1315,15 @@ msgstr "Tidsgr msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "SVDRP-tidsgrns (s)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "Zap-tidsgrns (s)" @@ -1535,6 +1577,9 @@ msgstr "Snart b msgid "Pause live video?" msgstr "Pausa pgende program?" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "Inspelningen har startat" diff --git a/po/tr_TR.po b/po/tr_TR.po index 46a6a088..1d399332 100644 --- a/po/tr_TR.po +++ b/po/tr_TR.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2008-02-28 00:33+0100\n" "Last-Translator: Oktay Yolgeen \n" "Language-Team: Turkish \n" @@ -660,6 +660,9 @@ msgstr " msgid "File" msgstr "Ktk" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "" @@ -672,6 +675,12 @@ msgstr "" msgid "First day" msgstr "lk gn" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "" @@ -847,6 +856,12 @@ msgstr "y msgid "always" msgstr "hep" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "OSD" @@ -934,6 +949,9 @@ msgstr "" msgid "Setup.OSD$Always sort folders first" msgstr "" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "" @@ -1118,6 +1136,9 @@ msgstr "CAM haz msgid " (activating)" msgstr "" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "CAM" @@ -1148,6 +1169,15 @@ msgstr "CAM kullan msgid "Can't reset CAM!" msgstr "CAM sfrlanamad!" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "" @@ -1178,6 +1208,9 @@ msgstr "Ola msgid "Setup.Recording$Default lifetime (d)" msgstr "Olaan ekim mr (gn)" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "" @@ -1277,6 +1310,15 @@ msgstr "Minimum kullan msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "SVDRP zaman am (sn)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "Zaping zaman am (sn)" @@ -1530,6 +1572,9 @@ msgstr " msgid "Pause live video?" msgstr "" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "Kayt baland" diff --git a/po/uk_UA.po b/po/uk_UA.po index 9d7328bc..8bff8e25 100644 --- a/po/uk_UA.po +++ b/po/uk_UA.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2015-02-13 18:14+0100\n" "Last-Translator: Yarema aka Knedlyk \n" "Language-Team: Ukrainian \n" @@ -661,6 +661,9 @@ msgstr "Строк зберігання" msgid "File" msgstr "Файл" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "Тека" @@ -673,6 +676,12 @@ msgstr "Повтор" msgid "First day" msgstr "Перший день" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "Вибрати теку" @@ -848,6 +857,12 @@ msgstr "згідно зі стилем" msgid "always" msgstr "завжди" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "Меню" @@ -935,6 +950,9 @@ msgstr "Теки в меню таймера" msgid "Setup.OSD$Always sort folders first" msgstr "Завжди сортувати теки першими" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "Кількість клавіш для символів" @@ -1119,6 +1137,9 @@ msgstr "CAM готовий" msgid " (activating)" msgstr " (активування)" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "CAM (Умовний доступ)" @@ -1149,6 +1170,15 @@ msgstr "CAM використовується - дійсно перезапуст msgid "Can't reset CAM!" msgstr "Помилка перезапуску CAM-модуля!" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "не призупиняти перегляд" @@ -1179,6 +1209,9 @@ msgstr "Пріоритет таймера по замовчуванню" msgid "Setup.Recording$Default lifetime (d)" msgstr "Строк зберігання запису по замовчуванню (дні)" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "Обробка клавіші відкладеного перегляду" @@ -1278,6 +1311,15 @@ msgstr "Мін. час очікування вводу (хв)" msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "Затримка обриву з'єднання SVDRP (сек)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "Затримка переключання каналу (сек)" @@ -1531,6 +1573,9 @@ msgstr "Запис скоро почнеться!" msgid "Pause live video?" msgstr "Призупинити перегляд?" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "Запис почався" diff --git a/po/zh_CN.po b/po/zh_CN.po index 0dfbd6c2..09aea4b6 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 2.2.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-10 13:40+0100\n" +"POT-Creation-Date: 2015-09-11 10:38+0200\n" "PO-Revision-Date: 2013-03-04 14:52+0800\n" "Last-Translator: NFVDR \n" "Language-Team: Chinese (simplified) \n" @@ -662,6 +662,9 @@ msgstr "终生" msgid "File" msgstr "文件" +msgid "Record on" +msgstr "" + msgid "Button$Folder" msgstr "文件夹" @@ -674,6 +677,12 @@ msgstr "重复" msgid "First day" msgstr "第一天" +msgid "Error while accessing remote timer" +msgstr "" + +msgid "Timer has been deleted!" +msgstr "" + msgid "Select folder" msgstr "选择文件夹" @@ -849,6 +858,12 @@ msgstr "皮肤选择" msgid "always" msgstr "总是" +msgid "by name" +msgstr "" + +msgid "by time" +msgstr "" + msgid "OSD" msgstr "系统菜单设置" @@ -936,6 +951,9 @@ msgstr "定时器菜单中的文件夹" msgid "Setup.OSD$Always sort folders first" msgstr "总是排序文件夹" +msgid "Setup.OSD$Default sort mode for recordings" +msgstr "" + msgid "Setup.OSD$Number keys for characters" msgstr "数字键的字符" @@ -1120,6 +1138,9 @@ msgstr "CAM准备" msgid " (activating)" msgstr "" +msgid "@ device" +msgstr "" + msgid "CAM" msgstr "CAM设置" @@ -1150,6 +1171,15 @@ msgstr "CAM正在使用-是否重启?" msgid "Can't reset CAM!" msgstr "不能重启CAM" +msgid "no instant recording" +msgstr "" + +msgid "confirm instant recording" +msgstr "" + +msgid "record instantly" +msgstr "" + msgid "do not pause live video" msgstr "取消暂停当前视频" @@ -1180,6 +1210,9 @@ msgstr "默认优先" msgid "Setup.Recording$Default lifetime (d)" msgstr "默认终身 (d)" +msgid "Setup.Recording$Record key handling" +msgstr "" + msgid "Setup.Recording$Pause key handling" msgstr "暂停关键的处理" @@ -1279,6 +1312,15 @@ msgstr "Min. 使用者静止 (min)" msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "SVDRP 超时 (s)" +msgid "Setup.Miscellaneous$SVDRP peering" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP host name" +msgstr "" + +msgid "Setup.Miscellaneous$SVDRP default host" +msgstr "" + msgid "Setup.Miscellaneous$Zap timeout (s)" msgstr "Zap 超时 (s)" @@ -1532,6 +1574,9 @@ msgstr "准备录像!" msgid "Pause live video?" msgstr "是否暂停视频播放?" +msgid "Start recording?" +msgstr "" + msgid "Recording started" msgstr "录像开始!" diff --git a/positioner.c b/positioner.c index 22bad1b8..fdb07bb5 100644 --- a/positioner.c +++ b/positioner.c @@ -8,7 +8,7 @@ * http://www.vdr-portal.de/board17-developer/board97-vdr-core/p1154305-grundlagen-und-winkelberechnungen-f%C3%BCr-h-h-diseqc-motor-antennenanlagen * by Albert Danis. * - * $Id: positioner.c 3.5 2015/02/14 11:54:31 kls Exp $ + * $Id: positioner.c 4.0 2015/02/14 11:54:31 kls Exp $ */ #include "positioner.h" diff --git a/positioner.h b/positioner.h index 4fdc3d05..29c15245 100644 --- a/positioner.h +++ b/positioner.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: positioner.h 3.3 2013/12/28 11:15:56 kls Exp $ + * $Id: positioner.h 4.0 2013/12/28 11:15:56 kls Exp $ */ #ifndef __POSITIONER_H diff --git a/receiver.c b/receiver.c index d8f51e68..9375d4f4 100644 --- a/receiver.c +++ b/receiver.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: receiver.c 3.3 2015/01/12 14:04:31 kls Exp $ + * $Id: receiver.c 4.0 2015/01/12 14:04:31 kls Exp $ */ #include "receiver.h" diff --git a/receiver.h b/receiver.h index b3548258..09da8cda 100644 --- a/receiver.h +++ b/receiver.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: receiver.h 3.3 2015/01/12 14:03:22 kls Exp $ + * $Id: receiver.h 4.1 2015/09/05 11:42:47 kls Exp $ */ #ifndef __RECEIVER_H @@ -31,7 +31,7 @@ protected: ///< (On == true) and right after it gets detached from (On == false) a cDevice. It can be used ///< to do things like starting/stopping a thread. ///< It is guaranteed that Receive() will not be called before Activate(true). - virtual void Receive(uchar *Data, int Length) = 0; + virtual void Receive(const uchar *Data, int Length) = 0; ///< This function is called from the cDevice we are attached to, and ///< delivers one TS packet from the set of PIDs the cReceiver has requested. ///< The data packet must be accepted immediately, and the call must return diff --git a/recorder.c b/recorder.c index 3d6b8063..896ec939 100644 --- a/recorder.c +++ b/recorder.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: recorder.c 3.3 2014/02/21 09:19:52 kls Exp $ + * $Id: recorder.c 4.4 2015/09/12 14:56:15 kls Exp $ */ #include "recorder.h" @@ -106,9 +106,31 @@ void cRecorder::Activate(bool On) Cancel(3); } -void cRecorder::Receive(uchar *Data, int Length) +void cRecorder::Receive(const uchar *Data, int Length) { if (Running()) { + static const uchar aff[TS_SIZE - 4] = { 0xB7, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF}; // Length is always TS_SIZE! + if ((Data[3] & 0b00110000) == 0b00100000 && !memcmp(Data + 4, aff, sizeof(aff))) + return; // Adaptation Field Filler found, skipping int p = ringBuffer->Put(Data, Length); if (p != Length && Running()) ringBuffer->ReportOverflow(Length - p); @@ -135,7 +157,8 @@ void cRecorder::Action(void) if (frameDetector->FramesPerSecond() > 0 && DoubleEqual(RecordingInfo.FramesPerSecond(), DEFAULTFRAMESPERSECOND) && !DoubleEqual(RecordingInfo.FramesPerSecond(), frameDetector->FramesPerSecond())) { RecordingInfo.SetFramesPerSecond(frameDetector->FramesPerSecond()); RecordingInfo.Write(); - Recordings.UpdateByName(recordingName); + LOCK_RECORDINGS_WRITE; + Recordings->UpdateByName(recordingName); } } InfoWritten = true; diff --git a/recorder.h b/recorder.h index 4e7848a6..2fc7ef18 100644 --- a/recorder.h +++ b/recorder.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: recorder.h 3.1 2015/01/15 14:27:02 kls Exp $ + * $Id: recorder.h 4.1 2015/09/05 11:46:23 kls Exp $ */ #ifndef __RECORDER_H @@ -35,7 +35,7 @@ protected: ///< member of the cReceiver class) from your own destructor in order ///< to properly get a call to Activate(false) when your object is ///< destroyed. - virtual void Receive(uchar *Data, int Length); + virtual void Receive(const uchar *Data, int Length); virtual void Action(void); public: cRecorder(const char *FileName, const cChannel *Channel, int Priority); diff --git a/recording.c b/recording.c index 0a084140..a847c7d1 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 3.28 2015/02/16 07:49:14 kls Exp $ + * $Id: recording.c 4.4 2015/09/09 10:21:58 kls Exp $ */ #include "recording.h" @@ -76,9 +76,6 @@ int DirectoryNameMax = NAME_MAX; bool DirectoryEncoding = false; int InstanceId = 0; -cRecordings DeletedRecordings(true); -static cRecordings VanishedRecordings; - // --- cRemoveDeletedRecordingsThread ---------------------------------------- class cRemoveDeletedRecordingsThread : public cThread { @@ -100,8 +97,8 @@ void cRemoveDeletedRecordingsThread::Action(void) if (LockFile.Lock()) { time_t StartTime = time(NULL); bool deleted = false; - cThreadLock DeletedRecordingsLock(&DeletedRecordings); - for (cRecording *r = DeletedRecordings.First(); r; ) { + LOCK_DELETEDRECORDINGS_WRITE; + for (cRecording *r = DeletedRecordings->First(); r; ) { if (cIoThrottle::Engaged()) return; if (time(NULL) - StartTime > MAXREMOVETIME) @@ -109,14 +106,14 @@ void cRemoveDeletedRecordingsThread::Action(void) if (cRemote::HasKeys()) return; // react immediately on user input if (r->Deleted() && time(NULL) - r->Deleted() > DELETEDLIFETIME) { - cRecording *next = DeletedRecordings.Next(r); + cRecording *next = DeletedRecordings->Next(r); r->Remove(); - DeletedRecordings.Del(r); + DeletedRecordings->Del(r); r = next; deleted = true; continue; } - r = DeletedRecordings.Next(r); + r = DeletedRecordings->Next(r); } if (deleted) { const char *IgnoreFiles[] = { SORTMODEFILE, NULL }; @@ -134,8 +131,8 @@ void RemoveDeletedRecordings(void) static time_t LastRemoveCheck = 0; if (time(NULL) - LastRemoveCheck > REMOVECHECKDELTA) { if (!RemoveDeletedRecordingsThread.Active()) { - cThreadLock DeletedRecordingsLock(&DeletedRecordings); - for (cRecording *r = DeletedRecordings.First(); r; r = DeletedRecordings.Next(r)) { + LOCK_DELETEDRECORDINGS_READ; + for (const cRecording *r = DeletedRecordings->First(); r; r = DeletedRecordings->Next(r)) { if (r->Deleted() && time(NULL) - r->Deleted() > DELETEDLIFETIME) { RemoveDeletedRecordingsThread.Start(); break; @@ -163,37 +160,43 @@ void AssertFreeDiskSpace(int Priority, bool Force) return; // Remove the oldest file that has been "deleted": isyslog("low disk space while recording, trying to remove a deleted recording..."); - cThreadLock DeletedRecordingsLock(&DeletedRecordings); - if (DeletedRecordings.Count()) { - cRecording *r = DeletedRecordings.First(); - cRecording *r0 = NULL; - while (r) { - if (r->IsOnVideoDirectoryFileSystem()) { // only remove recordings that will actually increase the free video disk space - if (!r0 || r->Start() < r0->Start()) - r0 = r; - } - r = DeletedRecordings.Next(r); - } - if (r0) { - if (r0->Remove()) - LastFreeDiskCheck += REMOVELATENCY / Factor; - DeletedRecordings.Del(r0); - return; - } - } - else { + int NumDeletedRecordings = 0; + { + LOCK_DELETEDRECORDINGS_WRITE; + NumDeletedRecordings = DeletedRecordings->Count(); + if (NumDeletedRecordings) { + cRecording *r = DeletedRecordings->First(); + cRecording *r0 = NULL; + while (r) { + if (r->IsOnVideoDirectoryFileSystem()) { // only remove recordings that will actually increase the free video disk space + if (!r0 || r->Start() < r0->Start()) + r0 = r; + } + r = DeletedRecordings->Next(r); + } + if (r0) { + if (r0->Remove()) + LastFreeDiskCheck += REMOVELATENCY / Factor; + DeletedRecordings->Del(r0); + return; + } + } + } + if (NumDeletedRecordings == 0) { // DeletedRecordings was empty, so to be absolutely sure there are no // deleted recordings we need to double check: - DeletedRecordings.Update(true); - if (DeletedRecordings.Count()) + cRecordings::Update(true); + LOCK_DELETEDRECORDINGS_READ; + if (DeletedRecordings->Count()) return; // the next call will actually remove it } // No "deleted" files to remove, so let's see if we can delete a recording: if (Priority > 0) { isyslog("...no deleted recording found, trying to delete an old recording..."); - cThreadLock RecordingsLock(&Recordings); - if (Recordings.Count()) { - cRecording *r = Recordings.First(); + LOCK_RECORDINGS_WRITE; + Recordings->SetExplicitModify(); + if (Recordings->Count()) { + cRecording *r = Recordings->First(); cRecording *r0 = NULL; while (r) { if (r->IsOnVideoDirectoryFileSystem()) { // only delete recordings that will actually increase the free video disk space @@ -209,10 +212,11 @@ void AssertFreeDiskSpace(int Priority, bool Force) } } } - r = Recordings.Next(r); + r = Recordings->Next(r); } if (r0 && r0->Delete()) { - Recordings.Del(r0); + Recordings->Del(r0); + Recordings->SetModified(); return; } } @@ -227,14 +231,6 @@ void AssertFreeDiskSpace(int Priority, bool Force) } } -// --- Clear vanished recordings --------------------------------------------- - -void ClearVanishedRecordings(void) -{ - cThreadLock RecordingsLock(&Recordings); // yes, it *is* Recordings! - VanishedRecordings.Clear(); -} - // --- cResumeFile ----------------------------------------------------------- cResumeFile::cResumeFile(const char *FileName, bool IsPesRecording) @@ -309,7 +305,8 @@ bool cResumeFile::Save(int Index) if (safe_write(f, &Index, sizeof(Index)) < 0) LOG_ERROR_STR(fileName); close(f); - Recordings.ResetResume(fileName); + LOCK_RECORDINGS_WRITE; + Recordings->ResetResume(fileName); return true; } } @@ -318,7 +315,8 @@ bool cResumeFile::Save(int Index) if (f) { fprintf(f, "I %d\n", Index); fclose(f); - Recordings.ResetResume(fileName); + LOCK_RECORDINGS_WRITE; + Recordings->ResetResume(fileName); } else LOG_ERROR_STR(fileName); @@ -331,8 +329,10 @@ bool cResumeFile::Save(int Index) void cResumeFile::Delete(void) { if (fileName) { - if (remove(fileName) == 0) - Recordings.ResetResume(fileName); + if (remove(fileName) == 0) { + LOCK_RECORDINGS_WRITE; + Recordings->ResetResume(fileName); + } else if (errno != ENOENT) LOG_ERROR_STR(fileName); } @@ -787,10 +787,8 @@ cRecording::cRecording(cTimer *Timer, const cEvent *Event) else break; } - if (Timer->IsSingleEvent()) { + if (Timer->IsSingleEvent()) Timer->SetFile(name); // this was an instant recording, so let's set the actual data - Timers.SetModified(); - } } else if (Timer->IsSingleEvent() || !Setup.UseSubtitle) name = strdup(Timer->File()); @@ -1017,7 +1015,7 @@ int cRecording::Compare(const cListObject &ListObject) const return strcasecmp(SortName(), r->SortName()); } -bool cRecording::IsInPath(const char *Path) +bool cRecording::IsInPath(const char *Path) const { if (isempty(Path)) return true; @@ -1154,20 +1152,14 @@ bool cRecording::IsOnVideoDirectoryFileSystem(void) const return isOnVideoDirectoryFileSystem; } -bool cRecording::HasMarks(void) +bool cRecording::HasMarks(void) const { return access(cMarks::MarksFileName(this), F_OK) == 0; } bool cRecording::DeleteMarks(void) { - if (remove(cMarks::MarksFileName(this)) < 0) { - if (errno != ENOENT) { - LOG_ERROR_STR(fileName); - return false; - } - } - return true; + return cMarks::DeleteMarksFile(this); } void cRecording::ReadInfo(void) @@ -1219,8 +1211,6 @@ bool cRecording::ChangePriorityLifetime(int NewPriority, int NewLifetime) if (!WriteInfo()) return false; } - Recordings.ChangeState(); - Recordings.TouchUpdate(); } return true; } @@ -1245,8 +1235,6 @@ bool cRecording::ChangeName(const char *NewName) } isOnVideoDirectoryFileSystem = -1; // it might have been moved to a different file system ClearSortName(); - Recordings.ChangeState(); - Recordings.TouchUpdate(); } return true; } @@ -1360,58 +1348,54 @@ int cRecording::FileSizeMB(void) const return fileSizeMB; } -// --- cRecordings ----------------------------------------------------------- +// --- cVideoDirectoryScannerThread ------------------------------------------ -cRecordings Recordings; +class cVideoDirectoryScannerThread : public cThread { +private: + cRecordings *recordings; + cRecordings *deletedRecordings; + bool initial; + void ScanVideoDir(const char *DirName, int LinkLevel = 0, int DirLevel = 0); +protected: + virtual void Action(void); +public: + cVideoDirectoryScannerThread(cRecordings *Recordings, cRecordings *DeletedRecordings); + ~cVideoDirectoryScannerThread(); + }; -char *cRecordings::updateFileName = NULL; - -cRecordings::cRecordings(bool Deleted) -:cThread("video directory scanner") +cVideoDirectoryScannerThread::cVideoDirectoryScannerThread(cRecordings *Recordings, cRecordings *DeletedRecordings) +:cThread("video directory scanner", true) { - deleted = Deleted; + recordings = Recordings; + deletedRecordings = DeletedRecordings; initial = true; - lastUpdate = 0; - state = 0; } -cRecordings::~cRecordings() +cVideoDirectoryScannerThread::~cVideoDirectoryScannerThread() { Cancel(3); } -void cRecordings::Action(void) +void cVideoDirectoryScannerThread::Action(void) { - Refresh(); + cStateKey StateKey; + recordings->Lock(StateKey); + initial = recordings->Count() == 0; // no name checking if the list is initially empty + StateKey.Remove(); + deletedRecordings->Lock(StateKey, true); + deletedRecordings->Clear(); + StateKey.Remove(); + ScanVideoDir(cVideoDirectory::Name()); } -const char *cRecordings::UpdateFileName(void) +void cVideoDirectoryScannerThread::ScanVideoDir(const char *DirName, int LinkLevel, int DirLevel) { - if (!updateFileName) - updateFileName = strdup(AddDirectory(cVideoDirectory::Name(), ".update")); - return updateFileName; -} - -void cRecordings::Refresh(bool Foreground) -{ - lastUpdate = time(NULL); // doing this first to make sure we don't miss anything - initial = Count() == 0; // no name checking if the list is initially empty - if (deleted) { - Lock(); - Clear(); - ChangeState(); - Unlock(); - } - ScanVideoDir(cVideoDirectory::Name(), Foreground); -} - -bool cRecordings::ScanVideoDir(const char *DirName, bool Foreground, int LinkLevel, int DirLevel) -{ - bool DoChangeState = false; // Find any new recordings: cReadDir d(DirName); struct dirent *e; - while ((Foreground || Running()) && (e = d.Next()) != NULL) { + while (Running() && (e = d.Next()) != NULL) { + if (cIoThrottle::Engaged()) + cCondWait::SleepMs(100); cString buffer = AddDirectory(DirName, e->d_name); struct stat st; if (lstat(buffer, &st) == 0) { @@ -1426,57 +1410,73 @@ bool cRecordings::ScanVideoDir(const char *DirName, bool Foreground, int LinkLev continue; } if (S_ISDIR(st.st_mode)) { - if (endswith(buffer, deleted ? DELEXT : RECEXT)) { - if (deleted || initial || !GetByName(buffer)) { + cRecordings *Recordings = NULL; + if (endswith(buffer, RECEXT)) + Recordings = recordings; + else if (endswith(buffer, DELEXT)) + Recordings = deletedRecordings; + if (Recordings) { + cStateKey StateKey; + Recordings->Lock(StateKey, true); + if (Recordings == deletedRecordings || initial || !Recordings->GetByName(buffer)) { cRecording *r = new cRecording(buffer); if (r->Name()) { r->NumFrames(); // initializes the numFrames member r->FileSizeMB(); // initializes the fileSizeMB member r->IsOnVideoDirectoryFileSystem(); // initializes the isOnVideoDirectoryFileSystem member - if (deleted) - r->deleted = time(NULL); - Lock(); - Add(r); - if (initial) - ChangeState(); - else - DoChangeState = true; - Unlock(); + if (Recordings == deletedRecordings) + r->SetDeleted(); + Recordings->Add(r); } else delete r; } + StateKey.Remove(); } else - DoChangeState |= ScanVideoDir(buffer, Foreground, LinkLevel + Link, DirLevel + 1); + ScanVideoDir(buffer, LinkLevel + Link, DirLevel + 1); } } } // Handle any vanished recordings: - if (!deleted && !initial && DirLevel == 0) { - for (cRecording *recording = First(); recording; ) { - cRecording *r = recording; - recording = Next(recording); - if (access(r->FileName(), F_OK) != 0) { - Lock(); - Del(r, false); - VanishedRecordings.Add(r); - DoChangeState = true; - Unlock(); - } + if (!initial && DirLevel == 0) { + cStateKey StateKey; + recordings->Lock(StateKey, true); + for (cRecording *Recording = recordings->First(); Recording; ) { + cRecording *r = Recording; + Recording = recordings->Next(Recording); + if (access(r->FileName(), F_OK) != 0) + recordings->Del(r); } + StateKey.Remove(); } - if (DoChangeState && DirLevel == 0) - ChangeState(); - return DoChangeState; } -bool cRecordings::StateChanged(int &State) +// --- cRecordings ----------------------------------------------------------- + +cRecordings cRecordings::recordings; +cRecordings cRecordings::deletedRecordings(true); +char *cRecordings::updateFileName = NULL; +cVideoDirectoryScannerThread *cRecordings::videoDirectoryScannerThread = NULL; +time_t cRecordings::lastUpdate = 0; + +cRecordings::cRecordings(bool Deleted) +:cList(Deleted ? "DelRecs" : "Recordings") { - int NewState = state; - bool Result = State != NewState; - State = state; - return Result; +} + +cRecordings::~cRecordings() +{ + // The first one to be destructed deletes it: + delete videoDirectoryScannerThread; + videoDirectoryScannerThread = NULL; +} + +const char *cRecordings::UpdateFileName(void) +{ + if (!updateFileName) + updateFileName = strdup(AddDirectory(cVideoDirectory::Name(), ".update")); + return updateFileName; } void cRecordings::TouchUpdate(void) @@ -1495,24 +1495,24 @@ bool cRecordings::NeedsUpdate(void) return lastUpdate < lastModified; } -bool cRecordings::Update(bool Wait) +void cRecordings::Update(bool Wait) { + if (!videoDirectoryScannerThread) + videoDirectoryScannerThread = new cVideoDirectoryScannerThread(&recordings, &deletedRecordings); + lastUpdate = time(NULL); // doing this first to make sure we don't miss anything + videoDirectoryScannerThread->Start(); if (Wait) { - Refresh(true); - return Count() > 0; + while (videoDirectoryScannerThread->Active()) + cCondWait::SleepMs(100); } - else - Start(); - return false; } -cRecording *cRecordings::GetByName(const char *FileName) +const cRecording *cRecordings::GetByName(const char *FileName) const { if (FileName) { - LOCK_THREAD; - for (cRecording *recording = First(); recording; recording = Next(recording)) { - if (strcmp(recording->FileName(), FileName) == 0) - return recording; + for (const cRecording *Recording = First(); Recording; Recording = Next(Recording)) { + if (strcmp(Recording->FileName(), FileName) == 0) + return Recording; } } return NULL; @@ -1520,12 +1520,8 @@ cRecording *cRecordings::GetByName(const char *FileName) void cRecordings::AddByName(const char *FileName, bool TriggerUpdate) { - LOCK_THREAD; - cRecording *recording = GetByName(FileName); - if (!recording) { - recording = new cRecording(FileName); - Add(recording); - ChangeState(); + if (!GetByName(FileName)) { + Add(new cRecording(FileName)); if (TriggerUpdate) TouchUpdate(); } @@ -1533,58 +1529,52 @@ void cRecordings::AddByName(const char *FileName, bool TriggerUpdate) void cRecordings::DelByName(const char *FileName) { - LOCK_THREAD; - cRecording *recording = GetByName(FileName); + cRecording *Recording = GetByName(FileName); cRecording *dummy = NULL; - if (!recording) - recording = dummy = new cRecording(FileName); // allows us to use a FileName that is not in the Recordings list - cThreadLock DeletedRecordingsLock(&DeletedRecordings); + if (!Recording) + Recording = dummy = new cRecording(FileName); // allows us to use a FileName that is not in the Recordings list + LOCK_DELETEDRECORDINGS_WRITE; if (!dummy) - Del(recording, false); - char *ext = strrchr(recording->fileName, '.'); + Del(Recording, false); + char *ext = strrchr(Recording->fileName, '.'); if (ext) { strncpy(ext, DELEXT, strlen(ext)); - if (access(recording->FileName(), F_OK) == 0) { - recording->deleted = time(NULL); - DeletedRecordings.Add(recording); - recording = NULL; // to prevent it from being deleted below + if (access(Recording->FileName(), F_OK) == 0) { + Recording->SetDeleted(); + DeletedRecordings->Add(Recording); + Recording = NULL; // to prevent it from being deleted below } } - delete recording; - ChangeState(); + delete Recording; TouchUpdate(); } void cRecordings::UpdateByName(const char *FileName) { - LOCK_THREAD; - cRecording *recording = GetByName(FileName); - if (recording) - recording->ReadInfo(); + if (cRecording *Recording = GetByName(FileName)) + Recording->ReadInfo(); } -int cRecordings::TotalFileSizeMB(void) +int cRecordings::TotalFileSizeMB(void) const { int size = 0; - LOCK_THREAD; - for (cRecording *recording = First(); recording; recording = Next(recording)) { - int FileSizeMB = recording->FileSizeMB(); - if (FileSizeMB > 0 && recording->IsOnVideoDirectoryFileSystem()) + for (const cRecording *Recording = First(); Recording; Recording = Next(Recording)) { + int FileSizeMB = Recording->FileSizeMB(); + if (FileSizeMB > 0 && Recording->IsOnVideoDirectoryFileSystem()) size += FileSizeMB; } return size; } -double cRecordings::MBperMinute(void) +double cRecordings::MBperMinute(void) const { int size = 0; int length = 0; - LOCK_THREAD; - for (cRecording *recording = First(); recording; recording = Next(recording)) { - if (recording->IsOnVideoDirectoryFileSystem()) { - int FileSizeMB = recording->FileSizeMB(); + for (const cRecording *Recording = First(); Recording; Recording = Next(Recording)) { + if (Recording->IsOnVideoDirectoryFileSystem()) { + int FileSizeMB = Recording->FileSizeMB(); if (FileSizeMB > 0) { - int LengthInSeconds = recording->LengthInSeconds(); + int LengthInSeconds = Recording->LengthInSeconds(); if (LengthInSeconds > 0) { if (LengthInSeconds / FileSizeMB < LIMIT_SECS_PER_MB_RADIO) { // don't count radio recordings size += FileSizeMB; @@ -1597,23 +1587,21 @@ double cRecordings::MBperMinute(void) return (size && length) ? double(size) * 60 / length : -1; } -int cRecordings::PathIsInUse(const char *Path) +int cRecordings::PathIsInUse(const char *Path) const { - LOCK_THREAD; int Use = ruNone; - for (cRecording *recording = First(); recording; recording = Next(recording)) { - if (recording->IsInPath(Path)) - Use |= recording->IsInUse(); + for (const cRecording *Recording = First(); Recording; Recording = Next(Recording)) { + if (Recording->IsInPath(Path)) + Use |= Recording->IsInUse(); } return Use; } -int cRecordings::GetNumRecordingsInPath(const char *Path) +int cRecordings::GetNumRecordingsInPath(const char *Path) const { - LOCK_THREAD; int n = 0; - for (cRecording *recording = First(); recording; recording = Next(recording)) { - if (recording->IsInPath(Path)) + for (const cRecording *Recording = First(); Recording; Recording = Next(Recording)) { + if (Recording->IsInPath(Path)) n++; } return n; @@ -1622,36 +1610,35 @@ int cRecordings::GetNumRecordingsInPath(const char *Path) bool cRecordings::MoveRecordings(const char *OldPath, const char *NewPath) { if (OldPath && NewPath && strcmp(OldPath, NewPath)) { - LOCK_THREAD; dsyslog("moving '%s' to '%s'", OldPath, NewPath); - for (cRecording *recording = First(); recording; recording = Next(recording)) { - if (recording->IsInPath(OldPath)) { - const char *p = recording->Name() + strlen(OldPath); + bool Moved = false; + for (cRecording *Recording = First(); Recording; Recording = Next(Recording)) { + if (Recording->IsInPath(OldPath)) { + const char *p = Recording->Name() + strlen(OldPath); cString NewName = cString::sprintf("%s%s", NewPath, p); - if (!recording->ChangeName(NewName)) + if (!Recording->ChangeName(NewName)) return false; - ChangeState(); + Moved = true; } } + if (Moved) + TouchUpdate(); } return true; } void cRecordings::ResetResume(const char *ResumeFileName) { - LOCK_THREAD; - for (cRecording *recording = First(); recording; recording = Next(recording)) { - if (!ResumeFileName || strncmp(ResumeFileName, recording->FileName(), strlen(recording->FileName())) == 0) - recording->ResetResume(); + for (cRecording *Recording = First(); Recording; Recording = Next(Recording)) { + if (!ResumeFileName || strncmp(ResumeFileName, Recording->FileName(), strlen(Recording->FileName())) == 0) + Recording->ResetResume(); } - ChangeState(); } void cRecordings::ClearSortNames(void) { - LOCK_THREAD; - for (cRecording *recording = First(); recording; recording = Next(recording)) - recording->ClearSortName(); + for (cRecording *Recording = First(); Recording; Recording = Next(Recording)) + Recording->ClearSortName(); } // --- cDirCopier ------------------------------------------------------------ @@ -1810,8 +1797,9 @@ void cDirCopier::Stop(void) Cancel(3); if (error) { cVideoDirectory::RemoveVideoFile(dirNameDst); - Recordings.AddByName(dirNameSrc); - Recordings.DelByName(dirNameDst); + LOCK_RECORDINGS_WRITE; + Recordings->AddByName(dirNameSrc); + Recordings->DelByName(dirNameDst); } } @@ -1891,17 +1879,19 @@ bool cRecordingsHandlerEntry::Active(bool &Error) copier->Start(); } ClearPending(); - Recordings.ChangeState(); + LOCK_RECORDINGS_WRITE; // to trigger a state change return true; } // Clean up: if (CopierFinishedOk && (Usage() & ruMove) != 0) { cRecording Recording(FileNameSrc()); - if (Recording.Delete()) - Recordings.DelByName(Recording.FileName()); + if (Recording.Delete()) { + LOCK_RECORDINGS_WRITE; + Recordings->DelByName(Recording.FileName()); + } } - Recordings.ChangeState(); - Recordings.TouchUpdate(); + LOCK_RECORDINGS_WRITE; // to trigger a state change + Recordings->TouchUpdate(); return false; } @@ -1945,7 +1935,7 @@ bool cRecordingsHandler::Add(int Usage, const char *FileNameSrc, const char *Fil operations.Add(new cRecordingsHandlerEntry(Usage, FileNameSrc, FileNameDst)); finished = false; Active(); // start it right away if possible - Recordings.ChangeState(); + LOCK_RECORDINGS_WRITE; // to trigger a state change return true; } else @@ -1967,7 +1957,7 @@ void cRecordingsHandler::Del(const char *FileName) cMutexLock MutexLock(&mutex); if (cRecordingsHandlerEntry *r = Get(FileName)) { operations.Del(r); - Recordings.ChangeState(); + LOCK_RECORDINGS_WRITE; // to trigger a state change } } @@ -1975,7 +1965,7 @@ void cRecordingsHandler::DelAll(void) { cMutexLock MutexLock(&mutex); operations.Clear(); - Recordings.ChangeState(); + LOCK_RECORDINGS_WRITE; // to trigger a state change } int cRecordingsHandler::GetUsage(const char *FileName) @@ -2028,7 +2018,7 @@ cMark::~cMark() cString cMark::ToText(void) { - return cString::sprintf("%s%s%s\n", *IndexToHMSF(position, true, framesPerSecond), Comment() ? " " : "", Comment() ? Comment() : ""); + return cString::sprintf("%s%s%s", *IndexToHMSF(position, true, framesPerSecond), Comment() ? " " : "", Comment() ? Comment() : ""); } bool cMark::Parse(const char *s) @@ -2047,7 +2037,7 @@ bool cMark::Parse(const char *s) bool cMark::Save(FILE *f) { - return fprintf(f, "%s", *ToText()) > 0; + return fprintf(f, "%s\n", *ToText()) > 0; } // --- cMarks ---------------------------------------------------------------- @@ -2057,9 +2047,19 @@ cString cMarks::MarksFileName(const cRecording *Recording) return AddDirectory(Recording->FileName(), Recording->IsPesRecording() ? MARKSFILESUFFIX ".vdr" : MARKSFILESUFFIX); } +bool cMarks::DeleteMarksFile(const cRecording *Recording) +{ + if (remove(cMarks::MarksFileName(Recording)) < 0) { + if (errno != ENOENT) { + LOG_ERROR_STR(Recording->FileName()); + return false; + } + } + return true; +} + bool cMarks::Load(const char *RecordingFileName, double FramesPerSecond, bool IsPesRecording) { - cMutexLock MutexLock(this); recordingFileName = RecordingFileName; fileName = AddDirectory(RecordingFileName, IsPesRecording ? MARKSFILESUFFIX ".vdr" : MARKSFILESUFFIX); framesPerSecond = FramesPerSecond; @@ -2072,7 +2072,6 @@ bool cMarks::Load(const char *RecordingFileName, double FramesPerSecond, bool Is bool cMarks::Update(void) { - cMutexLock MutexLock(this); time_t t = time(NULL); if (t > nextUpdate && *fileName) { time_t LastModified = LastModifiedTime(fileName); @@ -2104,7 +2103,6 @@ bool cMarks::Update(void) bool cMarks::Save(void) { - cMutexLock MutexLock(this); if (cConfig::Save()) { lastFileTime = LastModifiedTime(fileName); return true; @@ -2114,7 +2112,6 @@ bool cMarks::Save(void) void cMarks::Align(void) { - cMutexLock MutexLock(this); cIndexFile IndexFile(recordingFileName, false, isPesRecording); for (cMark *m = First(); m; m = Next(m)) { int p = IndexFile.GetClosestIFrame(m->Position()); @@ -2127,7 +2124,6 @@ void cMarks::Align(void) void cMarks::Sort(void) { - cMutexLock MutexLock(this); for (cMark *m1 = First(); m1; m1 = Next(m1)) { for (cMark *m2 = Next(m1); m2; m2 = Next(m2)) { if (m2->Position() < m1->Position()) { @@ -2140,43 +2136,42 @@ void cMarks::Sort(void) void cMarks::Add(int Position) { - cMutexLock MutexLock(this); cConfig::Add(new cMark(Position, NULL, framesPerSecond)); Sort(); } -cMark *cMarks::Get(int Position) +const cMark *cMarks::Get(int Position) const { - for (cMark *mi = First(); mi; mi = Next(mi)) { + for (const cMark *mi = First(); mi; mi = Next(mi)) { if (mi->Position() == Position) return mi; } return NULL; } -cMark *cMarks::GetPrev(int Position) +const cMark *cMarks::GetPrev(int Position) const { - for (cMark *mi = Last(); mi; mi = Prev(mi)) { + for (const cMark *mi = Last(); mi; mi = Prev(mi)) { if (mi->Position() < Position) return mi; } return NULL; } -cMark *cMarks::GetNext(int Position) +const cMark *cMarks::GetNext(int Position) const { - for (cMark *mi = First(); mi; mi = Next(mi)) { + for (const cMark *mi = First(); mi; mi = Next(mi)) { if (mi->Position() > Position) return mi; } return NULL; } -cMark *cMarks::GetNextBegin(cMark *EndMark) +const cMark *cMarks::GetNextBegin(const cMark *EndMark) const { - cMark *BeginMark = EndMark ? Next(EndMark) : First(); + const cMark *BeginMark = EndMark ? Next(EndMark) : First(); if (BeginMark && EndMark && BeginMark->Position() == EndMark->Position()) { - while (cMark *NextMark = Next(BeginMark)) { + while (const cMark *NextMark = Next(BeginMark)) { if (BeginMark->Position() == NextMark->Position()) { // skip Begin/End at the same position if (!(BeginMark = Next(NextMark))) break; @@ -2188,13 +2183,13 @@ cMark *cMarks::GetNextBegin(cMark *EndMark) return BeginMark; } -cMark *cMarks::GetNextEnd(cMark *BeginMark) +const cMark *cMarks::GetNextEnd(const cMark *BeginMark) const { if (!BeginMark) return NULL; - cMark *EndMark = Next(BeginMark); + const cMark *EndMark = Next(BeginMark); if (EndMark && BeginMark && BeginMark->Position() == EndMark->Position()) { - while (cMark *NextMark = Next(EndMark)) { + while (const cMark *NextMark = Next(EndMark)) { if (EndMark->Position() == NextMark->Position()) { // skip End/Begin at the same position if (!(EndMark = Next(NextMark))) break; @@ -2206,12 +2201,11 @@ cMark *cMarks::GetNextEnd(cMark *BeginMark) return EndMark; } -int cMarks::GetNumSequences(void) +int cMarks::GetNumSequences(void) const { - cMutexLock MutexLock(this); int NumSequences = 0; - if (cMark *BeginMark = GetNextBegin()) { - while (cMark *EndMark = GetNextEnd(BeginMark)) { + if (const cMark *BeginMark = GetNextBegin()) { + while (const cMark *EndMark = GetNextEnd(BeginMark)) { NumSequences++; BeginMark = GetNextBegin(EndMark); } @@ -2401,7 +2395,8 @@ void cIndexFileGenerator::Action(void) if (FrameDetector.FramesPerSecond() > 0 && !DoubleEqual(RecordingInfo.FramesPerSecond(), FrameDetector.FramesPerSecond())) { RecordingInfo.SetFramesPerSecond(FrameDetector.FramesPerSecond()); RecordingInfo.Write(); - Recordings.UpdateByName(recordingName); + LOCK_RECORDINGS_WRITE; + Recordings->UpdateByName(recordingName); } } Skins.QueueMessage(mtInfo, tr("Index file regeneration complete")); @@ -3018,7 +3013,7 @@ int ReadFrame(cUnbufferedFile *f, uchar *b, int Length, int Max) // --- Recordings Sort Mode -------------------------------------------------- -eRecordingsSortMode RecordingsSortMode = rsmName; +eRecordingsSortMode RecordingsSortMode = rsmTime; bool HasRecordingsSortMode(const char *Directory) { @@ -3027,7 +3022,7 @@ bool HasRecordingsSortMode(const char *Directory) void GetRecordingsSortMode(const char *Directory) { - RecordingsSortMode = rsmName; + RecordingsSortMode = eRecordingsSortMode(constrain(Setup.DefaultSortModeRec, 0, int(rsmTime))); if (FILE *f = fopen(AddDirectory(Directory, SORTMODEFILE), "r")) { char buf[8]; if (fgets(buf, sizeof(buf), f)) diff --git a/recording.h b/recording.h index 876e0b25..cebd2ee0 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 3.8 2015/02/07 14:29:14 kls Exp $ + * $Id: recording.h 4.3 2015/08/29 14:12:14 kls Exp $ */ #ifndef __RECORDING_H @@ -41,7 +41,6 @@ enum eRecordingUsage { }; void RemoveDeletedRecordings(void); -void ClearVanishedRecordings(void); void AssertFreeDiskSpace(int Priority = 0, bool Force = false); ///< The special Priority value -1 means that we shall get rid of any ///< deleted recordings faster than normal (because we're cutting). @@ -129,8 +128,9 @@ public: int Priority(void) const { return priority; } int Lifetime(void) const { return lifetime; } time_t Deleted(void) const { return deleted; } + void SetDeleted(void) { deleted = time(NULL); } virtual int Compare(const cListObject &ListObject) const; - bool IsInPath(const char *Path); + bool IsInPath(const char *Path) const; ///< Returns true if this recording is stored anywhere under the given Path. ///< If Path is NULL or an empty string, the entire video directory is checked. cString Folder(void) const; @@ -140,7 +140,7 @@ public: ///< Returns the base name of this recording (without the ///< video directory and folder). For use in menus etc. const char *Name(void) const { return name; } - ///< Returns the full name of the recording (without the video directory. + ///< Returns the full name of the recording (without the video directory). ///< For use in menus etc. const char *FileName(void) const; ///< Returns the full path name to the recording directory, including the @@ -166,7 +166,7 @@ public: bool IsEdited(void) const; bool IsPesRecording(void) const { return isPesRecording; } bool IsOnVideoDirectoryFileSystem(void) const; - bool HasMarks(void); + bool HasMarks(void) const; ///< Returns true if this recording has any editing marks. bool DeleteMarks(void); ///< Deletes the editing marks from this recording (if any). @@ -216,49 +216,54 @@ public: ///< as in time-shift). }; -class cRecordings : public cList, public cThread { +class cVideoDirectoryScannerThread; + +class cRecordings : public cList { private: + static cRecordings recordings; + static cRecordings deletedRecordings; static char *updateFileName; - bool deleted; - bool initial; - time_t lastUpdate; - int state; - const char *UpdateFileName(void); - void Refresh(bool Foreground = false); - bool ScanVideoDir(const char *DirName, bool Foreground = false, int LinkLevel = 0, int DirLevel = 0); -protected: - void Action(void); + static time_t lastUpdate; + static cVideoDirectoryScannerThread *videoDirectoryScannerThread; + static const char *UpdateFileName(void); public: cRecordings(bool Deleted = false); virtual ~cRecordings(); - bool Load(void) { return Update(true); } - ///< Loads the current list of recordings and returns true if there - ///< is anything in it (for compatibility with older plugins - use - ///< Update(true) instead). - bool Update(bool Wait = false); + static const cRecordings *GetRecordingsRead(cStateKey &StateKey, int TimeoutMs = 0) { return recordings.Lock(StateKey, false, TimeoutMs) ? &recordings : NULL; } + ///< Gets the list of recordings for read access. + ///< See cTimers::GetTimersRead() for details. + static cRecordings *GetRecordingsWrite(cStateKey &StateKey, int TimeoutMs = 0) { return recordings.Lock(StateKey, true, TimeoutMs) ? &recordings : NULL; } + ///< Gets the list of recordings for write access. + ///< See cTimers::GetTimersWrite() for details. + static const cRecordings *GetDeletedRecordingsRead(cStateKey &StateKey, int TimeoutMs = 0) { return deletedRecordings.Lock(StateKey, false, TimeoutMs) ? &deletedRecordings : NULL; } + ///< Gets the list of deleted recordings for read access. + ///< See cTimers::GetTimersRead() for details. + static cRecordings *GetDeletedRecordingsWrite(cStateKey &StateKey, int TimeoutMs = 0) { return deletedRecordings.Lock(StateKey, true, TimeoutMs) ? &deletedRecordings : NULL; } + ///< Gets the list of deleted recordings for write access. + ///< See cTimers::GetTimersWrite() for details. + static void Update(bool Wait = false); ///< Triggers an update of the list of recordings, which will run ///< as a separate thread if Wait is false. If Wait is true, the ///< function returns only after the update has completed. - ///< Returns true if Wait is true and there is anything in the list - ///< of recordings, false otherwise. - void TouchUpdate(void); + static void TouchUpdate(void); ///< Touches the '.update' file in the video directory, so that other ///< instances of VDR that access the same video directory can be triggered ///< to update their recordings list. - bool NeedsUpdate(void); - void ChangeState(void) { state++; } - bool StateChanged(int &State); + ///< This function is 'const', because it doesn't actually modify the list + ///< of recordings. + static bool NeedsUpdate(void); void ResetResume(const char *ResumeFileName = NULL); void ClearSortNames(void); - cRecording *GetByName(const char *FileName); + const cRecording *GetByName(const char *FileName) const; + cRecording *GetByName(const char *FileName) { return const_cast(static_cast(this)->GetByName(FileName)); } void AddByName(const char *FileName, bool TriggerUpdate = true); void DelByName(const char *FileName); void UpdateByName(const char *FileName); - int TotalFileSizeMB(void); - double MBperMinute(void); + int TotalFileSizeMB(void) const; + double MBperMinute(void) const; ///< Returns the average data rate (in MB/min) of all recordings, or -1 if ///< this value is unknown. - int PathIsInUse(const char *Path); + int PathIsInUse(const char *Path) const; ///< Checks whether any recording in the given Path is currently in use and therefore ///< the whole Path shall not be tampered with. Returns 0 (ruNone) if no recording ///< is in use. @@ -266,7 +271,7 @@ public: ///< If several recordings in the Path are currently in use, the return value will ///< be the combination of all individual recordings' flags. ///< If Path is NULL or an empty string, the entire video directory is checked. - int GetNumRecordingsInPath(const char *Path); + int GetNumRecordingsInPath(const char *Path) const; ///< Returns the total number of recordings in the given Path, including all ///< sub-folders of Path. ///< If Path is NULL or an empty string, the entire video directory is checked. @@ -281,10 +286,19 @@ public: ///< if all recordings have been successfully added to the RecordingsHandler. }; -/// Any access to Recordings that loops through the list of recordings -/// needs to hold a thread lock on this object! -extern cRecordings Recordings; -extern cRecordings DeletedRecordings; +// Provide lock controlled access to the list: + +DEF_LIST_LOCK(Recordings); +DEF_LIST_LOCK2(Recordings, DeletedRecordings); + +// These macros provide a convenient way of locking the global recordings list +// and making sure the lock is released as soon as the current scope is left +// (note that these macros wait forever to obtain the lock!): + +#define LOCK_RECORDINGS_READ USE_LIST_LOCK_READ(Recordings) +#define LOCK_RECORDINGS_WRITE USE_LIST_LOCK_WRITE(Recordings) +#define LOCK_DELETEDRECORDINGS_READ USE_LIST_LOCK_READ2(Recordings, DeletedRecordings) +#define LOCK_DELETEDRECORDINGS_WRITE USE_LIST_LOCK_WRITE2(Recordings, DeletedRecordings) class cRecordingsHandlerEntry; @@ -350,7 +364,7 @@ public: bool Save(FILE *f); }; -class cMarks : public cConfig, public cMutex { +class cMarks : public cConfig { private: cString recordingFileName; cString fileName; @@ -360,9 +374,11 @@ private: time_t lastFileTime; time_t lastChange; public: + cMarks(void): cConfig("Marks") {}; static cString MarksFileName(const cRecording *Recording); ///< Returns the marks file name for the given Recording (regardless whether such ///< a file actually exists). + static bool DeleteMarksFile(const cRecording *Recording); bool Load(const char *RecordingFileName, double FramesPerSecond = DEFAULTFRAMESPERSECOND, bool IsPesRecording = false); bool Update(void); bool Save(void); @@ -374,29 +390,35 @@ public: ///< calls to Del(), or any of the functions that return a "cMark *", in case ///< an other thread might modifiy the list while the returned pointer is ///< considered valid. - cMark *Get(int Position); - cMark *GetPrev(int Position); - cMark *GetNext(int Position); - cMark *GetNextBegin(cMark *EndMark = NULL); + const cMark *Get(int Position) const; + const cMark *GetPrev(int Position) const; + const cMark *GetNext(int Position) const; + const cMark *GetNextBegin(const cMark *EndMark = NULL) const; ///< Returns the next "begin" mark after EndMark, skipping any marks at the ///< same position as EndMark. If EndMark is NULL, the first actual "begin" ///< will be returned (if any). - cMark *GetNextEnd(cMark *BeginMark); + const cMark *GetNextEnd(const cMark *BeginMark) const; ///< Returns the next "end" mark after BeginMark, skipping any marks at the ///< same position as BeginMark. - int GetNumSequences(void); + int GetNumSequences(void) const; ///< Returns the actual number of sequences to be cut from the recording. ///< If there is only one actual "begin" mark, and it is positioned at index ///< 0 (the beginning of the recording), and there is no "end" mark, the ///< return value is 0, which means that the result is the same as the original ///< recording. + cMark *Get(int Position) { return const_cast(static_cast(this)->Get(Position)); } + cMark *GetPrev(int Position) { return const_cast(static_cast(this)->GetPrev(Position)); } + cMark *GetNext(int Position) { return const_cast(static_cast(this)->GetNext(Position)); } + cMark *GetNextBegin(const cMark *EndMark = NULL) { return const_cast(static_cast(this)->GetNextBegin(EndMark)); } + cMark *GetNextEnd(const cMark *BeginMark) { return const_cast(static_cast(this)->GetNextEnd(BeginMark)); } }; -#define RUC_BEFORERECORDING "before" -#define RUC_STARTRECORDING "started" -#define RUC_AFTERRECORDING "after" -#define RUC_EDITEDRECORDING "edited" -#define RUC_DELETERECORDING "deleted" +#define RUC_BEFORERECORDING "before" +#define RUC_STARTRECORDING "started" +#define RUC_AFTERRECORDING "after" +#define RUC_EDITINGRECORDING "editing" +#define RUC_EDITEDRECORDING "edited" +#define RUC_DELETERECORDING "deleted" class cRecordingUserCommand { private: diff --git a/remote.c b/remote.c index ac10dce7..85b67188 100644 --- a/remote.c +++ b/remote.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: remote.c 3.3 2015/01/20 14:53:57 kls Exp $ + * $Id: remote.c 4.0 2015/01/20 14:53:57 kls Exp $ */ #include "remote.h" diff --git a/remote.h b/remote.h index 415f2e77..5ad898c9 100644 --- a/remote.h +++ b/remote.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: remote.h 3.1 2013/12/25 12:32:44 kls Exp $ + * $Id: remote.h 4.0 2013/12/25 12:32:44 kls Exp $ */ #ifndef __REMOTE_H diff --git a/remux.c b/remux.c index 23e83877..6c07efcb 100644 --- a/remux.c +++ b/remux.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: remux.c 3.9 2015/01/14 09:57:09 kls Exp $ + * $Id: remux.c 4.1 2015/03/11 09:49:38 kls Exp $ */ #include "remux.h" @@ -1552,7 +1552,9 @@ int cFrameDetector::Analyze(const uchar *Data, int Length) uint32_t Delta = ptsValues[0] / Div; // determine frame info: if (isVideo) { - if (abs(Delta - 3600) <= 1) + if (Delta == 3753) + framesPerSecond = 24.0 / 1.001; + else if (abs(Delta - 3600) <= 1) framesPerSecond = 25.0; else if (Delta % 3003 == 0) framesPerSecond = 30.0 / 1.001; diff --git a/remux.h b/remux.h index 6bc91ad7..bd0d1459 100644 --- a/remux.h +++ b/remux.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: remux.h 3.4 2014/03/22 14:58:24 kls Exp $ + * $Id: remux.h 4.0 2014/03/22 14:58:24 kls Exp $ */ #ifndef __REMUX_H diff --git a/ringbuffer.c b/ringbuffer.c index e2921be2..05e2cf66 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 3.0 2012/09/22 11:26:49 kls Exp $ + * $Id: ringbuffer.c 4.0 2012/09/22 11:26:49 kls Exp $ */ #include "ringbuffer.h" diff --git a/ringbuffer.h b/ringbuffer.h index a9757421..70f22c69 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 3.0 2013/02/16 15:20:37 kls Exp $ + * $Id: ringbuffer.h 4.0 2013/02/16 15:20:37 kls Exp $ */ #ifndef __RINGBUFFER_H diff --git a/runvdr.template b/runvdr.template index 745db042..9597be65 100755 --- a/runvdr.template +++ b/runvdr.template @@ -20,7 +20,7 @@ # See the main source file 'vdr.c' for copyright information and # how to reach the author. # -# $Id: runvdr.template 3.1 2015/02/05 10:28:53 kls Exp $ +# $Id: runvdr.template 4.0 2015/02/05 10:28:53 kls Exp $ VDRPRG="./vdr" diff --git a/sdt.c b/sdt.c index 4e6748d8..05cd931b 100644 --- a/sdt.c +++ b/sdt.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: sdt.c 3.4 2015/01/04 14:33:35 kls Exp $ + * $Id: sdt.c 4.5 2015/08/02 11:33:23 kls Exp $ */ #include "sdt.h" @@ -13,6 +13,11 @@ #include "libsi/section.h" #include "libsi/descriptor.h" +// Set to 'true' for debug output: +static bool DebugSdt = false; + +#define dbgsdt(a...) if (DebugSdt) fprintf(stderr, a) + // --- cSdtFilter ------------------------------------------------------------ cSdtFilter::cSdtFilter(cPatFilter *PatFilter) @@ -47,15 +52,21 @@ void cSdtFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length return; if (!sectionSyncer.Sync(sdt.getVersionNumber(), sdt.getSectionNumber(), sdt.getLastSectionNumber())) return; - if (!Channels.Lock(true, 10)) + cStateKey StateKey; + cChannels *Channels = cChannels::GetChannelsWrite(StateKey, 10); + if (!Channels) { + sectionSyncer.Repeat(); // let's not miss any section of the SDT return; + } + dbgsdt("SDT: %2d %2d %2d %s %d\n", sdt.getVersionNumber(), sdt.getSectionNumber(), sdt.getLastSectionNumber(), *cSource::ToString(source), Transponder()); + bool ChannelsModified = false; SI::SDT::Service SiSdtService; for (SI::Loop::Iterator it; sdt.serviceLoop.getNext(SiSdtService, it); ) { - cChannel *channel = Channels.GetByChannelID(tChannelID(source, sdt.getOriginalNetworkId(), sdt.getTransportStreamId(), SiSdtService.getServiceId())); - if (!channel) - channel = Channels.GetByChannelID(tChannelID(source, 0, Transponder(), SiSdtService.getServiceId())); - if (channel) - channel->SetSeen(); + cChannel *Channel = Channels->GetByChannelID(tChannelID(source, sdt.getOriginalNetworkId(), sdt.getTransportStreamId(), SiSdtService.getServiceId())); + if (!Channel) + Channel = Channels->GetByChannelID(tChannelID(source, 0, Transponder(), SiSdtService.getServiceId())); + if (Channel) + Channel->SetSeen(); cLinkChannels *LinkChannels = NULL; SI::Descriptor *d; @@ -93,18 +104,21 @@ void cSdtFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length } sd->providerName.getText(ProviderNameBuf, sizeof(ProviderNameBuf)); char *pp = compactspace(ProviderNameBuf); - if (channel) { - channel->SetId(sdt.getOriginalNetworkId(), sdt.getTransportStreamId(), SiSdtService.getServiceId()); + if (Channel) { + ChannelsModified |= Channel->SetId(Channels, sdt.getOriginalNetworkId(), sdt.getTransportStreamId(), SiSdtService.getServiceId()); if (Setup.UpdateChannels == 1 || Setup.UpdateChannels >= 3) - channel->SetName(pn, ps, pp); + ChannelsModified |= Channel->SetName(pn, ps, pp); // Using SiSdtService.getFreeCaMode() is no good, because some // tv stations set this flag even for non-encrypted channels :-( // The special value 0xFFFF was supposed to mean "unknown encryption" // and would have been overwritten with real CA values later: - // channel->SetCa(SiSdtService.getFreeCaMode() ? 0xFFFF : 0); + // Channel->SetCa(SiSdtService.getFreeCaMode() ? 0xFFFF : 0); } else if (*pn && Setup.UpdateChannels >= 4) { - channel = Channels.NewChannel(Channel(), pn, ps, pp, sdt.getOriginalNetworkId(), sdt.getTransportStreamId(), SiSdtService.getServiceId()); + dbgsdt(" %5d %5d %5d %s/%s %d %s\n", sdt.getOriginalNetworkId(), sdt.getTransportStreamId(), SiSdtService.getServiceId(), *cSource::ToString(this->Channel()->Source()), *cSource::ToString(source), this->Channel()->Transponder(), pn); + Channel = Channels->NewChannel(this->Channel(), pn, ps, pp, sdt.getOriginalNetworkId(), sdt.getTransportStreamId(), SiSdtService.getServiceId()); + Channel->SetSource(source); // in case this comes from a satellite with a slightly different position + ChannelsModified = true; patFilter->Trigger(SiSdtService.getServiceId()); } } @@ -117,9 +131,9 @@ void cSdtFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length /* case SI::CaIdentifierDescriptorTag: { SI::CaIdentifierDescriptor *cid = (SI::CaIdentifierDescriptor *)d; - if (channel) { + if (Channel) { for (SI::Loop::Iterator it; cid->identifiers.hasNext(it); ) - channel->SetCa(cid->identifiers.getNext(it)); + Channel->SetCa(Channels, cid->identifiers.getNext(it)); } } break; @@ -128,15 +142,17 @@ void cSdtFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length SI::NVODReferenceDescriptor *nrd = (SI::NVODReferenceDescriptor *)d; SI::NVODReferenceDescriptor::Service Service; for (SI::Loop::Iterator it; nrd->serviceLoop.getNext(Service, it); ) { - cChannel *link = Channels.GetByChannelID(tChannelID(source, Service.getOriginalNetworkId(), Service.getTransportStream(), Service.getServiceId())); + cChannel *link = Channels->GetByChannelID(tChannelID(source, Service.getOriginalNetworkId(), Service.getTransportStream(), Service.getServiceId())); if (!link && Setup.UpdateChannels >= 4) { - link = Channels.NewChannel(Channel(), "NVOD", "", "", Service.getOriginalNetworkId(), Service.getTransportStream(), Service.getServiceId()); + link = Channels->NewChannel(this->Channel(), "NVOD", "", "", Service.getOriginalNetworkId(), Service.getTransportStream(), Service.getServiceId()); patFilter->Trigger(Service.getServiceId()); + ChannelsModified = true; } if (link) { if (!LinkChannels) LinkChannels = new cLinkChannels; LinkChannels->Add(new cLinkChannel(link)); + ChannelsModified = true; } } } @@ -146,15 +162,18 @@ void cSdtFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length delete d; } if (LinkChannels) { - if (channel) - channel->SetLinkChannels(LinkChannels); + if (Channel) + ChannelsModified |= Channel->SetLinkChannels(LinkChannels); else delete LinkChannels; } } if (sdt.getSectionNumber() == sdt.getLastSectionNumber()) { - if (Setup.UpdateChannels == 1 || Setup.UpdateChannels >= 3) - Channels.MarkObsoleteChannels(Source(), sdt.getOriginalNetworkId(), sdt.getTransportStreamId()); + if (Setup.UpdateChannels == 1 || Setup.UpdateChannels >= 3) { + ChannelsModified |= Channels->MarkObsoleteChannels(source, sdt.getOriginalNetworkId(), sdt.getTransportStreamId()); + if (source != Source()) + ChannelsModified |= Channels->MarkObsoleteChannels(Source(), sdt.getOriginalNetworkId(), sdt.getTransportStreamId()); + } } - Channels.Unlock(); + StateKey.Remove(ChannelsModified); } diff --git a/sdt.h b/sdt.h index 01653f45..c46c9b73 100644 --- a/sdt.h +++ b/sdt.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: sdt.h 3.1 2014/03/10 14:40:54 kls Exp $ + * $Id: sdt.h 4.0 2014/03/10 14:40:54 kls Exp $ */ #ifndef __SDT_H diff --git a/sections.c b/sections.c index 32d80de9..55447bf1 100644 --- a/sections.c +++ b/sections.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: sections.c 3.1 2015/01/14 11:35:53 kls Exp $ + * $Id: sections.c 4.0 2015/01/14 11:35:53 kls Exp $ */ #include "sections.h" diff --git a/sections.h b/sections.h index 7cb833ce..de0a3881 100644 --- a/sections.h +++ b/sections.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: sections.h 3.0 2005/08/13 11:23:55 kls Exp $ + * $Id: sections.h 4.0 2005/08/13 11:23:55 kls Exp $ */ #ifndef __SECTIONS_H diff --git a/shutdown.c b/shutdown.c index 97d056e9..7b8ff804 100644 --- a/shutdown.c +++ b/shutdown.c @@ -6,7 +6,7 @@ * * Original version written by Udo Richter . * - * $Id: shutdown.c 3.1 2013/10/02 09:02:01 kls Exp $ + * $Id: shutdown.c 4.1 2015/07/18 11:29:26 kls Exp $ */ #include "shutdown.h" @@ -172,9 +172,10 @@ bool cShutdownHandler::ConfirmShutdown(bool Interactive) return false; } - cTimer *timer = Timers.GetNextActiveTimer(); - time_t Next = timer ? timer->StartTime() : 0; - time_t Delta = timer ? Next - time(NULL) : 0; + LOCK_TIMERS_READ; + const cTimer *Timer = Timers->GetNextActiveTimer(); + time_t Next = Timer ? Timer->StartTime() : 0; + time_t Delta = Timer ? Next - time(NULL) : 0; if (cRecordControls::Active() || (Next && Delta <= 0)) { // VPS recordings in timer end margin may cause Delta <= 0 @@ -215,9 +216,10 @@ bool cShutdownHandler::ConfirmRestart(bool Interactive) return false; } - cTimer *timer = Timers.GetNextActiveTimer(); - time_t Next = timer ? timer->StartTime() : 0; - time_t Delta = timer ? Next - time(NULL) : 0; + LOCK_TIMERS_READ; + const cTimer *Timer = Timers->GetNextActiveTimer(); + time_t Next = Timer ? Timer->StartTime() : 0; + time_t Delta = Timer ? Next - time(NULL) : 0; if (cRecordControls::Active() || (Next && Delta <= 0)) { // VPS recordings in timer end margin may cause Delta <= 0 @@ -233,15 +235,16 @@ bool cShutdownHandler::ConfirmRestart(bool Interactive) bool cShutdownHandler::DoShutdown(bool Force) { + LOCK_TIMERS_READ; time_t Now = time(NULL); - cTimer *timer = Timers.GetNextActiveTimer(); + const cTimer *Timer = Timers->GetNextActiveTimer(); cPlugin *Plugin = cPluginManager::GetNextWakeupPlugin(); - time_t Next = timer ? timer->StartTime() : 0; + time_t Next = Timer ? Timer->StartTime() : 0; time_t NextPlugin = Plugin ? Plugin->WakeupTime() : 0; if (NextPlugin && (!Next || Next > NextPlugin)) { Next = NextPlugin; - timer = NULL; + Timer = NULL; } time_t Delta = Next ? Next - Now : 0; @@ -250,13 +253,13 @@ bool cShutdownHandler::DoShutdown(bool Force) return false; Delta = Setup.MinEventTimeout * 60; Next = Now + Delta; - timer = NULL; + Timer = NULL; dsyslog("reboot at %s", *TimeToString(Next)); } - if (Next && timer) { + if (Next && Timer) { dsyslog("next timer event at %s", *TimeToString(Next)); - CallShutdownCommand(Next, timer->Channel()->Number(), timer->File(), Force); + CallShutdownCommand(Next, Timer->Channel()->Number(), Timer->File(), Force); } else if (Next && Plugin) { CallShutdownCommand(Next, 0, Plugin->Name(), Force); diff --git a/shutdown.h b/shutdown.h index 5e4af29f..7bf886ac 100644 --- a/shutdown.h +++ b/shutdown.h @@ -6,7 +6,7 @@ * * Original version written by Udo Richter . * - * $Id: shutdown.h 3.0 2013/02/18 10:35:27 kls Exp $ + * $Id: shutdown.h 4.0 2013/02/18 10:35:27 kls Exp $ */ #ifndef __SHUTDOWN_H diff --git a/skinclassic.c b/skinclassic.c index fa384315..b6d183b1 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 3.0 2013/03/03 15:26:09 kls Exp $ + * $Id: skinclassic.c 4.0 2013/03/03 15:26:09 kls Exp $ */ #include "skinclassic.h" diff --git a/skinclassic.h b/skinclassic.h index 005901b4..57e96f8a 100644 --- a/skinclassic.h +++ b/skinclassic.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: skinclassic.h 3.0 2005/01/02 14:38:56 kls Exp $ + * $Id: skinclassic.h 4.0 2005/01/02 14:38:56 kls Exp $ */ #ifndef __SKINCLASSIC_H diff --git a/skinlcars.c b/skinlcars.c index a722b521..d84a753f 100644 --- a/skinlcars.c +++ b/skinlcars.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: skinlcars.c 3.8 2014/06/12 08:48:15 kls Exp $ + * $Id: skinlcars.c 4.1 2015/09/01 10:07:07 kls Exp $ */ // "Star Trek: The Next Generation"(R) is a registered trademark of Paramount Pictures, @@ -710,7 +710,7 @@ private: int lastDiskUsageState; bool lastDiskAlert; double lastSystemLoad; - int lastTimersState; + cStateKey timersStateKey; time_t lastSignalDisplay; int lastLiveIndicatorY; bool lastLiveIndicatorTransferring; @@ -775,7 +775,6 @@ cSkinLCARSDisplayMenu::cSkinLCARSDisplayMenu(void) lastEvent = NULL; lastRecording = NULL; lastSeen = -1; - lastTimersState = -1; lastSignalDisplay = 0; lastLiveIndicatorY = -1; lastLiveIndicatorTransferring = false; @@ -970,7 +969,7 @@ void cSkinLCARSDisplayMenu::SetMenuCategory(eMenuCategory MenuCategory) xi01 = xm03; xi02 = xm04; xi03 = xm05; - lastTimersState = -1; + timersStateKey.Reset(); DrawMainFrameLower(); DrawMainBracket(); DrawStatusElbows(); @@ -1237,19 +1236,22 @@ void cSkinLCARSDisplayMenu::DrawTimer(const cTimer *Timer, int y, bool MultiRec) } if (Event) osd->DrawText(xs00 + d, y + lineHeight - tinyFont->Height(), Event->Title(), ColorFg, ColorBg, tinyFont, xs03 - xs00 - 2 * d); + // The remote timer indicator: + if (Timer->Remote()) + osd->DrawRectangle(xs00 - (lineHeight - Gap) / 2, y, xs00 - Gap - 1, y + lineHeight - 1, Timer->Recording() ? Theme.Color(clrMenuTimerRecording) : ColorBg); // The timer recording indicator: - if (Timer->Recording()) + else if (Timer->Recording()) osd->DrawRectangle(xs03 + Gap, y - (MultiRec ? Gap : 0), xs04 - Gap / 2 - 1, y + lineHeight - 1, Theme.Color(clrMenuTimerRecording)); } void cSkinLCARSDisplayMenu::DrawTimers(void) { - if (Timers.Modified(lastTimersState)) { + if (const cTimers *Timers = cTimers::GetTimersRead(timersStateKey)) { deviceRecording.Clear(); const cFont *font = cFont::GetFont(fontOsd); - osd->DrawRectangle(xs00, ys04, xs04 - 1, ys05 - 1, Theme.Color(clrBackground)); + osd->DrawRectangle(xs00 - (lineHeight - Gap) / 2, ys04, xs04 - 1, ys05 - 1, Theme.Color(clrBackground)); osd->DrawRectangle(xs07, ys04, xs13 - 1, ys05 - 1, Theme.Color(clrBackground)); - cSortedTimers SortedTimers; + cSortedTimers SortedTimers(Timers); cVector FreeDeviceSlots; int NumDevices = 0; int y = ys04; @@ -1262,7 +1264,14 @@ void cSkinLCARSDisplayMenu::DrawTimers(void) break; if (const cTimer *Timer = SortedTimers[i]) { if (Timer->Recording()) { - if (cRecordControl *RecordControl = cRecordControls::GetRecordControl(Timer)) { + if (Timer->Remote()) { + if (!Device && Timer->HasFlags(tfActive)) { + DrawTimer(Timer, y, false); + FreeDeviceSlots.Append(y); + y += lineHeight + Gap; + } + } + else if (cRecordControl *RecordControl = cRecordControls::GetRecordControl(Timer)) { if (!Device || Device == RecordControl->Device()) { DrawTimer(Timer, y, NumTimers > 0); NumTimers++; @@ -1313,7 +1322,7 @@ void cSkinLCARSDisplayMenu::DrawTimers(void) } // Total number of active timers: int NumTimers = 0; - for (cTimer *Timer = Timers.First(); Timer; Timer = Timers.Next(Timer)) { + for (const cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) { if (Timer->HasFlags(tfActive)) NumTimers++; } @@ -1321,6 +1330,7 @@ void cSkinLCARSDisplayMenu::DrawTimers(void) osd->DrawText(xs08, ys00, itoa(NumDevices), Theme.Color(clrMenuFrameFg), frameColor, font, xs09 - xs08, ys01 - ys00, taBottom | taRight | taBorder); lastSignalDisplay = 0; initial = true; // forces redrawing of devices + timersStateKey.Remove(); } } @@ -1423,25 +1433,23 @@ void cSkinLCARSDisplayMenu::DrawLive(const cChannel *Channel) DrawSeen(0, 0); } // The current programme: - cSchedulesLock SchedulesLock; - if (const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock)) { - if (const cSchedule *Schedule = Schedules->GetSchedule(Channel)) { - const cEvent *Event = Schedule->GetPresentEvent(); - if (initial || Event != lastEvent) { - DrawInfo(Event, true); - lastEvent = Event; - lastSeen = -1; - } - int Current = 0; - int Total = 0; - if (Event) { - time_t t = time(NULL); - if (t > Event->StartTime()) - Current = t - Event->StartTime(); - Total = Event->Duration(); - } - DrawSeen(Current, Total); + LOCK_SCHEDULES_READ; + if (const cSchedule *Schedule = Schedules->GetSchedule(Channel)) { + const cEvent *Event = Schedule->GetPresentEvent(); + if (initial || Event != lastEvent) { + DrawInfo(Event, true); + lastEvent = Event; + lastSeen = -1; } + int Current = 0; + int Total = 0; + if (Event) { + time_t t = time(NULL); + if (t > Event->StartTime()) + Current = t - Event->StartTime(); + Total = Event->Duration(); + } + DrawSeen(Current, Total); } } @@ -1731,7 +1739,8 @@ void cSkinLCARSDisplayMenu::Flush(void) if (MenuCategory() == mcMain) { cDevice *Device = cDevice::PrimaryDevice(); if (!Device->Replaying() || Device->Transferring()) { - const cChannel *Channel = Channels.GetByNumber(cDevice::PrimaryDevice()->CurrentChannel()); + LOCK_CHANNELS_READ; + const cChannel *Channel = Channels->GetByNumber(cDevice::PrimaryDevice()->CurrentChannel()); DrawLive(Channel); } else if (cControl *Control = cControl::Control(true)) diff --git a/skinlcars.h b/skinlcars.h index abab0189..9c9cdfb9 100644 --- a/skinlcars.h +++ b/skinlcars.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: skinlcars.h 3.0 2012/04/15 13:17:35 kls Exp $ + * $Id: skinlcars.h 4.0 2012/04/15 13:17:35 kls Exp $ */ #ifndef __SKINLCARS_H diff --git a/skins.c b/skins.c index 8d00e8d4..3aee8140 100644 --- a/skins.c +++ b/skins.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: skins.c 3.1 2013/08/18 12:07:22 kls Exp $ + * $Id: skins.c 4.0 2013/08/18 12:07:22 kls Exp $ */ #include "skins.h" diff --git a/skins.h b/skins.h index e1d12c05..2286087b 100644 --- a/skins.h +++ b/skins.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: skins.h 3.4 2015/01/15 10:45:47 kls Exp $ + * $Id: skins.h 4.1 2015/09/10 11:19:48 kls Exp $ */ #ifndef __SKINS_H @@ -129,6 +129,11 @@ enum eMenuSortMode { msmProvider }; +enum eMenuOrientation { + moVertical = 0, + moHorizontal + }; + class cSkinDisplayMenu : public cSkinDisplay { ///< This class implements the general purpose menu display, which is ///< used throughout the program to display information and let the @@ -179,6 +184,10 @@ public: ///< Sets the mode by which the items in this menu are sorted. ///< This is purely informative and may be used by a skin to display the ///< current sort mode by means of some text or symbol. + virtual eMenuOrientation MenuOrientation(void) { return moVertical; } + ///< Asks the skin for the orientation of the displayed menu. + ///< If menu orientation is set to horizontal, the keys left/right + ///< and up/down are just toggled. virtual void Scroll(bool Up, bool Page); ///< If this menu contains a text area that can be scrolled, this function ///< will be called to actually scroll the text. Up indicates whether the diff --git a/skinsttng.c b/skinsttng.c index 7e450fda..f10f1204 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 3.1 2013/11/15 15:33:14 kls Exp $ + * $Id: skinsttng.c 4.0 2013/11/15 15:33:14 kls Exp $ */ // "Star Trek: The Next Generation"(R) is a registered trademark of Paramount Pictures diff --git a/skinsttng.h b/skinsttng.h index f29262d4..7b55db62 100644 --- a/skinsttng.h +++ b/skinsttng.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: skinsttng.h 3.0 2005/01/02 14:39:29 kls Exp $ + * $Id: skinsttng.h 4.0 2005/01/02 14:39:29 kls Exp $ */ #ifndef __SKINSTTNG_H diff --git a/sourceparams.c b/sourceparams.c index 3eec406a..6e92bbef 100644 --- a/sourceparams.c +++ b/sourceparams.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: sourceparams.c 3.1 2014/03/09 12:03:09 kls Exp $ + * $Id: sourceparams.c 4.1 2015/08/02 11:56:39 kls Exp $ */ #include "sourceparams.h" @@ -33,7 +33,7 @@ cSourceParam::cSourceParam(char Source, const char *Description) cSourceParams SourceParams; -cSourceParam *cSourceParams::Get(char Source) const +cSourceParam *cSourceParams::Get(char Source) { for (cSourceParam *sp = First(); sp; sp = Next(sp)) { if (sp->Source() == Source) diff --git a/sourceparams.h b/sourceparams.h index 9f98bd07..1e0a6abc 100644 --- a/sourceparams.h +++ b/sourceparams.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: sourceparams.h 3.0 2010/02/28 11:58:03 kls Exp $ + * $Id: sourceparams.h 4.1 2015/08/02 11:56:25 kls Exp $ */ #ifndef __SOURCEPARAMS_H @@ -45,7 +45,7 @@ public: class cSourceParams : public cList { public: - cSourceParam *Get(char Source) const; + cSourceParam *Get(char Source); }; extern cSourceParams SourceParams; diff --git a/sources.c b/sources.c index f0a34327..aad682f9 100644 --- a/sources.c +++ b/sources.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: sources.c 3.6 2014/03/09 12:05:42 kls Exp $ + * $Id: sources.c 4.0 2014/03/09 12:05:42 kls Exp $ */ #include "sources.h" diff --git a/sources.h b/sources.h index 1cd05b83..e3e7faf0 100644 --- a/sources.h +++ b/sources.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: sources.h 3.3 2014/03/09 11:59:49 kls Exp $ + * $Id: sources.h 4.0 2014/03/09 11:59:49 kls Exp $ */ #ifndef __SOURCES_H diff --git a/spu.c b/spu.c index 8394abf9..199d85ee 100644 --- a/spu.c +++ b/spu.c @@ -6,7 +6,7 @@ * This code is distributed under the terms and conditions of the * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details. * - * $Id: spu.c 3.0 2008/02/10 14:06:48 kls Exp $ + * $Id: spu.c 4.0 2008/02/10 14:06:48 kls Exp $ */ #include "spu.h" diff --git a/spu.h b/spu.h index 8945b441..3c2ba993 100644 --- a/spu.h +++ b/spu.h @@ -6,7 +6,7 @@ * This code is distributed under the terms and conditions of the * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details. * - * $Id: spu.h 3.0 2006/04/17 12:48:55 kls Exp $ + * $Id: spu.h 4.0 2006/04/17 12:48:55 kls Exp $ */ #ifndef __SPU_VDR_H diff --git a/status.c b/status.c index 10b8ce8a..846c10c3 100644 --- a/status.c +++ b/status.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: status.c 3.1 2014/01/25 10:47:39 kls Exp $ + * $Id: status.c 4.0 2014/01/25 10:47:39 kls Exp $ */ #include "status.h" diff --git a/status.h b/status.h index 222280ac..6d1b9df5 100644 --- a/status.h +++ b/status.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: status.h 3.1 2014/01/25 10:47:39 kls Exp $ + * $Id: status.h 4.1 2015/08/02 10:34:44 kls Exp $ */ #ifndef __STATUS_H @@ -15,7 +15,7 @@ #include "player.h" #include "tools.h" -enum eTimerChange { tcMod, tcAdd, tcDel }; +enum eTimerChange { tcMod, tcAdd, tcDel }; // tcMod is obsolete and no longer used! class cTimer; @@ -29,10 +29,7 @@ protected: // require a retune. virtual void TimerChange(const cTimer *Timer, eTimerChange Change) {} // Indicates a change in the timer settings. - // If Change is tcAdd or tcDel, Timer points to the timer that has - // been added or will be deleted, respectively. In case of tcMod, - // Timer is NULL; this indicates that some timer has been changed. - // Note that tcAdd and tcDel are always also followed by a tcMod. + // Timer points to the timer that has been added or will be deleted, respectively. virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber, bool LiveView) {} // Indicates a channel switch on the given DVB device. // If ChannelNumber is 0, this is before the channel is being switched, diff --git a/summary2info b/summary2info index 6baf4e7d..511079b0 100755 --- a/summary2info +++ b/summary2info @@ -10,7 +10,7 @@ # See the main source file 'vdr.c' for copyright information and # how to reach the author. # -# $Id: summary2info 3.0 2011/12/04 14:17:35 kls Exp $ +# $Id: summary2info 4.0 2011/12/04 14:17:35 kls Exp $ $VideoDir = $ARGV[0] || die "please provide the name of the video directory\n"; diff --git a/svdrp.c b/svdrp.c index 2b5edb2c..1a24d296 100644 --- a/svdrp.c +++ b/svdrp.c @@ -10,7 +10,7 @@ * and interact with the Video Disk Recorder - or write a full featured * graphical interface that sits on top of an SVDRP connection. * - * $Id: svdrp.c 3.6 2015/01/12 11:16:27 kls Exp $ + * $Id: svdrp.c 4.9 2015/09/14 13:23:06 kls Exp $ */ #include "svdrp.h" @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -33,19 +34,90 @@ #include "keys.h" #include "menu.h" #include "plugin.h" +#include "recording.h" #include "remote.h" #include "skins.h" +#include "thread.h" #include "timers.h" -#include "tools.h" #include "videodir.h" +static bool DumpSVDRPDataTransfer = false; + +#define dbgsvdrp(a...) if (DumpSVDRPDataTransfer) fprintf(stderr, a) + +static int SVDRPTcpPort = 0; +static int SVDRPUdpPort = 0; + +// --- cIpAddress ------------------------------------------------------------ + +class cIpAddress { +private: + cString address; + int port; + cString connection; +public: + cIpAddress(void); + cIpAddress(const char *Address, int Port); + const char *Address(void) const { return address; } + int Port(void) const { return port; } + void Set(const char *Address, int Port); + void Set(const sockaddr *SockAddr); + const char *Connection(void) const { return connection; } + }; + +cIpAddress::cIpAddress(void) +{ + Set(INADDR_ANY, 0); +} + +cIpAddress::cIpAddress(const char *Address, int Port) +{ + Set(Address, Port); +} + +void cIpAddress::Set(const char *Address, int Port) +{ + address = Address; + port = Port; + connection = cString::sprintf("%s:%d", *address, port); +} + +void cIpAddress::Set(const sockaddr *SockAddr) +{ + const sockaddr_in *Addr = (sockaddr_in *)SockAddr; + Set(inet_ntoa(Addr->sin_addr), ntohs(Addr->sin_port)); +} + // --- cSocket --------------------------------------------------------------- -cSocket::cSocket(int Port, int Queue) +#define MAXUDPBUF 1024 + +class cSocket { +private: + int port; + bool tcp; + int sock; + cIpAddress lastIpAddress; + bool IsOwnInterface(sockaddr_in *Addr); +public: + cSocket(int Port, bool Tcp); + ~cSocket(); + bool Listen(void); + bool Connect(const char *Address); + void Close(void); + int Port(void) const { return port; } + int Socket(void) const { return sock; } + static bool SendDgram(const char *Dgram, int Port, const char *Address = NULL); + int Accept(void); + cString Discover(void); + const cIpAddress *LastIpAddress(void) const { return &lastIpAddress; } + }; + +cSocket::cSocket(int Port, bool Tcp) { port = Port; + tcp = Tcp; sock = -1; - queue = Queue; } cSocket::~cSocket() @@ -53,6 +125,30 @@ cSocket::~cSocket() Close(); } +bool cSocket::IsOwnInterface(sockaddr_in *Addr) +{ + ifaddrs *ifaddr; + if (getifaddrs(&ifaddr) >= 0) { + bool Own = false; + for (ifaddrs *ifa = ifaddr; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr) { + if (ifa->ifa_addr->sa_family == AF_INET) { + sockaddr_in *addr = (sockaddr_in *)ifa->ifa_addr; + if (addr->sin_addr.s_addr == Addr->sin_addr.s_addr) { + Own = true; + break; + } + } + } + } + freeifaddrs(ifaddr); + return Own; + } + else + LOG_ERROR; + return false; +} + void cSocket::Close(void) { if (sock >= 0) { @@ -61,75 +157,508 @@ void cSocket::Close(void) } } -bool cSocket::Open(void) +bool cSocket::Listen(void) { if (sock < 0) { // create socket: - sock = socket(PF_INET, SOCK_STREAM, 0); + sock = tcp ? socket(PF_INET, SOCK_STREAM, IPPROTO_IP) : socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) { LOG_ERROR; - port = 0; return false; } // allow it to always reuse the same port: int ReUseAddr = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &ReUseAddr, sizeof(ReUseAddr)); - // - struct sockaddr_in name; - name.sin_family = AF_INET; - name.sin_port = htons(port); - name.sin_addr.s_addr = SVDRPhosts.LocalhostOnly() ? htonl(INADDR_LOOPBACK) : htonl(INADDR_ANY); - if (bind(sock, (struct sockaddr *)&name, sizeof(name)) < 0) { + // configure port and ip: + sockaddr_in Addr; + memset(&Addr, 0, sizeof(Addr)); + Addr.sin_family = AF_INET; + Addr.sin_port = htons(port); + Addr.sin_addr.s_addr = SVDRPhosts.LocalhostOnly() ? htonl(INADDR_LOOPBACK) : htonl(INADDR_ANY); + if (bind(sock, (sockaddr *)&Addr, sizeof(Addr)) < 0) { LOG_ERROR; Close(); return false; } // make it non-blocking: - int oldflags = fcntl(sock, F_GETFL, 0); - if (oldflags < 0) { + int Flags = fcntl(sock, F_GETFL, 0); + if (Flags < 0) { LOG_ERROR; return false; } - oldflags |= O_NONBLOCK; - if (fcntl(sock, F_SETFL, oldflags) < 0) { + Flags |= O_NONBLOCK; + if (fcntl(sock, F_SETFL, Flags) < 0) { LOG_ERROR; return false; } - // listen to the socket: - if (listen(sock, queue) < 0) { - LOG_ERROR; - return false; + if (tcp) { + // listen to the socket: + if (listen(sock, 1) < 0) { + LOG_ERROR; + return false; + } } + isyslog("SVDRP listening on port %d/%s", port, tcp ? "tcp" : "udp"); } return true; } +bool cSocket::Connect(const char *Address) +{ + if (sock < 0 && tcp) { + // create socket: + sock = socket(PF_INET, SOCK_STREAM, IPPROTO_IP); + if (sock < 0) { + LOG_ERROR; + return false; + } + // configure port and ip: + sockaddr_in Addr; + memset(&Addr, 0, sizeof(Addr)); + Addr.sin_family = AF_INET; + Addr.sin_port = htons(port); + Addr.sin_addr.s_addr = inet_addr(Address); + if (connect(sock, (sockaddr *)&Addr, sizeof(Addr)) < 0) { + LOG_ERROR; + Close(); + return false; + } + // make it non-blocking: + int Flags = fcntl(sock, F_GETFL, 0); + if (Flags < 0) { + LOG_ERROR; + return false; + } + Flags |= O_NONBLOCK; + if (fcntl(sock, F_SETFL, Flags) < 0) { + LOG_ERROR; + return false; + } + isyslog("SVDRP > %s:%d server connection established", Address, port); + return true; + } + return false; +} + +bool cSocket::SendDgram(const char *Dgram, int Port, const char *Address) +{ + // Create a socket: + int Socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (Socket < 0) { + LOG_ERROR; + return false; + } + if (!Address) { + // Enable broadcast: + int One = 1; + if (setsockopt(Socket, SOL_SOCKET, SO_BROADCAST, &One, sizeof(One)) < 0) { + LOG_ERROR; + close(Socket); + return false; + } + } + // Configure port and ip: + sockaddr_in Addr; + memset(&Addr, 0, sizeof(Addr)); + Addr.sin_family = AF_INET; + Addr.sin_addr.s_addr = Address ? inet_addr(Address) : htonl(INADDR_BROADCAST); + Addr.sin_port = htons(Port); + // Send datagram: + dsyslog("SVDRP > %s:%d send dgram '%s'", inet_ntoa(Addr.sin_addr), Port, Dgram); + int Length = strlen(Dgram); + int Sent = sendto(Socket, Dgram, Length, 0, (sockaddr *)&Addr, sizeof(Addr)); + if (Sent < 0) + LOG_ERROR; + close(Socket); + return Sent == Length; +} + int cSocket::Accept(void) { - if (Open()) { - struct sockaddr_in clientname; - uint size = sizeof(clientname); - int newsock = accept(sock, (struct sockaddr *)&clientname, &size); - if (newsock > 0) { - bool accepted = SVDRPhosts.Acceptable(clientname.sin_addr.s_addr); - if (!accepted) { + if (sock >= 0 && tcp) { + sockaddr_in Addr; + uint Size = sizeof(Addr); + int NewSock = accept(sock, (sockaddr *)&Addr, &Size); + if (NewSock >= 0) { + bool Accepted = SVDRPhosts.Acceptable(Addr.sin_addr.s_addr); + if (!Accepted) { const char *s = "Access denied!\n"; - if (write(newsock, s, strlen(s)) < 0) + if (write(NewSock, s, strlen(s)) < 0) LOG_ERROR; - close(newsock); - newsock = -1; + close(NewSock); + NewSock = -1; } - isyslog("connect from %s, port %hu - %s", inet_ntoa(clientname.sin_addr), ntohs(clientname.sin_port), accepted ? "accepted" : "DENIED"); + lastIpAddress.Set((sockaddr *)&Addr); + isyslog("SVDRP < %s client connection %s", lastIpAddress.Connection(), Accepted ? "accepted" : "DENIED"); } - else if (errno != EINTR && errno != EAGAIN) + else if (FATALERRNO) LOG_ERROR; - return newsock; + return NewSock; } return -1; } +cString cSocket::Discover(void) +{ + if (sock >= 0 && !tcp) { + char buf[MAXUDPBUF]; + sockaddr_in Addr; + uint Size = sizeof(Addr); + int NumBytes = recvfrom(sock, buf, sizeof(buf), 0, (sockaddr *)&Addr, &Size); + if (NumBytes >= 0) { + buf[NumBytes] = 0; + if (!IsOwnInterface(&Addr)) { + lastIpAddress.Set((sockaddr *)&Addr); + if (!SVDRPhosts.Acceptable(Addr.sin_addr.s_addr)) { + dsyslog("SVDRP < %s discovery ignored (%s)", lastIpAddress.Connection(), buf); + return NULL; + } + if (!startswith(buf, "SVDRP:discover")) { + dsyslog("SVDRP < %s discovery unrecognized (%s)", lastIpAddress.Connection(), buf); + return NULL; + } + isyslog("SVDRP < %s discovery received (%s)", lastIpAddress.Connection(), buf); + return buf; + } + } + else if (FATALERRNO) + LOG_ERROR; + } + return NULL; +} + +// --- cSVDRPClient ---------------------------------------------------------- + +class cSVDRPClient { +private: + cIpAddress ipAddress; + cSocket socket; + cString serverName; + int timeout; + cTimeMs pingTime; + cFile file; + int fetchFlags; + void Close(void); +public: + cSVDRPClient(const char *Address, int Port, const char *ServerName, int Timeout); + ~cSVDRPClient(); + const char *ServerName(void) const { return serverName; } + const char *Connection(void) const { return ipAddress.Connection(); } + bool HasAddress(const char *Address, int Port) const; + bool Send(const char *Command); + bool Process(cStringList *Response = NULL); + bool Execute(const char *Command, cStringList *Response = NULL); + void SetFetchFlag(eSvdrpFetchFlags Flag); + bool HasFetchFlag(eSvdrpFetchFlags Flag); + }; + +static cPoller SVDRPClientPoller; + +cSVDRPClient::cSVDRPClient(const char *Address, int Port, const char *ServerName, int Timeout) +:ipAddress(Address, Port) +,socket(Port, true) +{ + serverName = ServerName; + timeout = Timeout * 1000 * 9 / 10; // ping after 90% of timeout + pingTime.Set(timeout); + fetchFlags = sffTimers; + if (socket.Connect(Address)) { + if (file.Open(socket.Socket())) { + SVDRPClientPoller.Add(file, false); + dsyslog("SVDRP > %s client created for '%s'", ipAddress.Connection(), *serverName); + return; + } + } + esyslog("SVDRP > %s ERROR: failed to create client for '%s'", ipAddress.Connection(), *serverName); +} + +cSVDRPClient::~cSVDRPClient() +{ + Close(); + dsyslog("SVDRP > %s client destroyed for '%s'", ipAddress.Connection(), *serverName); +} + +void cSVDRPClient::Close(void) +{ + if (file.IsOpen()) { + SVDRPClientPoller.Del(file, false); + file.Close(); + socket.Close(); + } +} + +bool cSVDRPClient::HasAddress(const char *Address, int Port) const +{ + return strcmp(ipAddress.Address(), Address) == 0 && ipAddress.Port() == Port; +} + +bool cSVDRPClient::Send(const char *Command) +{ + pingTime.Set(timeout); + dbgsvdrp("> %s: %s\n", *serverName, Command); + if (safe_write(file, Command, strlen(Command) + 1) < 0) { + LOG_ERROR; + return false; + } + return true; +} + +bool cSVDRPClient::Process(cStringList *Response) +{ + if (file.IsOpen()) { + char input[BUFSIZ]; + int numChars = 0; +#define SVDRPResonseTimeout 5000 // ms + cTimeMs Timeout(SVDRPResonseTimeout); + for (;;) { + if (file.Ready(false)) { + unsigned char c; + int r = safe_read(file, &c, 1); + if (r > 0) { + if (c == '\n' || c == 0x00) { + // strip trailing whitespace: + while (numChars > 0 && strchr(" \t\r\n", input[numChars - 1])) + input[--numChars] = 0; + // make sure the string is terminated: + input[numChars] = 0; + dbgsvdrp("< %s: %s\n", *serverName, input); + if (Response) { + Response->Append(strdup(input)); + if (numChars >= 4 && input[3] != '-') // no more lines will follow + break; + } + else { + switch (atoi(input)) { + case 220: if (numChars > 4) { + char *n = input + 4; + if (char *t = strchr(n, ' ')) { + *t = 0; + if (strcmp(n, serverName) != 0) { + serverName = n; + dsyslog("SVDRP < %s remote server name is '%s'", ipAddress.Connection(), *serverName); + } + } + } + break; + case 221: dsyslog("SVDRP < %s remote server closed connection to '%s'", ipAddress.Connection(), *serverName); + Close(); + break; + } + } + numChars = 0; + } + else { + if (numChars >= int(sizeof(input))) { + esyslog("SVDRP < %s ERROR: out of memory", ipAddress.Connection()); + Close(); + break; + } + input[numChars++] = c; + input[numChars] = 0; + } + Timeout.Set(SVDRPResonseTimeout); + } + else if (r <= 0) { + isyslog("SVDRP < %s lost connection to remote server '%s'", ipAddress.Connection(), *serverName); + Close(); + } + } + else if (!Response) + break; + else if (Timeout.TimedOut()) { + esyslog("SVDRP < %s timeout while waiting for response from '%s'", ipAddress.Connection(), *serverName); + return false; + } + } + if (pingTime.TimedOut()) + Execute("PING"); + } + return file.IsOpen(); +} + +bool cSVDRPClient::Execute(const char *Command, cStringList *Response) +{ + if (Response) + Response->Clear(); + return Send(Command) && Process(Response); +} + +void cSVDRPClient::SetFetchFlag(eSvdrpFetchFlags Flags) +{ + fetchFlags |= Flags; +} + +bool cSVDRPClient::HasFetchFlag(eSvdrpFetchFlags Flag) +{ + bool Result = (fetchFlags & Flag); + fetchFlags &= ~Flag; + return Result; +} + +// --- cSVDRPClientHandler --------------------------------------------------- + +class cSVDRPClientHandler : public cThread { +private: + cMutex mutex; + int tcpPort; + cSocket udpSocket; + cVector clientConnections; + void HandleClientConnection(void); + void ProcessConnections(void); + cSVDRPClient *GetClientForServer(const char *ServerName); +protected: + virtual void Action(void); +public: + cSVDRPClientHandler(int TcpPort, int UdpPort); + virtual ~cSVDRPClientHandler(); + void SendDiscover(const char *Address = NULL); + bool Execute(const char *ServerName, const char *Command, cStringList *Response = NULL); + bool GetServerNames(cStringList *ServerNames, eSvdrpFetchFlags FetchFlags = sffNone); + bool TriggerFetchingTimers(const char *ServerName); + }; + +static cSVDRPClientHandler *SVDRPClientHandler = NULL; + +cSVDRPClientHandler::cSVDRPClientHandler(int TcpPort, int UdpPort) +:cThread("SVDRP client handler", true) +,udpSocket(UdpPort, false) +{ + tcpPort = TcpPort; +} + +cSVDRPClientHandler::~cSVDRPClientHandler() +{ + Cancel(3); + for (int i = 0; i < clientConnections.Size(); i++) + delete clientConnections[i]; +} + +cSVDRPClient *cSVDRPClientHandler::GetClientForServer(const char *ServerName) +{ + for (int i = 0; i < clientConnections.Size(); i++) { + if (strcmp(clientConnections[i]->ServerName(), ServerName) == 0) + return clientConnections[i]; + } + return NULL; +} + +void cSVDRPClientHandler::SendDiscover(const char *Address) +{ + cMutexLock MutexLock(&mutex); + cString Dgram = cString::sprintf("SVDRP:discover name:%s port:%d vdrversion:%d apiversion:%d timeout:%d", Setup.SVDRPHostName, tcpPort, VDRVERSNUM, APIVERSNUM, Setup.SVDRPTimeout); + udpSocket.SendDgram(Dgram, udpSocket.Port(), Address); +} + +void cSVDRPClientHandler::ProcessConnections(void) +{ + cMutexLock MutexLock(&mutex); + for (int i = 0; i < clientConnections.Size(); i++) { + if (!clientConnections[i]->Process()) { + delete clientConnections[i]; + clientConnections.Remove(i); + i--; + } + } +} + +void cSVDRPClientHandler::HandleClientConnection(void) +{ + cString NewDiscover = udpSocket.Discover(); + if (*NewDiscover) { + cString p = strgetval(NewDiscover, "port", ':'); + if (*p) { + int Port = atoi(p); + for (int i = 0; i < clientConnections.Size(); i++) { + if (clientConnections[i]->HasAddress(udpSocket.LastIpAddress()->Address(), Port)) { + dsyslog("SVDRP < %s connection to '%s' confirmed", clientConnections[i]->Connection(), clientConnections[i]->ServerName()); + return; + } + } + cString ServerName = strgetval(NewDiscover, "name", ':'); + if (*ServerName) { + cString t = strgetval(NewDiscover, "timeout", ':'); + if (*t) { + int Timeout = atoi(t); + if (Timeout > 10) { // don't let it get too small + const char *Address = udpSocket.LastIpAddress()->Address(); + clientConnections.Append(new cSVDRPClient(Address, Port, ServerName, Timeout)); + SendDiscover(Address); + } + else + esyslog("SVDRP < %s ERROR: invalid timeout (%d)", udpSocket.LastIpAddress()->Connection(), Timeout); + } + else + esyslog("SVDRP < %s ERROR: missing timeout", udpSocket.LastIpAddress()->Connection()); + } + else + esyslog("SVDRP < %s ERROR: missing server name", udpSocket.LastIpAddress()->Connection()); + } + else + esyslog("SVDRP < %s ERROR: missing port number", udpSocket.LastIpAddress()->Connection()); + } +} + +void cSVDRPClientHandler::Action(void) +{ + if (udpSocket.Listen()) { + SVDRPClientPoller.Add(udpSocket.Socket(), false); + SendDiscover(); + while (Running()) { + SVDRPClientPoller.Poll(1000); + cMutexLock MutexLock(&mutex); + HandleClientConnection(); + ProcessConnections(); + } + SVDRPClientPoller.Del(udpSocket.Socket(), false); + udpSocket.Close(); + } +} + +bool cSVDRPClientHandler::Execute(const char *ServerName, const char *Command, cStringList *Response) +{ + cMutexLock MutexLock(&mutex); + if (cSVDRPClient *Client = GetClientForServer(ServerName)) + return Client->Execute(Command, Response); + return false; +} + +bool cSVDRPClientHandler::GetServerNames(cStringList *ServerNames, eSvdrpFetchFlags FetchFlag) +{ + cMutexLock MutexLock(&mutex); + ServerNames->Clear(); + for (int i = 0; i < clientConnections.Size(); i++) { + cSVDRPClient *Client = clientConnections[i]; + if (FetchFlag == sffNone || Client->HasFetchFlag(FetchFlag)) + ServerNames->Append(strdup(Client->ServerName())); + } + return ServerNames->Size() > 0; +} + +bool cSVDRPClientHandler::TriggerFetchingTimers(const char *ServerName) +{ + cMutexLock MutexLock(&mutex); + if (cSVDRPClient *Client = GetClientForServer(ServerName)) { + Client->SetFetchFlag(sffTimers); + return true; + } + return false; +} + // --- cPUTEhandler ---------------------------------------------------------- +class cPUTEhandler { +private: + FILE *f; + int status; + const char *message; +public: + cPUTEhandler(void); + ~cPUTEhandler(); + bool Process(const char *s); + int Status(void) { return status; } + const char *Message(void) { return message; } + }; + cPUTEhandler::cPUTEhandler(void) { if ((f = tmpfile()) != NULL) { @@ -175,7 +704,7 @@ bool cPUTEhandler::Process(const char *s) return false; } -// --- cSVDRP ---------------------------------------------------------------- +// --- cSVDRPServer ---------------------------------------------------------- #define MAXHELPTOPIC 10 #define EITDISABLETIME 10 // seconds until EIT processing is enabled again after a CLRE command @@ -244,7 +773,8 @@ const char *HelpPages[] = { " List timers. Without option, all timers are listed. Otherwise\n" " only the given timer is listed. If the keyword 'id' is given, the\n" " channels will be listed with their unique channel ids instead of\n" - " their numbers.", + " their numbers. This command lists only the timers that are defined\n" + " locally on this VDR, not any remote timers from other VDRs.", "MESG \n" " Displays the given message on the OSD. The message will be queued\n" " and displayed whenever this is suitable.\n", @@ -278,6 +808,10 @@ const char *HelpPages[] = { " zero, this means that the timer is currently recording and has started\n" " at the given time. The first value in the resulting line is the number\n" " of the timer.", + "PING\n" + " Used by peer-to-peer connections between VDRs to keep the connection\n" + " from timing out. May be used at any time and simply returns a line of\n" + " the form ' is alive'.", "PLAY [ begin | ]\n" " Play the recording with the given number. Before a recording can be\n" " played, an LSTR command must have been executed in order to retrieve\n" @@ -298,6 +832,10 @@ const char *HelpPages[] = { " If 'help' is followed by a command, the detailed help for that command is\n" " given. The keyword 'main' initiates a call to the main menu function of the\n" " given plugin.\n", + "POLL timers\n" + " Used by peer-to-peer connections between VDRs to inform other machines\n" + " about changes to timers. The receiving VDR shall use LSTT to query the\n" + " remote machine's timers and update its list of timers accordingly.\n", "PUTE [ file ]\n" " Put data into the EPG list. The data entered has to strictly follow the\n" " format defined in vdr(5) for the 'epg.data' file. A '.' on a line\n" @@ -317,7 +855,7 @@ const char *HelpPages[] = { "UPDT \n" " Updates a timer. Settings must be in the same format as returned\n" " by the LSTT command. If a timer with the same channel, day, start\n" - " and stop time does not yet exists, it will be created.", + " and stop time does not yet exist, it will be created.", "UPDR\n" " Initiates a re-read of the recordings directory, which is the SVDRP\n" " equivalent to 'touch .update'.", @@ -385,41 +923,103 @@ const char *GetHelpPage(const char *Cmd, const char **p) return NULL; } -char *cSVDRP::grabImageDir = NULL; +static cString grabImageDir; -cSVDRP::cSVDRP(int Port) -:socket(Port) +class cSVDRPServer { +private: + int socket; + cString connection; + cFile file; + cPUTEhandler *PUTEhandler; + int numChars; + int length; + char *cmdLine; + time_t lastActivity; + void Close(bool SendReply = false, bool Timeout = false); + bool Send(const char *s, int length = -1); + void Reply(int Code, const char *fmt, ...) __attribute__ ((format (printf, 3, 4))); + void PrintHelpTopics(const char **hp); + void CmdCHAN(const char *Option); + void CmdCLRE(const char *Option); + void CmdDELC(const char *Option); + void CmdDELR(const char *Option); + void CmdDELT(const char *Option); + void CmdEDIT(const char *Option); + void CmdGRAB(const char *Option); + void CmdHELP(const char *Option); + void CmdHITK(const char *Option); + void CmdLSTC(const char *Option); + void CmdLSTE(const char *Option); + void CmdLSTR(const char *Option); + void CmdLSTT(const char *Option); + void CmdMESG(const char *Option); + void CmdMODC(const char *Option); + void CmdMODT(const char *Option); + void CmdMOVC(const char *Option); + void CmdMOVR(const char *Option); + void CmdNEWC(const char *Option); + void CmdNEWT(const char *Option); + void CmdNEXT(const char *Option); + void CmdPING(const char *Option); + void CmdPLAY(const char *Option); + void CmdPLUG(const char *Option); + void CmdPOLL(const char *Option); + void CmdPUTE(const char *Option); + void CmdREMO(const char *Option); + void CmdSCAN(const char *Option); + void CmdSTAT(const char *Option); + void CmdUPDT(const char *Option); + void CmdUPDR(const char *Option); + void CmdVOLU(const char *Option); + void Execute(char *Cmd); +public: + cSVDRPServer(int Socket, const char *Connection); + ~cSVDRPServer(); + bool HasConnection(void) { return file.IsOpen(); } + bool Process(void); + }; + +static cPoller SVDRPServerPoller; + +cSVDRPServer::cSVDRPServer(int Socket, const char *Connection) { + socket = Socket; + connection = Connection; PUTEhandler = NULL; numChars = 0; length = BUFSIZ; cmdLine = MALLOC(char, length); - lastActivity = 0; - isyslog("SVDRP listening on port %d", Port); + lastActivity = time(NULL); + if (file.Open(socket)) { + time_t now = time(NULL); + Reply(220, "%s SVDRP VideoDiskRecorder %s; %s; %s", Setup.SVDRPHostName, VDRVERSION, *TimeToString(now), cCharSetConv::SystemCharacterTable() ? cCharSetConv::SystemCharacterTable() : "UTF-8"); + SVDRPServerPoller.Add(file, false); + } + dsyslog("SVDRP < %s server created", *connection); } -cSVDRP::~cSVDRP() +cSVDRPServer::~cSVDRPServer() { Close(true); free(cmdLine); + dsyslog("SVDRP < %s server destroyed", *connection); } -void cSVDRP::Close(bool SendReply, bool Timeout) +void cSVDRPServer::Close(bool SendReply, bool Timeout) { if (file.IsOpen()) { if (SendReply) { - //TODO how can we get the *full* hostname? - char buffer[BUFSIZ]; - gethostname(buffer, sizeof(buffer)); - Reply(221, "%s closing connection%s", buffer, Timeout ? " (timeout)" : ""); + Reply(221, "%s closing connection%s", Setup.SVDRPHostName, Timeout ? " (timeout)" : ""); } - isyslog("closing SVDRP connection"); //TODO store IP#??? + isyslog("SVDRP < %s connection closed", *connection); + SVDRPServerPoller.Del(file, false); file.Close(); DELETENULL(PUTEhandler); } + close(socket); } -bool cSVDRP::Send(const char *s, int length) +bool cSVDRPServer::Send(const char *s, int length) { if (length < 0) length = strlen(s); @@ -431,7 +1031,7 @@ bool cSVDRP::Send(const char *s, int length) return true; } -void cSVDRP::Reply(int Code, const char *fmt, ...) +void cSVDRPServer::Reply(int Code, const char *fmt, ...) { if (file.IsOpen()) { if (Code != 0) { @@ -454,12 +1054,12 @@ void cSVDRP::Reply(int Code, const char *fmt, ...) } else { Reply(451, "Zero return code - looks like a programming error!"); - esyslog("SVDRP: zero return code!"); + esyslog("SVDRP < %s zero return code!", *connection); } } } -void cSVDRP::PrintHelpTopics(const char **hp) +void cSVDRPServer::PrintHelpTopics(const char **hp) { int NumPages = 0; if (hp) { @@ -485,14 +1085,15 @@ void cSVDRP::PrintHelpTopics(const char **hp) } } -void cSVDRP::CmdCHAN(const char *Option) +void cSVDRPServer::CmdCHAN(const char *Option) { + LOCK_CHANNELS_READ; if (*Option) { int n = -1; int d = 0; if (isnumber(Option)) { int o = strtol(Option, NULL, 10); - if (o >= 1 && o <= Channels.MaxNumber()) + if (o >= 1 && o <= cChannels::MaxNumber()) n = o; } else if (strcmp(Option, "-") == 0) { @@ -504,35 +1105,31 @@ void cSVDRP::CmdCHAN(const char *Option) } else if (strcmp(Option, "+") == 0) { n = cDevice::CurrentChannel(); - if (n < Channels.MaxNumber()) { + if (n < cChannels::MaxNumber()) { n++; d = 1; } } + else if (const cChannel *Channel = Channels->GetByChannelID(tChannelID::FromString(Option))) + n = Channel->Number(); else { - cChannel *channel = Channels.GetByChannelID(tChannelID::FromString(Option)); - if (channel) - n = channel->Number(); - else { - for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) { - if (!channel->GroupSep()) { - if (strcasecmp(channel->Name(), Option) == 0) { - n = channel->Number(); - break; - } + for (const cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) { + if (!Channel->GroupSep()) { + if (strcasecmp(Channel->Name(), Option) == 0) { + n = Channel->Number(); + break; } } - } + } } if (n < 0) { Reply(501, "Undefined channel \"%s\"", Option); return; } if (!d) { - cChannel *channel = Channels.GetByNumber(n); - if (channel) { - if (!cDevice::PrimaryDevice()->SwitchChannel(channel, true)) { - Reply(554, "Error switching to channel \"%d\"", channel->Number()); + if (const cChannel *Channel = Channels->GetByNumber(n)) { + if (!cDevice::PrimaryDevice()->SwitchChannel(Channel, true)) { + Reply(554, "Error switching to channel \"%d\"", Channel->Number()); return; } } @@ -544,26 +1141,27 @@ void cSVDRP::CmdCHAN(const char *Option) else cDevice::SwitchChannel(d); } - cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel()); - if (channel) - Reply(250, "%d %s", channel->Number(), channel->Name()); + if (const cChannel *Channel = Channels->GetByNumber(cDevice::CurrentChannel())) + Reply(250, "%d %s", Channel->Number(), Channel->Name()); else Reply(550, "Unable to find channel \"%d\"", cDevice::CurrentChannel()); } -void cSVDRP::CmdCLRE(const char *Option) +void cSVDRPServer::CmdCLRE(const char *Option) { if (*Option) { + LOCK_TIMERS_WRITE; + LOCK_CHANNELS_READ; tChannelID ChannelID = tChannelID::InvalidID; if (isnumber(Option)) { int o = strtol(Option, NULL, 10); - if (o >= 1 && o <= Channels.MaxNumber()) - ChannelID = Channels.GetByNumber(o)->GetChannelID(); + if (o >= 1 && o <= cChannels::MaxNumber()) + ChannelID = Channels->GetByNumber(o)->GetChannelID(); } else { ChannelID = tChannelID::FromString(Option); if (ChannelID == tChannelID::InvalidID) { - for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { + for (const cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) { if (!Channel->GroupSep()) { if (strcasecmp(Channel->Name(), Option) == 0) { ChannelID = Channel->GetChannelID(); @@ -574,87 +1172,80 @@ void cSVDRP::CmdCLRE(const char *Option) } } if (!(ChannelID == tChannelID::InvalidID)) { - cSchedulesLock SchedulesLock(true, 1000); - cSchedules *s = (cSchedules *)cSchedules::Schedules(SchedulesLock); - if (s) { - cSchedule *Schedule = NULL; - ChannelID.ClrRid(); - for (cSchedule *p = s->First(); p; p = s->Next(p)) { - if (p->ChannelID() == ChannelID) { - Schedule = p; - break; - } + LOCK_SCHEDULES_WRITE; + cSchedule *Schedule = NULL; + ChannelID.ClrRid(); + for (cSchedule *p = Schedules->First(); p; p = Schedules->Next(p)) { + if (p->ChannelID() == ChannelID) { + Schedule = p; + break; } - if (Schedule) { - for (cTimer *Timer = Timers.First(); Timer; Timer = Timers.Next(Timer)) { - if (ChannelID == Timer->Channel()->GetChannelID().ClrRid()) - Timer->SetEvent(NULL); - } - Schedule->Cleanup(INT_MAX); - cEitFilter::SetDisableUntil(time(NULL) + EITDISABLETIME); - Reply(250, "EPG data of channel \"%s\" cleared", Option); - } - else { - Reply(550, "No EPG data found for channel \"%s\"", Option); - return; - } + } + if (Schedule) { + for (cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) { + if (ChannelID == Timer->Channel()->GetChannelID().ClrRid()) + Timer->SetEvent(NULL); + } + Schedule->Cleanup(INT_MAX); + cEitFilter::SetDisableUntil(time(NULL) + EITDISABLETIME); + Reply(250, "EPG data of channel \"%s\" cleared", Option); + } + else { + Reply(550, "No EPG data found for channel \"%s\"", Option); + return; } - else - Reply(451, "Can't get EPG data"); } else Reply(501, "Undefined channel \"%s\"", Option); } else { + LOCK_TIMERS_WRITE; + LOCK_SCHEDULES_WRITE; + for (cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) + Timer->SetEvent(NULL); // processing all timers here (local *and* remote) + for (cSchedule *Schedule = Schedules->First(); Schedule; Schedule = Schedules->Next(Schedule)) + Schedule->Cleanup(INT_MAX); cEitFilter::SetDisableUntil(time(NULL) + EITDISABLETIME); - if (cSchedules::ClearAll()) { - Reply(250, "EPG data cleared"); - cEitFilter::SetDisableUntil(time(NULL) + EITDISABLETIME); - } - else - Reply(451, "Error while clearing EPG data"); + Reply(250, "EPG data cleared"); } } -void cSVDRP::CmdDELC(const char *Option) +void cSVDRPServer::CmdDELC(const char *Option) { if (*Option) { if (isnumber(Option)) { - if (!Channels.BeingEdited()) { - cChannel *channel = Channels.GetByNumber(strtol(Option, NULL, 10)); - if (channel) { - for (cTimer *timer = Timers.First(); timer; timer = Timers.Next(timer)) { - if (timer->Channel() == channel) { - Reply(550, "Channel \"%s\" is in use by timer %d", Option, timer->Index() + 1); - return; - } - } - int CurrentChannelNr = cDevice::CurrentChannel(); - cChannel *CurrentChannel = Channels.GetByNumber(CurrentChannelNr); - if (CurrentChannel && channel == CurrentChannel) { - int n = Channels.GetNextNormal(CurrentChannel->Index()); - if (n < 0) - n = Channels.GetPrevNormal(CurrentChannel->Index()); - CurrentChannel = Channels.Get(n); - CurrentChannelNr = 0; // triggers channel switch below - } - Channels.Del(channel); - Channels.ReNumber(); - Channels.SetModified(true); - isyslog("channel %s deleted", Option); - if (CurrentChannel && CurrentChannel->Number() != CurrentChannelNr) { - if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring()) - Channels.SwitchTo(CurrentChannel->Number()); - else - cDevice::SetCurrentChannel(CurrentChannel); - } - Reply(250, "Channel \"%s\" deleted", Option); + LOCK_TIMERS_READ; + LOCK_CHANNELS_WRITE; + Channels->SetExplicitModify(); + if (cChannel *Channel = Channels->GetByNumber(strtol(Option, NULL, 10))) { + if (const cTimer *Timer = Timers->UsesChannel(Channel)) { + Reply(550, "Channel \"%s\" is in use by timer %s", Option, *Timer->ToDescr()); + return; } - else - Reply(501, "Channel \"%s\" not defined", Option); + int CurrentChannelNr = cDevice::CurrentChannel(); + cChannel *CurrentChannel = Channels->GetByNumber(CurrentChannelNr); + if (CurrentChannel && Channel == CurrentChannel) { + int n = Channels->GetNextNormal(CurrentChannel->Index()); + if (n < 0) + n = Channels->GetPrevNormal(CurrentChannel->Index()); + CurrentChannel = Channels->Get(n); + CurrentChannelNr = 0; // triggers channel switch below + } + Channels->Del(Channel); + Channels->ReNumber(); + Channels->SetModifiedByUser(); + Channels->SetModified(); + isyslog("SVDRP < %s channel %s deleted", *connection, Option); + if (CurrentChannel && CurrentChannel->Number() != CurrentChannelNr) { + if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring()) + Channels->SwitchTo(CurrentChannel->Number()); + else + cDevice::SetCurrentChannel(CurrentChannel); + } + Reply(250, "Channel \"%s\" deleted", Option); } else - Reply(550, "Channels are being edited - try again later"); + Reply(501, "Channel \"%s\" not defined", Option); } else Reply(501, "Error in channel number \"%s\"", Option); @@ -667,7 +1258,7 @@ static cString RecordingInUseMessage(int Reason, const char *RecordingId, cRecor { cRecordControl *rc; if ((Reason & ruTimer) != 0 && (rc = cRecordControls::GetRecordControl(Recording->FileName())) != NULL) - return cString::sprintf("Recording \"%s\" is in use by timer %d", RecordingId, rc->Timer()->Index() + 1); + return cString::sprintf("Recording \"%s\" is in use by timer %d", RecordingId, rc->Timer()->Id()); else if ((Reason & ruReplay) != 0) return cString::sprintf("Recording \"%s\" is being replayed", RecordingId); else if ((Reason & ruCut) != 0) @@ -679,25 +1270,27 @@ static cString RecordingInUseMessage(int Reason, const char *RecordingId, cRecor return NULL; } -void cSVDRP::CmdDELR(const char *Option) +void cSVDRPServer::CmdDELR(const char *Option) { if (*Option) { if (isnumber(Option)) { - cRecording *recording = recordings.Get(strtol(Option, NULL, 10) - 1); - if (recording) { - if (int RecordingInUse = recording->IsInUse()) - Reply(550, "%s", *RecordingInUseMessage(RecordingInUse, Option, recording)); + LOCK_RECORDINGS_WRITE; + Recordings->SetExplicitModify(); + if (cRecording *Recording = Recordings->Get(strtol(Option, NULL, 10) - 1)) { + if (int RecordingInUse = Recording->IsInUse()) + Reply(550, "%s", *RecordingInUseMessage(RecordingInUse, Option, Recording)); else { - if (recording->Delete()) { + if (Recording->Delete()) { + Recordings->DelByName(Recording->FileName()); + Recordings->SetModified(); Reply(250, "Recording \"%s\" deleted", Option); - Recordings.DelByName(recording->FileName()); } else Reply(554, "Error while deleting recording!"); } } else - Reply(550, "Recording \"%s\" not found%s", Option, recordings.Count() ? "" : " (use LSTR before deleting)"); + Reply(550, "Recording \"%s\" not found", Option); } else Reply(501, "Error in recording number \"%s\"", Option); @@ -706,27 +1299,22 @@ void cSVDRP::CmdDELR(const char *Option) Reply(501, "Missing recording number"); } -void cSVDRP::CmdDELT(const char *Option) +void cSVDRPServer::CmdDELT(const char *Option) { if (*Option) { if (isnumber(Option)) { - if (!Timers.BeingEdited()) { - cTimer *timer = Timers.Get(strtol(Option, NULL, 10) - 1); - if (timer) { - if (!timer->Recording()) { - isyslog("deleting timer %s", *timer->ToDescr()); - Timers.Del(timer); - Timers.SetModified(); - Reply(250, "Timer \"%s\" deleted", Option); - } - else - Reply(550, "Timer \"%s\" is recording", Option); - } - else - Reply(501, "Timer \"%s\" not defined", Option); + LOCK_TIMERS_WRITE; + Timers->SetExplicitModify(); + if (cTimer *Timer = Timers->GetById(strtol(Option, NULL, 10))) { + if (Timer->Recording()) + Timer->Skip(); + Timers->Del(Timer); + Timers->SetModified(); + isyslog("SVDRP < %s deleted timer %s", *connection, *Timer->ToDescr()); + Reply(250, "Timer \"%s\" deleted", Option); } else - Reply(550, "Timers are being edited - try again later"); + Reply(501, "Timer \"%s\" not defined", Option); } else Reply(501, "Error in timer number \"%s\"", Option); @@ -735,16 +1323,16 @@ void cSVDRP::CmdDELT(const char *Option) Reply(501, "Missing timer number"); } -void cSVDRP::CmdEDIT(const char *Option) +void cSVDRPServer::CmdEDIT(const char *Option) { if (*Option) { if (isnumber(Option)) { - cRecording *recording = recordings.Get(strtol(Option, NULL, 10) - 1); - if (recording) { + LOCK_RECORDINGS_READ; + if (const cRecording *Recording = Recordings->Get(strtol(Option, NULL, 10) - 1)) { cMarks Marks; - if (Marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording()) && Marks.Count()) { - if (RecordingsHandler.Add(ruCut, recording->FileName())) - Reply(250, "Editing recording \"%s\" [%s]", Option, recording->Title()); + if (Marks.Load(Recording->FileName(), Recording->FramesPerSecond(), Recording->IsPesRecording()) && Marks.Count()) { + if (RecordingsHandler.Add(ruCut, Recording->FileName())) + Reply(250, "Editing recording \"%s\" [%s]", Option, Recording->Title()); else Reply(554, "Can't start editing process"); } @@ -752,7 +1340,7 @@ void cSVDRP::CmdEDIT(const char *Option) Reply(554, "No editing marks defined"); } else - Reply(550, "Recording \"%s\" not found%s", Option, recordings.Count() ? "" : " (use LSTR before editing)"); + Reply(550, "Recording \"%s\" not found", Option); } else Reply(501, "Error in recording number \"%s\"", Option); @@ -761,7 +1349,7 @@ void cSVDRP::CmdEDIT(const char *Option) Reply(501, "Missing recording number"); } -void cSVDRP::CmdGRAB(const char *Option) +void cSVDRPServer::CmdGRAB(const char *Option) { const char *FileName = NULL; bool Jpeg = true; @@ -831,7 +1419,7 @@ void cSVDRP::CmdGRAB(const char *Option) // canonicalize the file name: char RealFileName[PATH_MAX]; if (FileName) { - if (grabImageDir) { + if (*grabImageDir) { cString s(FileName); FileName = s; const char *slash = strrchr(FileName, '/'); @@ -868,7 +1456,7 @@ void cSVDRP::CmdGRAB(const char *Option) int fd = open(FileName, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC, DEFFILEMODE); if (fd >= 0) { if (safe_write(fd, Image, ImageSize) == ImageSize) { - dsyslog("grabbed image to %s", FileName); + dsyslog("SVDRP < %s grabbed image to %s", *connection, FileName); Reply(250, "Grabbed image %s", Option); } else { @@ -898,7 +1486,7 @@ void cSVDRP::CmdGRAB(const char *Option) Reply(501, "Missing filename"); } -void cSVDRP::CmdHELP(const char *Option) +void cSVDRPServer::CmdHELP(const char *Option) { if (*Option) { const char *hp = GetHelpPage(Option, HelpPages); @@ -926,7 +1514,7 @@ void cSVDRP::CmdHELP(const char *Option) Reply(214, "End of HELP info"); } -void cSVDRP::CmdHITK(const char *Option) +void cSVDRPServer::CmdHITK(const char *Option) { if (*Option) { if (!cRemote::Enabled()) { @@ -965,136 +1553,132 @@ void cSVDRP::CmdHITK(const char *Option) } } -void cSVDRP::CmdLSTC(const char *Option) +void cSVDRPServer::CmdLSTC(const char *Option) { + LOCK_CHANNELS_READ; bool WithGroupSeps = strcasecmp(Option, ":groups") == 0; if (*Option && !WithGroupSeps) { if (isnumber(Option)) { - cChannel *channel = Channels.GetByNumber(strtol(Option, NULL, 10)); - if (channel) - Reply(250, "%d %s", channel->Number(), *channel->ToText()); + if (const cChannel *Channel = Channels->GetByNumber(strtol(Option, NULL, 10))) + Reply(250, "%d %s", Channel->Number(), *Channel->ToText()); else Reply(501, "Channel \"%s\" not defined", Option); } else { - cChannel *next = Channels.GetByChannelID(tChannelID::FromString(Option)); - if (!next) { - for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) { - if (!channel->GroupSep()) { - if (strcasestr(channel->Name(), Option)) { - if (next) - Reply(-250, "%d %s", next->Number(), *next->ToText()); - next = channel; + const cChannel *Next = Channels->GetByChannelID(tChannelID::FromString(Option)); + if (!Next) { + for (const cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) { + if (!Channel->GroupSep()) { + if (strcasestr(Channel->Name(), Option)) { + if (Next) + Reply(-250, "%d %s", Next->Number(), *Next->ToText()); + Next = Channel; } } } } - if (next) - Reply(250, "%d %s", next->Number(), *next->ToText()); + if (Next) + Reply(250, "%d %s", Next->Number(), *Next->ToText()); else Reply(501, "Channel \"%s\" not defined", Option); } } - else if (Channels.MaxNumber() >= 1) { - for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) { + else if (cChannels::MaxNumber() >= 1) { + for (const cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) { if (WithGroupSeps) - Reply(channel->Next() ? -250: 250, "%d %s", channel->GroupSep() ? 0 : channel->Number(), *channel->ToText()); - else if (!channel->GroupSep()) - Reply(channel->Number() < Channels.MaxNumber() ? -250 : 250, "%d %s", channel->Number(), *channel->ToText()); + Reply(Channel->Next() ? -250: 250, "%d %s", Channel->GroupSep() ? 0 : Channel->Number(), *Channel->ToText()); + else if (!Channel->GroupSep()) + Reply(Channel->Number() < cChannels::MaxNumber() ? -250 : 250, "%d %s", Channel->Number(), *Channel->ToText()); } } else Reply(550, "No channels defined"); } -void cSVDRP::CmdLSTE(const char *Option) +void cSVDRPServer::CmdLSTE(const char *Option) { - cSchedulesLock SchedulesLock; - const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock); - if (Schedules) { - const cSchedule* Schedule = NULL; - eDumpMode DumpMode = dmAll; - time_t AtTime = 0; - if (*Option) { - char buf[strlen(Option) + 1]; - strcpy(buf, Option); - const char *delim = " \t"; - char *strtok_next; - char *p = strtok_r(buf, delim, &strtok_next); - while (p && DumpMode == dmAll) { - if (strcasecmp(p, "NOW") == 0) - DumpMode = dmPresent; - else if (strcasecmp(p, "NEXT") == 0) - DumpMode = dmFollowing; - else if (strcasecmp(p, "AT") == 0) { - DumpMode = dmAtTime; - if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) { - if (isnumber(p)) - AtTime = strtol(p, NULL, 10); - else { - Reply(501, "Invalid time"); - return; - } - } - else { - Reply(501, "Missing time"); - return; - } - } - else if (!Schedule) { - cChannel* Channel = NULL; + LOCK_SCHEDULES_READ; + const cSchedule* Schedule = NULL; + eDumpMode DumpMode = dmAll; + time_t AtTime = 0; + if (*Option) { + char buf[strlen(Option) + 1]; + strcpy(buf, Option); + const char *delim = " \t"; + char *strtok_next; + char *p = strtok_r(buf, delim, &strtok_next); + while (p && DumpMode == dmAll) { + if (strcasecmp(p, "NOW") == 0) + DumpMode = dmPresent; + else if (strcasecmp(p, "NEXT") == 0) + DumpMode = dmFollowing; + else if (strcasecmp(p, "AT") == 0) { + DumpMode = dmAtTime; + if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) { if (isnumber(p)) - Channel = Channels.GetByNumber(strtol(Option, NULL, 10)); - else - Channel = Channels.GetByChannelID(tChannelID::FromString(Option)); - if (Channel) { - Schedule = Schedules->GetSchedule(Channel); - if (!Schedule) { - Reply(550, "No schedule found"); - return; - } - } + AtTime = strtol(p, NULL, 10); else { - Reply(550, "Channel \"%s\" not defined", p); + Reply(501, "Invalid time"); return; } } else { - Reply(501, "Unknown option: \"%s\"", p); + Reply(501, "Missing time"); return; } - p = strtok_r(NULL, delim, &strtok_next); } - } - int fd = dup(file); - if (fd) { - FILE *f = fdopen(fd, "w"); - if (f) { - if (Schedule) - Schedule->Dump(f, "215-", DumpMode, AtTime); - else - Schedules->Dump(f, "215-", DumpMode, AtTime); - fflush(f); - Reply(215, "End of EPG data"); - fclose(f); - } - else { - Reply(451, "Can't open file connection"); - close(fd); + else if (!Schedule) { + LOCK_CHANNELS_READ; + const cChannel* Channel = NULL; + if (isnumber(p)) + Channel = Channels->GetByNumber(strtol(Option, NULL, 10)); + else + Channel = Channels->GetByChannelID(tChannelID::FromString(Option)); + if (Channel) { + Schedule = Schedules->GetSchedule(Channel); + if (!Schedule) { + Reply(550, "No schedule found"); + return; + } + } + else { + Reply(550, "Channel \"%s\" not defined", p); + return; + } + } + else { + Reply(501, "Unknown option: \"%s\"", p); + return; + } + p = strtok_r(NULL, delim, &strtok_next); } + } + int fd = dup(file); + if (fd) { + FILE *f = fdopen(fd, "w"); + if (f) { + if (Schedule) + Schedule->Dump(f, "215-", DumpMode, AtTime); + else + Schedules->Dump(f, "215-", DumpMode, AtTime); + fflush(f); + Reply(215, "End of EPG data"); + fclose(f); + } + else { + Reply(451, "Can't open file connection"); + close(fd); } - else - Reply(451, "Can't dup stream descriptor"); } else - Reply(451, "Can't get EPG data"); + Reply(451, "Can't dup stream descriptor"); } -void cSVDRP::CmdLSTR(const char *Option) +void cSVDRPServer::CmdLSTR(const char *Option) { int Number = 0; bool Path = false; - recordings.Update(true); + LOCK_RECORDINGS_READ; if (*Option) { char buf[strlen(Option) + 1]; strcpy(buf, Option); @@ -1119,14 +1703,13 @@ void cSVDRP::CmdLSTR(const char *Option) p = strtok_r(NULL, delim, &strtok_next); } if (Number) { - cRecording *recording = recordings.Get(strtol(Option, NULL, 10) - 1); - if (recording) { + if (const cRecording *Recording = Recordings->Get(strtol(Option, NULL, 10) - 1)) { FILE *f = fdopen(file, "w"); if (f) { if (Path) - Reply(250, "%s", recording->FileName()); + Reply(250, "%s", Recording->FileName()); else { - recording->Info()->Write(f, "215-"); + Recording->Info()->Write(f, "215-"); fflush(f); Reply(215, "End of recording information"); } @@ -1139,21 +1722,21 @@ void cSVDRP::CmdLSTR(const char *Option) Reply(550, "Recording \"%s\" not found", Option); } } - else if (recordings.Count()) { - cRecording *recording = recordings.First(); - while (recording) { - Reply(recording == recordings.Last() ? 250 : -250, "%d %s", recording->Index() + 1, recording->Title(' ', true)); - recording = recordings.Next(recording); + else if (Recordings->Count()) { + const cRecording *Recording = Recordings->First(); + while (Recording) { + Reply(Recording == Recordings->Last() ? 250 : -250, "%d %s", Recording->Index() + 1, Recording->Title(' ', true)); + Recording = Recordings->Next(Recording); } } else Reply(550, "No recordings available"); } -void cSVDRP::CmdLSTT(const char *Option) +void cSVDRPServer::CmdLSTT(const char *Option) { - int Number = 0; - bool Id = false; + int Id = 0; + bool UseChannelId = false; if (*Option) { char buf[strlen(Option) + 1]; strcpy(buf, Option); @@ -1162,9 +1745,9 @@ void cSVDRP::CmdLSTT(const char *Option) char *p = strtok_r(buf, delim, &strtok_next); while (p) { if (isnumber(p)) - Number = strtol(p, NULL, 10); + Id = strtol(p, NULL, 10); else if (strcasecmp(p, "ID") == 0) - Id = true; + UseChannelId = true; else { Reply(501, "Unknown option: \"%s\"", p); return; @@ -1172,30 +1755,44 @@ void cSVDRP::CmdLSTT(const char *Option) p = strtok_r(NULL, delim, &strtok_next); } } - if (Number) { - cTimer *timer = Timers.Get(Number - 1); - if (timer) - Reply(250, "%d %s", timer->Index() + 1, *timer->ToText(Id)); - else - Reply(501, "Timer \"%s\" not defined", Option); - } - else if (Timers.Count()) { - for (int i = 0; i < Timers.Count(); i++) { - cTimer *timer = Timers.Get(i); - if (timer) - Reply(i < Timers.Count() - 1 ? -250 : 250, "%d %s", timer->Index() + 1, *timer->ToText(Id)); - else - Reply(501, "Timer \"%d\" not found", i + 1); + LOCK_TIMERS_READ; + if (Id) { + for (const cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) { + if (!Timer->Remote()) { + if (Timer->Id() == Id) { + Reply(250, "%d %s", Timer->Id(), *Timer->ToText(UseChannelId)); + return; + } + } } + Reply(501, "Timer \"%s\" not defined", Option); + return; } - else - Reply(550, "No timers defined"); + else { + const cTimer *LastLocalTimer = Timers->Last(); + while (LastLocalTimer) { + if (LastLocalTimer->Remote()) + LastLocalTimer = Timers->Prev(LastLocalTimer); + else + break; + } + if (LastLocalTimer) { + for (const cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) { + if (!Timer->Remote()) + Reply(Timer != LastLocalTimer ? -250 : 250, "%d %s", Timer->Id(), *Timer->ToText(UseChannelId)); + if (Timer == LastLocalTimer) + break; + } + return; + } + } + Reply(550, "No timers defined"); } -void cSVDRP::CmdMESG(const char *Option) +void cSVDRPServer::CmdMESG(const char *Option) { if (*Option) { - isyslog("SVDRP message: '%s'", Option); + isyslog("SVDRP < %s message '%s'", *connection, Option); Skins.QueueMessage(mtInfo, Option); Reply(250, "Message queued"); } @@ -1203,36 +1800,34 @@ void cSVDRP::CmdMESG(const char *Option) Reply(501, "Missing message"); } -void cSVDRP::CmdMODC(const char *Option) +void cSVDRPServer::CmdMODC(const char *Option) { if (*Option) { char *tail; int n = strtol(Option, &tail, 10); if (tail && tail != Option) { tail = skipspace(tail); - if (!Channels.BeingEdited()) { - cChannel *channel = Channels.GetByNumber(n); - if (channel) { - cChannel ch; - if (ch.Parse(tail)) { - if (Channels.HasUniqueChannelID(&ch, channel)) { - *channel = ch; - Channels.ReNumber(); - Channels.SetModified(true); - isyslog("modifed channel %d %s", channel->Number(), *channel->ToText()); - Reply(250, "%d %s", channel->Number(), *channel->ToText()); - } - else - Reply(501, "Channel settings are not unique"); + LOCK_CHANNELS_WRITE; + Channels->SetExplicitModify(); + if (cChannel *Channel = Channels->GetByNumber(n)) { + cChannel ch; + if (ch.Parse(tail)) { + if (Channels->HasUniqueChannelID(&ch, Channel)) { + *Channel = ch; + Channels->ReNumber(); + Channels->SetModifiedByUser(); + Channels->SetModified(); + isyslog("SVDRP < %s modifed channel %d %s", *connection, Channel->Number(), *Channel->ToText()); + Reply(250, "%d %s", Channel->Number(), *Channel->ToText()); } else - Reply(501, "Error in channel settings"); + Reply(501, "Channel settings are not unique"); } else - Reply(501, "Channel \"%d\" not defined", n); + Reply(501, "Error in channel settings"); } else - Reply(550, "Channels are being edited - try again later"); + Reply(501, "Channel \"%d\" not defined", n); } else Reply(501, "Error in channel number"); @@ -1241,35 +1836,32 @@ void cSVDRP::CmdMODC(const char *Option) Reply(501, "Missing channel settings"); } -void cSVDRP::CmdMODT(const char *Option) +void cSVDRPServer::CmdMODT(const char *Option) { if (*Option) { char *tail; - int n = strtol(Option, &tail, 10); + int Id = strtol(Option, &tail, 10); if (tail && tail != Option) { tail = skipspace(tail); - if (!Timers.BeingEdited()) { - cTimer *timer = Timers.Get(n - 1); - if (timer) { - cTimer t = *timer; - if (strcasecmp(tail, "ON") == 0) - t.SetFlags(tfActive); - else if (strcasecmp(tail, "OFF") == 0) - t.ClrFlags(tfActive); - else if (!t.Parse(tail)) { - Reply(501, "Error in timer settings"); - return; - } - *timer = t; - Timers.SetModified(); - isyslog("timer %s modified (%s)", *timer->ToDescr(), timer->HasFlags(tfActive) ? "active" : "inactive"); - Reply(250, "%d %s", timer->Index() + 1, *timer->ToText()); + LOCK_TIMERS_WRITE; + Timers->SetExplicitModify(); + if (cTimer *Timer = Timers->GetById(Id)) { + cTimer t = *Timer; + if (strcasecmp(tail, "ON") == 0) + t.SetFlags(tfActive); + else if (strcasecmp(tail, "OFF") == 0) + t.ClrFlags(tfActive); + else if (!t.Parse(tail)) { + Reply(501, "Error in timer settings"); + return; } - else - Reply(501, "Timer \"%d\" not defined", n); + *Timer = t; + Timers->SetModified(); + isyslog("SVDRP < %s modified timer %s (%s)", *connection, *Timer->ToDescr(), Timer->HasFlags(tfActive) ? "active" : "inactive"); + Reply(250, "%d %s", Timer->Id(), *Timer->ToText(true)); } else - Reply(550, "Timers are being edited - try again later"); + Reply(501, "Timer \"%d\" not defined", Id); } else Reply(501, "Error in timer number"); @@ -1278,60 +1870,60 @@ void cSVDRP::CmdMODT(const char *Option) Reply(501, "Missing timer settings"); } -void cSVDRP::CmdMOVC(const char *Option) +void cSVDRPServer::CmdMOVC(const char *Option) { if (*Option) { - if (!Channels.BeingEdited() && !Timers.BeingEdited()) { - char *tail; - int From = strtol(Option, &tail, 10); + char *tail; + int From = strtol(Option, &tail, 10); + if (tail && tail != Option) { + tail = skipspace(tail); if (tail && tail != Option) { - tail = skipspace(tail); - if (tail && tail != Option) { - int To = strtol(tail, NULL, 10); - int CurrentChannelNr = cDevice::CurrentChannel(); - cChannel *CurrentChannel = Channels.GetByNumber(CurrentChannelNr); - cChannel *FromChannel = Channels.GetByNumber(From); - if (FromChannel) { - cChannel *ToChannel = Channels.GetByNumber(To); - if (ToChannel) { - int FromNumber = FromChannel->Number(); - int ToNumber = ToChannel->Number(); - if (FromNumber != ToNumber) { - Channels.Move(FromChannel, ToChannel); - Channels.ReNumber(); - Channels.SetModified(true); - if (CurrentChannel && CurrentChannel->Number() != CurrentChannelNr) { - if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring()) - Channels.SwitchTo(CurrentChannel->Number()); - else - cDevice::SetCurrentChannel(CurrentChannel); - } - isyslog("channel %d moved to %d", FromNumber, ToNumber); - Reply(250,"Channel \"%d\" moved to \"%d\"", From, To); + LOCK_TIMERS_READ; // necessary to keep timers and channels in sync! + LOCK_CHANNELS_WRITE; + Channels->SetExplicitModify(); + int To = strtol(tail, NULL, 10); + int CurrentChannelNr = cDevice::CurrentChannel(); + const cChannel *CurrentChannel = Channels->GetByNumber(CurrentChannelNr); + cChannel *FromChannel = Channels->GetByNumber(From); + if (FromChannel) { + cChannel *ToChannel = Channels->GetByNumber(To); + if (ToChannel) { + int FromNumber = FromChannel->Number(); + int ToNumber = ToChannel->Number(); + if (FromNumber != ToNumber) { + Channels->Move(FromChannel, ToChannel); + Channels->ReNumber(); + Channels->SetModifiedByUser(); + Channels->SetModified(); + if (CurrentChannel && CurrentChannel->Number() != CurrentChannelNr) { + if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring()) + Channels->SwitchTo(CurrentChannel->Number()); + else + cDevice::SetCurrentChannel(CurrentChannel); } - else - Reply(501, "Can't move channel to same position"); + isyslog("SVDRP < %s channel %d moved to %d", *connection, FromNumber, ToNumber); + Reply(250,"Channel \"%d\" moved to \"%d\"", From, To); } else - Reply(501, "Channel \"%d\" not defined", To); + Reply(501, "Can't move channel to same position"); } else - Reply(501, "Channel \"%d\" not defined", From); + Reply(501, "Channel \"%d\" not defined", To); } else - Reply(501, "Error in channel number"); + Reply(501, "Channel \"%d\" not defined", From); } else Reply(501, "Error in channel number"); } else - Reply(550, "Channels or timers are being edited - try again later"); + Reply(501, "Error in channel number"); } else Reply(501, "Missing channel number"); } -void cSVDRP::CmdMOVR(const char *Option) +void cSVDRPServer::CmdMOVR(const char *Option) { if (*Option) { char *opt = strdup(Option); @@ -1342,17 +1934,21 @@ void cSVDRP::CmdMOVR(const char *Option) char c = *option; *option = 0; if (isnumber(num)) { - cRecording *recording = recordings.Get(strtol(num, NULL, 10) - 1); - if (recording) { - if (int RecordingInUse = recording->IsInUse()) - Reply(550, "%s", *RecordingInUseMessage(RecordingInUse, Option, recording)); + LOCK_RECORDINGS_WRITE; + Recordings->SetExplicitModify(); + if (cRecording *Recording = Recordings->Get(strtol(num, NULL, 10) - 1)) { + if (int RecordingInUse = Recording->IsInUse()) + Reply(550, "%s", *RecordingInUseMessage(RecordingInUse, Option, Recording)); else { if (c) option = skipspace(++option); if (*option) { - cString oldName = recording->Name(); - if ((recording = Recordings.GetByName(recording->FileName())) != NULL && recording->ChangeName(option)) - Reply(250, "Recording \"%s\" moved to \"%s\"", *oldName, recording->Name()); + cString oldName = Recording->Name(); + if ((Recording = Recordings->GetByName(Recording->FileName())) != NULL && Recording->ChangeName(option)) { + Recordings->SetModified(); + Recordings->TouchUpdate(); + Reply(250, "Recording \"%s\" moved to \"%s\"", *oldName, Recording->Name()); + } else Reply(554, "Error while moving recording \"%s\" to \"%s\"!", *oldName, option); } @@ -1361,7 +1957,7 @@ void cSVDRP::CmdMOVR(const char *Option) } } else - Reply(550, "Recording \"%s\" not found%s", num, recordings.Count() ? "" : " (use LSTR before moving)"); + Reply(550, "Recording \"%s\" not found", num); } else Reply(501, "Error in recording number \"%s\"", num); @@ -1371,18 +1967,21 @@ void cSVDRP::CmdMOVR(const char *Option) Reply(501, "Missing recording number"); } -void cSVDRP::CmdNEWC(const char *Option) +void cSVDRPServer::CmdNEWC(const char *Option) { if (*Option) { cChannel ch; if (ch.Parse(Option)) { - if (Channels.HasUniqueChannelID(&ch)) { + LOCK_CHANNELS_WRITE; + Channels->SetExplicitModify(); + if (Channels->HasUniqueChannelID(&ch)) { cChannel *channel = new cChannel; *channel = ch; - Channels.Add(channel); - Channels.ReNumber(); - Channels.SetModified(true); - isyslog("new channel %d %s", channel->Number(), *channel->ToText()); + Channels->Add(channel); + Channels->ReNumber(); + Channels->SetModifiedByUser(); + Channels->SetModified(); + isyslog("SVDRP < %s new channel %d %s", *connection, channel->Number(), *channel->ToText()); Reply(250, "%d %s", channel->Number(), *channel->ToText()); } else @@ -1395,37 +1994,38 @@ void cSVDRP::CmdNEWC(const char *Option) Reply(501, "Missing channel settings"); } -void cSVDRP::CmdNEWT(const char *Option) +void cSVDRPServer::CmdNEWT(const char *Option) { if (*Option) { - cTimer *timer = new cTimer; - if (timer->Parse(Option)) { - Timers.Add(timer); - Timers.SetModified(); - isyslog("timer %s added", *timer->ToDescr()); - Reply(250, "%d %s", timer->Index() + 1, *timer->ToText()); + cTimer *Timer = new cTimer; + if (Timer->Parse(Option)) { + LOCK_TIMERS_WRITE; + Timer->ClrFlags(tfRecording); + Timers->Add(Timer); + isyslog("SVDRP < %s added timer %s", *connection, *Timer->ToDescr()); + Reply(250, "%d %s", Timer->Id(), *Timer->ToText(true)); return; } else Reply(501, "Error in timer settings"); - delete timer; + delete Timer; } else Reply(501, "Missing timer settings"); } -void cSVDRP::CmdNEXT(const char *Option) +void cSVDRPServer::CmdNEXT(const char *Option) { - cTimer *t = Timers.GetNextActiveTimer(); - if (t) { + LOCK_TIMERS_READ; + if (const cTimer *t = Timers->GetNextActiveTimer()) { time_t Start = t->StartTime(); - int Number = t->Index() + 1; + int Id = t->Id(); if (!*Option) - Reply(250, "%d %s", Number, *TimeToString(Start)); + Reply(250, "%d %s", Id, *TimeToString(Start)); else if (strcasecmp(Option, "ABS") == 0) - Reply(250, "%d %ld", Number, Start); + Reply(250, "%d %ld", Id, Start); else if (strcasecmp(Option, "REL") == 0) - Reply(250, "%d %ld", Number, Start - time(NULL)); + Reply(250, "%d %ld", Id, Start - time(NULL)); else Reply(501, "Unknown option: \"%s\"", Option); } @@ -1433,7 +2033,12 @@ void cSVDRP::CmdNEXT(const char *Option) Reply(550, "No active timers"); } -void cSVDRP::CmdPLAY(const char *Option) +void cSVDRPServer::CmdPING(const char *Option) +{ + Reply(250, "%s is alive", Setup.SVDRPHostName); +} + +void cSVDRPServer::CmdPLAY(const char *Option) { if (*Option) { char *opt = strdup(Option); @@ -1444,8 +2049,8 @@ void cSVDRP::CmdPLAY(const char *Option) char c = *option; *option = 0; if (isnumber(num)) { - cRecording *recording = recordings.Get(strtol(num, NULL, 10) - 1); - if (recording) { + LOCK_RECORDINGS_READ; + if (const cRecording *Recording = Recordings->Get(strtol(num, NULL, 10) - 1)) { if (c) option = skipspace(++option); cReplayControl::SetRecording(NULL); @@ -1453,20 +2058,20 @@ void cSVDRP::CmdPLAY(const char *Option) if (*option) { int pos = 0; if (strcasecmp(option, "BEGIN") != 0) - pos = HMSFToIndex(option, recording->FramesPerSecond()); - cResumeFile resume(recording->FileName(), recording->IsPesRecording()); + pos = HMSFToIndex(option, Recording->FramesPerSecond()); + cResumeFile Resume(Recording->FileName(), Recording->IsPesRecording()); if (pos <= 0) - resume.Delete(); + Resume.Delete(); else - resume.Save(pos); + Resume.Save(pos); } - cReplayControl::SetRecording(recording->FileName()); + cReplayControl::SetRecording(Recording->FileName()); cControl::Launch(new cReplayControl); cControl::Attach(); - Reply(250, "Playing recording \"%s\" [%s]", num, recording->Title()); + Reply(250, "Playing recording \"%s\" [%s]", num, Recording->Title()); } else - Reply(550, "Recording \"%s\" not found%s", num, recordings.Count() ? "" : " (use LSTR before playing)"); + Reply(550, "Recording \"%s\" not found", num); } else Reply(501, "Error in recording number \"%s\"", num); @@ -1476,7 +2081,7 @@ void cSVDRP::CmdPLAY(const char *Option) Reply(501, "Missing recording number"); } -void cSVDRP::CmdPLUG(const char *Option) +void cSVDRPServer::CmdPLUG(const char *Option) { if (*Option) { char *opt = strdup(Option); @@ -1547,7 +2152,37 @@ void cSVDRP::CmdPLUG(const char *Option) } } -void cSVDRP::CmdPUTE(const char *Option) +void cSVDRPServer::CmdPOLL(const char *Option) +{ + if (*Option) { + char buf[strlen(Option) + 1]; + char *p = strcpy(buf, Option); + const char *delim = " \t"; + char *strtok_next; + char *RemoteName = strtok_r(p, delim, &strtok_next); + char *ListName = strtok_r(NULL, delim, &strtok_next); + if (SVDRPClientHandler) { + if (ListName) { + if (strcasecmp(ListName, "timers") == 0) { + if (SVDRPClientHandler->TriggerFetchingTimers(RemoteName)) + Reply(250, "OK"); + else + Reply(501, "No connection to \"%s\"", RemoteName); + } + else + Reply(501, "Unknown list name: \"%s\"", ListName); + } + else + Reply(501, "Missing list name"); + } + else + Reply(501, "No SVDRP client connections"); + } + else + Reply(501, "Missing parameters"); +} + +void cSVDRPServer::CmdPUTE(const char *Option) { if (*Option) { FILE *f = fopen(Option, "r"); @@ -1572,7 +2207,7 @@ void cSVDRP::CmdPUTE(const char *Option) } } -void cSVDRP::CmdREMO(const char *Option) +void cSVDRPServer::CmdREMO(const char *Option) { if (*Option) { if (!strcasecmp(Option, "ON")) { @@ -1590,13 +2225,13 @@ void cSVDRP::CmdREMO(const char *Option) Reply(250, "Remote control is %s", cRemote::Enabled() ? "enabled" : "disabled"); } -void cSVDRP::CmdSCAN(const char *Option) +void cSVDRPServer::CmdSCAN(const char *Option) { EITScanner.ForceScan(); Reply(250, "EPG scan triggered"); } -void cSVDRP::CmdSTAT(const char *Option) +void cSVDRPServer::CmdSTAT(const char *Option) { if (*Option) { if (strcasecmp(Option, "DISK") == 0) { @@ -1611,45 +2246,41 @@ void cSVDRP::CmdSTAT(const char *Option) Reply(501, "No option given"); } -void cSVDRP::CmdUPDT(const char *Option) +void cSVDRPServer::CmdUPDT(const char *Option) { if (*Option) { - cTimer *timer = new cTimer; - if (timer->Parse(Option)) { - if (!Timers.BeingEdited()) { - cTimer *t = Timers.GetTimer(timer); - if (t) { - t->Parse(Option); - delete timer; - timer = t; - isyslog("timer %s updated", *timer->ToDescr()); - } - else { - Timers.Add(timer); - isyslog("timer %s added", *timer->ToDescr()); - } - Timers.SetModified(); - Reply(250, "%d %s", timer->Index() + 1, *timer->ToText()); - return; + cTimer *Timer = new cTimer; + if (Timer->Parse(Option)) { + LOCK_TIMERS_WRITE; + if (cTimer *t = Timers->GetTimer(Timer)) { + t->Parse(Option); + delete Timer; + Timer = t; + isyslog("SVDRP < %s updated timer %s", *connection, *Timer->ToDescr()); } - else - Reply(550, "Timers are being edited - try again later"); + else { + Timers->Add(Timer); + isyslog("SVDRP < %s added timer %s", *connection, *Timer->ToDescr()); + } + Reply(250, "%d %s", Timer->Id(), *Timer->ToText(true)); + return; } else Reply(501, "Error in timer settings"); - delete timer; + delete Timer; } else Reply(501, "Missing timer settings"); } -void cSVDRP::CmdUPDR(const char *Option) +void cSVDRPServer::CmdUPDR(const char *Option) { - Recordings.Update(false); + LOCK_RECORDINGS_WRITE; + Recordings->Update(false); Reply(250, "Re-read of recordings directory triggered"); } -void cSVDRP::CmdVOLU(const char *Option) +void cSVDRPServer::CmdVOLU(const char *Option) { if (*Option) { if (isnumber(Option)) @@ -1673,7 +2304,7 @@ void cSVDRP::CmdVOLU(const char *Option) #define CMD(c) (strcasecmp(Cmd, c) == 0) -void cSVDRP::Execute(char *Cmd) +void cSVDRPServer::Execute(char *Cmd) { // handle PUTE data: if (PUTEhandler) { @@ -1714,8 +2345,10 @@ void cSVDRP::Execute(char *Cmd) else if (CMD("NEWC")) CmdNEWC(s); else if (CMD("NEWT")) CmdNEWT(s); else if (CMD("NEXT")) CmdNEXT(s); + else if (CMD("PING")) CmdPING(s); else if (CMD("PLAY")) CmdPLAY(s); else if (CMD("PLUG")) CmdPLUG(s); + else if (CMD("POLL")) CmdPOLL(s); else if (CMD("PUTE")) CmdPUTE(s); else if (CMD("REMO")) CmdREMO(s); else if (CMD("SCAN")) CmdSCAN(s); @@ -1727,21 +2360,9 @@ void cSVDRP::Execute(char *Cmd) else Reply(500, "Command unrecognized: \"%s\"", Cmd); } -bool cSVDRP::Process(void) +bool cSVDRPServer::Process(void) { - bool NewConnection = !file.IsOpen(); - bool SendGreeting = NewConnection; - - if (file.IsOpen() || file.Open(socket.Accept())) { - if (SendGreeting) { - //TODO how can we get the *full* hostname? - char buffer[BUFSIZ]; - gethostname(buffer, sizeof(buffer)); - time_t now = time(NULL); - Reply(220, "%s SVDRP VideoDiskRecorder %s; %s; %s", buffer, VDRVERSION, *TimeToString(now), cCharSetConv::SystemCharacterTable() ? cCharSetConv::SystemCharacterTable() : "UTF-8"); - } - if (NewConnection) - lastActivity = time(NULL); + if (file.IsOpen()) { while (file.Ready(false)) { unsigned char c; int r = safe_read(file, &c, 1); @@ -1781,7 +2402,7 @@ bool cSVDRP::Process(void) cmdLine = NewBuffer; } else { - esyslog("ERROR: out of memory"); + esyslog("SVDRP < %s ERROR: out of memory", *connection); Close(); break; } @@ -1792,23 +2413,161 @@ bool cSVDRP::Process(void) lastActivity = time(NULL); } else if (r <= 0) { - isyslog("lost connection to SVDRP client"); + isyslog("SVDRP < %s lost connection to client", *connection); Close(); } } if (Setup.SVDRPTimeout && time(NULL) - lastActivity > Setup.SVDRPTimeout) { - isyslog("timeout on SVDRP connection"); + isyslog("SVDRP < %s timeout on connection", *connection); Close(true, true); } - return true; } + return file.IsOpen(); +} + +void SetSVDRPPorts(int TcpPort, int UdpPort) +{ + SVDRPTcpPort = TcpPort; + SVDRPUdpPort = UdpPort; +} + +void SetSVDRPGrabImageDir(const char *GrabImageDir) +{ + grabImageDir = GrabImageDir; +} + +// --- cSVDRPServerHandler --------------------------------------------------- + +class cSVDRPServerHandler : public cThread { +private: + cMutex mutex; + bool ready; + cSocket tcpSocket; + cVector serverConnections; + void HandleServerConnection(void); + void ProcessConnections(void); +protected: + virtual void Action(void); +public: + cSVDRPServerHandler(int TcpPort); + virtual ~cSVDRPServerHandler(); + void WaitUntilReady(void); + }; + +static cSVDRPServerHandler *SVDRPServerHandler = NULL; + +cSVDRPServerHandler::cSVDRPServerHandler(int TcpPort) +:cThread("SVDRP server handler", true) +,tcpSocket(TcpPort, true) +{ + ready = false; +} + +cSVDRPServerHandler::~cSVDRPServerHandler() +{ + Cancel(3); + for (int i = 0; i < serverConnections.Size(); i++) + delete serverConnections[i]; +} + +void cSVDRPServerHandler::WaitUntilReady(void) +{ + cTimeMs Timeout(3000); + while (!ready && !Timeout.TimedOut()) + cCondWait::SleepMs(10); +} + +void cSVDRPServerHandler::ProcessConnections(void) +{ + cMutexLock MutexLock(&mutex); + for (int i = 0; i < serverConnections.Size(); i++) { + if (!serverConnections[i]->Process()) { + delete serverConnections[i]; + serverConnections.Remove(i); + i--; + } + } +} + +void cSVDRPServerHandler::HandleServerConnection(void) +{ + int NewSocket = tcpSocket.Accept(); + if (NewSocket >= 0) + serverConnections.Append(new cSVDRPServer(NewSocket, tcpSocket.LastIpAddress()->Connection())); +} + +void cSVDRPServerHandler::Action(void) +{ + if (tcpSocket.Listen()) { + SVDRPServerPoller.Add(tcpSocket.Socket(), false); + ready = true; + while (Running()) { + SVDRPServerPoller.Poll(1000); + cMutexLock MutexLock(&mutex); + HandleServerConnection(); + ProcessConnections(); + } + SVDRPServerPoller.Del(tcpSocket.Socket(), false); + tcpSocket.Close(); + } +} + +// --- SVDRP Handler --------------------------------------------------------- + +static cMutex SVDRPHandlerMutex; + +void StartSVDRPServerHandler(void) +{ + cMutexLock MutexLock(&SVDRPHandlerMutex); + if (SVDRPTcpPort && !SVDRPServerHandler) { + SVDRPServerHandler = new cSVDRPServerHandler(SVDRPTcpPort); + SVDRPServerHandler->Start(); + SVDRPServerHandler->WaitUntilReady(); + } +} + +void StartSVDRPClientHandler(void) +{ + cMutexLock MutexLock(&SVDRPHandlerMutex); + if (SVDRPTcpPort && SVDRPUdpPort && !SVDRPClientHandler) { + SVDRPClientHandler = new cSVDRPClientHandler(SVDRPTcpPort, SVDRPUdpPort); + SVDRPClientHandler->Start(); + } +} + +void StopSVDRPServerHandler(void) +{ + cMutexLock MutexLock(&SVDRPHandlerMutex); + delete SVDRPServerHandler; + SVDRPServerHandler = NULL; +} + +void StopSVDRPClientHandler(void) +{ + cMutexLock MutexLock(&SVDRPHandlerMutex); + delete SVDRPClientHandler; + SVDRPClientHandler = NULL; +} + +void SendSVDRPDiscover(const char *Address) +{ + cMutexLock MutexLock(&SVDRPHandlerMutex); + if (SVDRPClientHandler) + SVDRPClientHandler->SendDiscover(Address); +} + +bool GetSVDRPServerNames(cStringList *ServerNames, eSvdrpFetchFlags FetchFlag) +{ + cMutexLock MutexLock(&SVDRPHandlerMutex); + if (SVDRPClientHandler) + return SVDRPClientHandler->GetServerNames(ServerNames, FetchFlag); return false; } -void cSVDRP::SetGrabImageDir(const char *GrabImageDir) +bool ExecSVDRPCommand(const char *ServerName, const char *Command, cStringList *Response) { - free(grabImageDir); - grabImageDir = GrabImageDir ? strdup(GrabImageDir) : NULL; + cMutexLock MutexLock(&SVDRPHandlerMutex); + if (SVDRPClientHandler) + return SVDRPClientHandler->Execute(ServerName, Command, Response); + return false; } - -//TODO more than one connection??? diff --git a/svdrp.h b/svdrp.h index 8ac419af..ba33fc27 100644 --- a/svdrp.h +++ b/svdrp.h @@ -4,93 +4,49 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: svdrp.h 3.2 2013/10/21 07:42:03 kls Exp $ + * $Id: svdrp.h 4.5 2015/09/09 09:44:12 kls Exp $ */ #ifndef __SVDRP_H #define __SVDRP_H -#include "recording.h" #include "tools.h" -class cSocket { -private: - int port; - int sock; - int queue; - void Close(void); -public: - cSocket(int Port, int Queue = 1); - ~cSocket(); - bool Open(void); - int Accept(void); +enum eSvdrpFetchFlags { + sffNone = 0b0000, + sffTimers = 0b0001, }; -class cPUTEhandler { -private: - FILE *f; - int status; - const char *message; -public: - cPUTEhandler(void); - ~cPUTEhandler(); - bool Process(const char *s); - int Status(void) { return status; } - const char *Message(void) { return message; } - }; - -class cSVDRP { -private: - cSocket socket; - cFile file; - cRecordings recordings; - cPUTEhandler *PUTEhandler; - int numChars; - int length; - char *cmdLine; - time_t lastActivity; - static char *grabImageDir; - void Close(bool SendReply = false, bool Timeout = false); - bool Send(const char *s, int length = -1); - void Reply(int Code, const char *fmt, ...) __attribute__ ((format (printf, 3, 4))); - void PrintHelpTopics(const char **hp); - void CmdCHAN(const char *Option); - void CmdCLRE(const char *Option); - void CmdDELC(const char *Option); - void CmdDELR(const char *Option); - void CmdDELT(const char *Option); - void CmdEDIT(const char *Option); - void CmdGRAB(const char *Option); - void CmdHELP(const char *Option); - void CmdHITK(const char *Option); - void CmdLSTC(const char *Option); - void CmdLSTE(const char *Option); - void CmdLSTR(const char *Option); - void CmdLSTT(const char *Option); - void CmdMESG(const char *Option); - void CmdMODC(const char *Option); - void CmdMODT(const char *Option); - void CmdMOVC(const char *Option); - void CmdMOVR(const char *Option); - void CmdNEWC(const char *Option); - void CmdNEWT(const char *Option); - void CmdNEXT(const char *Option); - void CmdPLAY(const char *Option); - void CmdPLUG(const char *Option); - void CmdPUTE(const char *Option); - void CmdREMO(const char *Option); - void CmdSCAN(const char *Option); - void CmdSTAT(const char *Option); - void CmdUPDT(const char *Option); - void CmdUPDR(const char *Option); - void CmdVOLU(const char *Option); - void Execute(char *Cmd); -public: - cSVDRP(int Port); - ~cSVDRP(); - bool HasConnection(void) { return file.IsOpen(); } - bool Process(void); - static void SetGrabImageDir(const char *GrabImageDir); - }; +void SetSVDRPPorts(int TcpPort, int UdpPort); +void SetSVDRPGrabImageDir(const char *GrabImageDir); +void StartSVDRPServerHandler(void); +void StartSVDRPClientHandler(void); +void StopSVDRPServerHandler(void); +void StopSVDRPClientHandler(void); +void SendSVDRPDiscover(const char *Address = NULL); +bool GetSVDRPServerNames(cStringList *ServerNames, eSvdrpFetchFlags FetchFlag = sffNone); + ///< Gets a list of all available VDRs this VDR is connected to via SVDRP, + ///< and stores it in the given ServerNames list. The list is cleared + ///< before getting the server names. + ///< If FetchFlag is given, only the server names for which the local + ///< client has this flag set will be returned, and the client's flag + ///< will be cleared. + ///< Returns true if the resulting list is not empty. +bool ExecSVDRPCommand(const char *ServerName, const char *Command, cStringList *Response = NULL); + ///< Sends the given SVDRP Command string to the remote VDR identified + ///< by ServerName and collects all of the response strings in Response. + ///< If no Response parameter is given, the response from command execution + ///< is ignored. + ///< Returns true if the data exchange was successful. Whether or + ///< not the actual SVDRP command was successful depends on the + ///< resulting strings from the remote VDR, which can be accessed + ///< through Response. If Response is given, it will be cleared before + ///< the command is actually executed. +inline int SVDRPCode(const char *s) { return s ? atoi(s) : 0; } + ///< Returns the value of the three digit reply code of the given + ///< SVDRP response string. +inline const char *SVDRPValue(const char *s) { return s && s[0] && s[1] && s[2] && s[3] ? s + 4 : NULL; } + ///< Returns the actual value of the given SVDRP response string, skipping + ///< the three digit reply code and possible continuation line indicator. #endif //__SVDRP_H diff --git a/themes.c b/themes.c index ccc21640..76a0ff81 100644 --- a/themes.c +++ b/themes.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: themes.c 3.0 2012/02/17 13:57:32 kls Exp $ + * $Id: themes.c 4.0 2012/02/17 13:57:32 kls Exp $ */ #include "themes.h" diff --git a/themes.h b/themes.h index 1bb9cf13..44bb0a34 100644 --- a/themes.h +++ b/themes.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: themes.h 3.0 2012/10/07 11:11:43 kls Exp $ + * $Id: themes.h 4.0 2012/10/07 11:11:43 kls Exp $ */ #ifndef __THEMES_H diff --git a/thread.c b/thread.c index e5e19c9b..993d16dd 100644 --- a/thread.c +++ b/thread.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: thread.c 3.2 2013/12/29 15:26:33 kls Exp $ + * $Id: thread.c 4.1 2015/08/29 14:43:03 kls Exp $ */ #include "thread.h" @@ -21,6 +21,16 @@ #include #include "tools.h" +#define ABORT { dsyslog("ABORT!"); abort(); } // use debugger to trace back the problem + +//#define DEBUG_LOCKING // uncomment this line to activate debug output for locking + +#ifdef DEBUG_LOCKING +#define dbglocking(a...) fprintf(stderr, a) +#else +#define dbglocking(a...) +#endif + static bool GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow) { struct timeval now; @@ -403,6 +413,132 @@ bool cThreadLock::Lock(cThread *Thread) return false; } +// --- cStateLock ------------------------------------------------------------ + +cStateLock::cStateLock(const char *Name) +:rwLock(true) +{ + name = Name; + threadId = 0; + state = 0; + explicitModify = false; +} + +bool cStateLock::Lock(cStateKey &StateKey, bool Write, int TimeoutMs) +{ + dbglocking("%5d %-10s %10p lock state = %d/%d write = %d timeout = %d\n", cThread::ThreadId(), name, &StateKey, state, StateKey.state, Write, TimeoutMs); + StateKey.timedOut = false; + if (StateKey.stateLock) { + esyslog("ERROR: StateKey already in use in call to cStateLock::Lock() (tid=%d, lock=%s)", StateKey.stateLock->threadId, name); + ABORT; + return false; + } + if (rwLock.Lock(Write, TimeoutMs)) { + StateKey.stateLock = this; + if (Write) { + dbglocking("%5d %-10s %10p locked write\n", cThread::ThreadId(), name, &StateKey); + threadId = cThread::ThreadId(); + StateKey.write = true; + return true; + } + else if (state != StateKey.state) { + dbglocking("%5d %-10s %10p locked read\n", cThread::ThreadId(), name, &StateKey); + return true; + } + else { + dbglocking("%5d %-10s %10p state unchanged\n", cThread::ThreadId(), name, &StateKey); + StateKey.stateLock = NULL; + rwLock.Unlock(); + } + } + else if (TimeoutMs) { + dbglocking("%5d %-10s %10p timeout\n", cThread::ThreadId(), name, &StateKey); + StateKey.timedOut = true; + } + return false; +} + +void cStateLock::Unlock(cStateKey &StateKey, bool IncState) +{ + dbglocking("%5d %-10s %10p unlock state = %d/%d inc = %d\n", cThread::ThreadId(), name, &StateKey, state, StateKey.state, IncState); + if (StateKey.stateLock != this) { + esyslog("ERROR: cStateLock::Unlock() called with an unused key (tid=%d, lock=%s)", threadId, name); + ABORT; + return; + } + if (StateKey.write && threadId != cThread::ThreadId()) { + esyslog("ERROR: cStateLock::Unlock() called without holding a lock (tid=%d, lock=%s)", threadId, name); + ABORT; + return; + } + if (StateKey.write && IncState && !explicitModify) + state++; + StateKey.state = state; + StateKey.write = false; + threadId = 0; + explicitModify = false; + rwLock.Unlock(); +} + +void cStateLock::IncState(void) +{ + if (threadId != cThread::ThreadId()) { + esyslog("ERROR: cStateLock::IncState() called without holding a lock (tid=%d, lock=%s)", threadId, name); + ABORT; + } + else + state++; +} + +// --- cStateKey ------------------------------------------------------------- + +cStateKey::cStateKey(bool IgnoreFirst) +{ + stateLock = NULL; + write = false; + state = 0; + if (!IgnoreFirst) + Reset(); +} + +cStateKey::~cStateKey() +{ + if (stateLock) { + esyslog("ERROR: cStateKey::~cStateKey() called without releasing the lock first (tid=%d, lock=%s, key=%p)", stateLock->threadId, stateLock->name, this); + ABORT; + } +} + +void cStateKey::Reset(void) +{ + state = -1; // lock and key are initialized differently, to make the first check return true +} + +void cStateKey::Remove(bool IncState) +{ + if (stateLock) { + stateLock->Unlock(*this, IncState); + stateLock = NULL; + } + else { + esyslog("ERROR: cStateKey::Remove() called without holding a lock (key=%p)", this); + ABORT; + } +} + +bool cStateKey::StateChanged(void) +{ + if (!stateLock) { + esyslog("ERROR: cStateKey::StateChanged() called without holding a lock (tid=%d, key=%p)", cThread::ThreadId(), this); + ABORT; + } + else if (write) + return state != stateLock->state; + else + return true; + return false; +} + // --- cIoThrottle ----------------------------------------------------------- cMutex cIoThrottle::mutex; diff --git a/thread.h b/thread.h index d2d8ee2a..b5a07c79 100644 --- a/thread.h +++ b/thread.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: thread.h 3.2 2015/01/14 11:39:55 kls Exp $ + * $Id: thread.h 4.1 2015/08/17 13:06:24 kls Exp $ */ #ifndef __THREAD_H @@ -164,6 +164,94 @@ public: #define LOCK_THREAD cThreadLock ThreadLock(this) +class cStateKey; + +class cStateLock { + friend class cStateKey; +private: + const char *name; + tThreadId threadId; + cRwLock rwLock; + int state; + bool explicitModify; + void Unlock(cStateKey &StateKey, bool IncState = true); + ///< Releases a lock that has been obtained by a previous call to Lock() + ///< with the given StateKey. If this was a write-lock, and IncState is true, + ///< the state of the lock will be incremented. In any case, the (new) state + ///< of the lock will be copied to the StateKey's state. +public: + cStateLock(const char *Name = NULL); + bool Lock(cStateKey &StateKey, bool Write = false, int TimeoutMs = 0); + ///< Tries to get a lock and returns true if successful. + ///< If TimoutMs is not 0, it waits for the given number of milliseconds + ///< and returns false if no lock has been obtained within that time. + ///< Otherwise it waits indefinitely for the lock. The given StateKey + ///< will store which lock it has been used with, and will use that + ///< information when its Remove() function is called. + ///< There are two possible locks, one only for read access, and one + ///< for reading and writing: + ///< + ///< If Write is false (i.e. a read-lock is requested), the lock's state + ///< is compared to the given StateKey's state, and true is returned if + ///< they differ. + ///< If true is returned, the read-lock is still in place and the + ///< protected data structures can be safely accessed (in read-only mode!). + ///< Once the necessary operations have been performed, the lock must + ///< be released by a call to the StateKey's Remove() function. + ///< If false is returned, the state has not changed since the last check, + ///< and the read-lock has been released. In that case the protected + ///< data structures have not changed since the last call, so no action + ///< is required. Note that if TimeoutMs is used with read-locks, Lock() + ///< might return false even if the states of lock and key differ, just + ///< because it was unable to obtain the lock within the given time. + ///< You can call cStateKey::TimedOut() to detect this. + ///< + ///< If Write is true (i.e. a write-lock is requested), the states of the + ///< lock and the given StateKey don't matter, it will always try to obtain + ///< a write lock. + void SetExplicitModify(void) { explicitModify = true; } + ///< If you have obtained a write lock on this lock, and you don't want its + ///< state to be automatically incremented when the lock is released, a call to + ///< this function will disable this, and you can explicitly call IncState() + ///< to increment the state. + void IncState(void); + ///< Increments the state of this lock. + }; + +class cStateKey { + friend class cStateLock; +private: + cStateLock *stateLock; + bool write; + int state; + bool timedOut; +public: + cStateKey(bool IgnoreFirst = false); + ///< Sets up a new state key. If IgnoreFirst is true, the first use + ///< of this key with a lock will not return true if the lock's state + ///< hasn't explicitly changed. + ~cStateKey(); + void Reset(void); + ///< Resets the state of this key, so that the next call to a lock's + ///< Lock() function with this key will return true, even if the + ///< lock's state hasn't changed. + void Remove(bool IncState = true); + ///< Removes this key from the lock it was previously used with. + ///< If this key was used to obtain a write lock, the state of the lock will + ///< be incremented and copied to this key. You can set IncState to false + ///< to prevent this. + bool StateChanged(void); + ///< Returns true if this key is used for obtaining a write lock, and the + ///< lock's state differs from that of the key. When used with a read lock, + ///< it always returns true, because otherwise the lock wouldn't have been + ///< obtained in the first place. + bool InLock(void) { return stateLock; } + ///< Returns true if this key is currently in a lock. + bool TimedOut(void) const { return timedOut; } + ///< Returns true if the last lock attempt this key was used with failed due + ///< to a timeout. + }; + class cIoThrottle { private: static cMutex mutex; diff --git a/timers.c b/timers.c index 3eb80689..771f66b2 100644 --- a/timers.c +++ b/timers.c @@ -4,18 +4,18 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: timers.c 3.1 2013/12/28 11:33:08 kls Exp $ + * $Id: timers.c 4.5 2015/09/13 13:10:24 kls Exp $ */ #include "timers.h" #include -#include "channels.h" #include "device.h" #include "i18n.h" #include "libsi/si.h" #include "recording.h" #include "remote.h" #include "status.h" +#include "svdrp.h" // IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d' // format characters in order to allow any number of blanks after a numeric @@ -23,19 +23,22 @@ // --- cTimer ---------------------------------------------------------------- -cTimer::cTimer(bool Instant, bool Pause, cChannel *Channel) +cTimer::cTimer(bool Instant, bool Pause, const cChannel *Channel) { + id = 0; startTime = stopTime = 0; - lastSetEvent = 0; + scheduleState = -1; deferred = 0; - recording = pending = inVpsMargin = false; + pending = inVpsMargin = false; flags = tfNone; *file = 0; aux = NULL; + remote = NULL; event = NULL; if (Instant) SetFlags(tfActive | tfInstant); - channel = Channel ? Channel : Channels.GetByNumber(cDevice::CurrentChannel()); + LOCK_CHANNELS_READ; + channel = Channel ? Channel : Channels->GetByNumber(cDevice::CurrentChannel()); time_t t = time(NULL); struct tm tm_r; struct tm *now = localtime_r(&t, &tm_r); @@ -44,27 +47,25 @@ cTimer::cTimer(bool Instant, bool Pause, cChannel *Channel) start = now->tm_hour * 100 + now->tm_min; stop = 0; if (!Setup.InstantRecordTime && channel && (Instant || Pause)) { - cSchedulesLock SchedulesLock; - if (const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock)) { - if (const cSchedule *Schedule = Schedules->GetSchedule(channel)) { - if (const cEvent *Event = Schedule->GetPresentEvent()) { - time_t tstart = Event->StartTime(); - time_t tstop = Event->EndTime(); - if (Event->Vps() && Setup.UseVps) { - SetFlags(tfVps); - tstart = Event->Vps(); - } - else { - tstop += Setup.MarginStop * 60; - tstart -= Setup.MarginStart * 60; - } - day = SetTime(tstart, 0); - struct tm *time = localtime_r(&tstart, &tm_r); - start = time->tm_hour * 100 + time->tm_min; - time = localtime_r(&tstop, &tm_r); - stop = time->tm_hour * 100 + time->tm_min; - SetEvent(Event); + LOCK_SCHEDULES_READ; + if (const cSchedule *Schedule = Schedules->GetSchedule(channel)) { + if (const cEvent *Event = Schedule->GetPresentEvent()) { + time_t tstart = Event->StartTime(); + time_t tstop = Event->EndTime(); + if (Event->Vps() && Setup.UseVps) { + SetFlags(tfVps); + tstart = Event->Vps(); } + else { + tstop += Setup.MarginStop * 60; + tstart -= Setup.MarginStart * 60; + } + day = SetTime(tstart, 0); + struct tm *time = localtime_r(&tstart, &tm_r); + start = time->tm_hour * 100 + time->tm_min; + time = localtime_r(&tstop, &tm_r); + stop = time->tm_hour * 100 + time->tm_min; + SetEvent(Event); } } } @@ -82,17 +83,20 @@ cTimer::cTimer(bool Instant, bool Pause, cChannel *Channel) cTimer::cTimer(const cEvent *Event) { + id = 0; startTime = stopTime = 0; - lastSetEvent = 0; + scheduleState = -1; deferred = 0; - recording = pending = inVpsMargin = false; + pending = inVpsMargin = false; flags = tfActive; *file = 0; aux = NULL; + remote = NULL; event = NULL; if (Event->Vps() && Setup.UseVps) SetFlags(tfVps); - channel = Channels.GetByChannelID(Event->ChannelID(), true); + LOCK_CHANNELS_READ; + channel = Channels->GetByChannelID(Event->ChannelID(), true); time_t tstart = (flags & tfVps) ? Event->Vps() : Event->StartTime(); time_t tstop = tstart + Event->Duration(); if (!(HasFlags(tfVps))) { @@ -120,6 +124,7 @@ cTimer::cTimer(const cTimer &Timer) { channel = NULL; aux = NULL; + remote = NULL; event = NULL; flags = tfNone; *this = Timer; @@ -127,21 +132,23 @@ cTimer::cTimer(const cTimer &Timer) cTimer::~cTimer() { + if (event) + event->DecNumTimers(); free(aux); + free(remote); } cTimer& cTimer::operator= (const cTimer &Timer) { if (&Timer != this) { - uint OldFlags = flags & tfRecording; + id = Timer.id; startTime = Timer.startTime; stopTime = Timer.stopTime; - lastSetEvent = 0; - deferred = 0; - recording = Timer.recording; + scheduleState = -1; + deferred = 0; pending = Timer.pending; inVpsMargin = Timer.inVpsMargin; - flags = Timer.flags | OldFlags; + flags = Timer.flags; channel = Timer.channel; day = Timer.day; weekdays = Timer.weekdays; @@ -152,7 +159,13 @@ cTimer& cTimer::operator= (const cTimer &Timer) strncpy(file, Timer.file, sizeof(file)); free(aux); aux = Timer.aux ? strdup(Timer.aux) : NULL; - event = NULL; + free(remote); + remote = Timer.remote ? strdup(Timer.remote) : NULL; + if (event) + event->DecNumTimers(); + event = Timer.event; + if (event) + event->IncNumTimers(); } return *this; } @@ -171,14 +184,14 @@ int cTimer::Compare(const cListObject &ListObject) const cString cTimer::ToText(bool UseChannelID) const { strreplace(file, ':', '|'); - cString buffer = cString::sprintf("%u:%s:%s:%04d:%04d:%d:%d:%s:%s\n", flags, UseChannelID ? *Channel()->GetChannelID().ToString() : *itoa(Channel()->Number()), *PrintDay(day, weekdays, true), start, stop, priority, lifetime, file, aux ? aux : ""); + cString buffer = cString::sprintf("%u:%s:%s:%04d:%04d:%d:%d:%s:%s", flags, UseChannelID ? *Channel()->GetChannelID().ToString() : *itoa(Channel()->Number()), *PrintDay(day, weekdays, true), start, stop, priority, lifetime, file, aux ? aux : ""); strreplace(file, '|', ':'); return buffer; } cString cTimer::ToDescr(void) const { - return cString::sprintf("%d (%d %04d-%04d %s'%s')", Index() + 1, Channel()->Number(), start, stop, HasFlags(tfVps) ? "VPS " : "", file); + return cString::sprintf("%d%s%s (%d %04d-%04d %s'%s')", Id(), remote ? "@" : "", remote ? remote : "", Channel()->Number(), start, stop, HasFlags(tfVps) ? "VPS " : "", file); } int cTimer::TimeToInt(int t) @@ -313,7 +326,6 @@ bool cTimer::Parse(const char *s) } bool result = false; if (8 <= sscanf(s, "%u :%m[^:]:%m[^:]:%d :%d :%d :%d :%m[^:\n]:%m[^\n]", &flags, &channelbuffer, &daybuffer, &start, &stop, &priority, &lifetime, &filebuffer, &aux)) { - ClrFlags(tfRecording); if (aux && !*skipspace(aux)) { free(aux); aux = NULL; @@ -322,10 +334,11 @@ bool cTimer::Parse(const char *s) result = ParseDay(daybuffer, day, weekdays); Utf8Strn0Cpy(file, filebuffer, sizeof(file)); strreplace(file, '|', ':'); + LOCK_CHANNELS_READ; if (isnumber(channelbuffer)) - channel = Channels.GetByNumber(atoi(channelbuffer)); + channel = Channels->GetByNumber(atoi(channelbuffer)); else - channel = Channels.GetByChannelID(tChannelID::FromString(channelbuffer), true, true); + channel = Channels->GetByChannelID(tChannelID::FromString(channelbuffer), true, true); if (!channel) { esyslog("ERROR: channel %s not defined", channelbuffer); result = false; @@ -340,7 +353,9 @@ bool cTimer::Parse(const char *s) bool cTimer::Save(FILE *f) { - return fprintf(f, "%s", *ToText(true)) > 0; + if (!Remote()) + return fprintf(f, "%s\n", *ToText(true)) > 0; + return true; } bool cTimer::IsSingleEvent(void) const @@ -441,10 +456,8 @@ bool cTimer::Matches(time_t t, bool Directly, int Margin) const startTime = event->StartTime(); stopTime = event->EndTime(); if (!Margin) { // this is an actual check - if (event->Schedule()->PresentSeenWithin(EITPRESENTFOLLOWINGRATE)) { // VPS control can only work with up-to-date events... - if (event->StartTime() > 0) // checks for "phased out" events - return event->IsRunning(true); - } + if (event->Schedule()->PresentSeenWithin(EITPRESENTFOLLOWINGRATE)) // VPS control can only work with up-to-date events... + return event->IsRunning(true); return startTime <= t && t < stopTime; // ...otherwise we fall back to normal timer handling } } @@ -511,27 +524,18 @@ time_t cTimer::StopTime(void) const #define EPGLIMITBEFORE (1 * 3600) // Time in seconds before a timer's start time and #define EPGLIMITAFTER (1 * 3600) // after its stop time within which EPG events will be taken into consideration. -void cTimer::SetEventFromSchedule(const cSchedules *Schedules) +void cTimer::SetId(int Id) +{ + id = Id; +} + +bool cTimer::SetEventFromSchedule(const cSchedules *Schedules) { - cSchedulesLock SchedulesLock; - if (!Schedules) { - lastSetEvent = 0; // forces setting the event, even if the schedule hasn't been modified - if (!(Schedules = cSchedules::Schedules(SchedulesLock))) - return; - } const cSchedule *Schedule = Schedules->GetSchedule(Channel()); if (Schedule && Schedule->Events()->First()) { - time_t now = time(NULL); - if (!lastSetEvent || Schedule->Modified() >= lastSetEvent) { - lastSetEvent = now; + if (Schedule->Modified(scheduleState)) { const cEvent *Event = NULL; if (HasFlags(tfVps) && Schedule->Events()->First()->Vps()) { - if (event && event->StartTime() > 0) { // checks for "phased out" events - if (Recording()) - return; // let the recording end first - if (now <= event->EndTime() || Matches(0, true)) - return; // stay with the old event until the timer has completely expired - } // VPS timers only match if their start time exactly matches the event's VPS time: for (const cEvent *e = Schedule->Events()->First(); e; e = Schedule->Events()->Next(e)) { if (e->StartTime() && e->RunningStatus() != SI::RunningStatusNotRunning) { // skip outdated events @@ -566,30 +570,39 @@ void cTimer::SetEventFromSchedule(const cSchedules *Schedules) } } } - SetEvent(Event); + return SetEvent(Event); } } + return false; } -void cTimer::SetEvent(const cEvent *Event) +bool cTimer::SetEvent(const cEvent *Event) { - if (event != Event) { //XXX TODO check event data, too??? - if (Event) + if (event != Event) { + if (event) + event->DecNumTimers(); + if (Event) { isyslog("timer %s set to event %s", *ToDescr(), *Event->ToDescr()); - else + Event->IncNumTimers(); + Event->Schedule()->Modified(scheduleState); // to get the current state + } + else { isyslog("timer %s set to no event", *ToDescr()); + scheduleState = -1; + } event = Event; + return true; } + return false; } void cTimer::SetRecording(bool Recording) { - recording = Recording; - if (recording) + if (Recording) SetFlags(tfRecording); else ClrFlags(tfRecording); - isyslog("timer %s %s", *ToDescr(), recording ? "start" : "stop"); + isyslog("timer %s %s", *ToDescr(), Recording ? "start" : "stop"); } void cTimer::SetPending(bool Pending) @@ -637,7 +650,13 @@ void cTimer::SetLifetime(int Lifetime) void cTimer::SetAux(const char *Aux) { free(aux); - aux = strdup(Aux); + aux = Aux ? strdup(Aux) : NULL; +} + +void cTimer::SetRemote(const char *Remote) +{ + free(remote); + remote = Remote ? strdup(Remote) : NULL; } void cTimer::SetDeferred(int Seconds) @@ -691,20 +710,49 @@ void cTimer::OnOff(void) // --- cTimers --------------------------------------------------------------- -cTimers Timers; +cTimers cTimers::timers; +int cTimers::lastTimerId = 0; cTimers::cTimers(void) +:cConfig("Timers") { - state = 0; - beingEdited = 0;; - lastSetEvents = 0; lastDeleteExpired = 0; } +bool cTimers::Load(const char *FileName) +{ + LOCK_TIMERS_WRITE; + Timers->SetExplicitModify(); + if (timers.cConfig::Load(FileName)) { + for (cTimer *ti = timers.First(); ti; ti = timers.Next(ti)) { + ti->SetId(NewTimerId()); + ti->ClrFlags(tfRecording); + Timers->SetModified(); + } + return true; + } + return false; +} + +int cTimers::NewTimerId(void) +{ + return ++lastTimerId; // no need for locking, the caller must have a lock on the global Timers list +} + +const cTimer *cTimers::GetById(int Id) const +{ + for (const cTimer *ti = First(); ti; ti = Next(ti)) { + if (!ti->Remote() && ti->Id() == Id) + return ti; + } + return NULL; +} + cTimer *cTimers::GetTimer(cTimer *Timer) { for (cTimer *ti = First(); ti; ti = Next(ti)) { - if (ti->Channel() == Timer->Channel() && + if (!ti->Remote() && + ti->Channel() == Timer->Channel() && (ti->WeekDays() && ti->WeekDays() == Timer->WeekDays() || !ti->WeekDays() && ti->Day() == Timer->Day()) && ti->Start() == Timer->Start() && ti->Stop() == Timer->Stop()) @@ -713,12 +761,12 @@ cTimer *cTimers::GetTimer(cTimer *Timer) return NULL; } -cTimer *cTimers::GetMatch(time_t t) +const cTimer *cTimers::GetMatch(time_t t) const { static int LastPending = -1; - cTimer *t0 = NULL; - for (cTimer *ti = First(); ti; ti = Next(ti)) { - if (!ti->Recording() && ti->Matches(t)) { + const cTimer *t0 = NULL; + for (const cTimer *ti = First(); ti; ti = Next(ti)) { + if (!ti->Remote() && !ti->Recording() && ti->Matches(t)) { if (ti->Pending()) { if (ti->Index() > LastPending) { LastPending = ti->Index(); @@ -736,11 +784,11 @@ cTimer *cTimers::GetMatch(time_t t) return t0; } -cTimer *cTimers::GetMatch(const cEvent *Event, eTimerMatch *Match) +const cTimer *cTimers::GetMatch(const cEvent *Event, eTimerMatch *Match) const { - cTimer *t = NULL; + const cTimer *t = NULL; eTimerMatch m = tmNone; - for (cTimer *ti = First(); ti; ti = Next(ti)) { + for (const cTimer *ti = First(); ti; ti = Next(ti)) { eTimerMatch tm = ti->Matches(Event); if (tm > m) { t = ti; @@ -754,25 +802,33 @@ cTimer *cTimers::GetMatch(const cEvent *Event, eTimerMatch *Match) return t; } -cTimer *cTimers::GetNextActiveTimer(void) +const cTimer *cTimers::GetNextActiveTimer(void) const { - cTimer *t0 = NULL; - for (cTimer *ti = First(); ti; ti = Next(ti)) { - ti->Matches(); - if ((ti->HasFlags(tfActive)) && (!t0 || ti->StopTime() > time(NULL) && ti->Compare(*t0) < 0)) - t0 = ti; + const cTimer *t0 = NULL; + for (const cTimer *ti = First(); ti; ti = Next(ti)) { + if (!ti->Remote()) { + ti->Matches(); + if ((ti->HasFlags(tfActive)) && (!t0 || ti->StopTime() > time(NULL) && ti->Compare(*t0) < 0)) + t0 = ti; + } } return t0; } -void cTimers::SetModified(void) +const cTimers *cTimers::GetTimersRead(cStateKey &StateKey, int TimeoutMs) { - cStatus::MsgTimerChange(NULL, tcMod); - state++; + return timers.Lock(StateKey, false, TimeoutMs) ? &timers : NULL; +} + +cTimers *cTimers::GetTimersWrite(cStateKey &StateKey, int TimeoutMs) +{ + return timers.Lock(StateKey, true, TimeoutMs) ? &timers : NULL; } void cTimers::Add(cTimer *Timer, cTimer *After) { + if (!Timer->Remote()) + Timer->SetId(NewTimerId()); cConfig::Add(Timer, After); cStatus::MsgTimerChange(Timer, tcAdd); } @@ -789,46 +845,114 @@ void cTimers::Del(cTimer *Timer, bool DeleteObject) cConfig::Del(Timer, DeleteObject); } -bool cTimers::Modified(int &State) +const cTimer *cTimers::UsesChannel(const cChannel *Channel) const { - bool Result = state != State; - State = state; - return Result; + for (const cTimer *Timer = First(); Timer; Timer = Next(Timer)) { + if (Timer->Channel() == Channel) + return Timer; + } + return NULL; } -void cTimers::SetEvents(void) +bool cTimers::SetEvents(const cSchedules *Schedules) { - if (time(NULL) - lastSetEvents < 5) - return; - cSchedulesLock SchedulesLock(false, 100); - const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock); - if (Schedules) { - if (!lastSetEvents || Schedules->Modified() >= lastSetEvents) { - for (cTimer *ti = First(); ti; ti = Next(ti)) { - if (cRemote::HasKeys()) - return; // react immediately on user input - ti->SetEventFromSchedule(Schedules); - } - } - } - lastSetEvents = time(NULL); + bool TimersModified = false; + for (cTimer *ti = First(); ti; ti = Next(ti)) + TimersModified |= ti->SetEventFromSchedule(Schedules); + return TimersModified; } -void cTimers::DeleteExpired(void) +bool cTimers::DeleteExpired(void) { if (time(NULL) - lastDeleteExpired < 30) - return; + return false; + bool TimersModified = false; cTimer *ti = First(); while (ti) { cTimer *next = Next(ti); - if (ti->Expired()) { + if (!ti->Remote() && ti->Expired()) { isyslog("deleting timer %s", *ti->ToDescr()); Del(ti); - SetModified(); + TimersModified = true; } ti = next; } lastDeleteExpired = time(NULL); + return TimersModified; +} + +bool cTimers::GetRemoteTimers(const char *ServerName) +{ + bool Result = false; + if (ServerName) { + Result = DelRemoteTimers(ServerName); + cStringList Response; + if (ExecSVDRPCommand(ServerName, "LSTT ID", &Response)) { + for (int i = 0; i < Response.Size(); i++) { + const char *s = Response[i]; + int Code = SVDRPCode(s); + if (Code == 250) { + if (const char *v = SVDRPValue(s)) { + int Id = atoi(v); + while (*v && *v != ' ') + v++; // skip id + cTimer *Timer = new cTimer; + if (Timer->Parse(v)) { + Timer->SetRemote(ServerName); + Timer->SetId(Id); + Add(Timer); + Result = true; + } + else { + esyslog("ERROR: %s: error in timer settings: %s", ServerName, v); + delete Timer; + } + } + } + else if (Code != 550) + esyslog("ERROR: %s: %s", ServerName, s); + } + return Result; + } + } + else { + cStringList ServerNames; + if (GetSVDRPServerNames(&ServerNames, sffTimers)) { + for (int i = 0; i < ServerNames.Size(); i++) + Result |= GetRemoteTimers(ServerNames[i]); + } + } + return Result; +} + +bool cTimers::DelRemoteTimers(const char *ServerName) +{ + bool Deleted = false; + cTimer *Timer = First(); + while (Timer) { + cTimer *t = Next(Timer); + if (Timer->Remote() && (!ServerName || strcmp(Timer->Remote(), ServerName) == 0)) { + Del(Timer); + Deleted = true; + } + Timer = t; + } + return Deleted; +} + +void cTimers::TriggerRemoteTimerPoll(const char *ServerName) +{ + if (ServerName) { + if (!ExecSVDRPCommand(ServerName, cString::sprintf("POLL %s TIMERS", Setup.SVDRPHostName))) + esyslog("ERROR: can't send 'POLL %s TIMERS' to '%s'", Setup.SVDRPHostName, ServerName); + } + else { + cStringList ServerNames; + if (GetSVDRPServerNames(&ServerNames)) { + for (int i = 0; i < ServerNames.Size(); i++) + TriggerRemoteTimerPoll(ServerNames[i]); + } + } } // --- cSortedTimers --------------------------------------------------------- @@ -838,10 +962,10 @@ static int CompareTimers(const void *a, const void *b) return (*(const cTimer **)a)->Compare(**(const cTimer **)b); } -cSortedTimers::cSortedTimers(void) -:cVector(Timers.Count()) +cSortedTimers::cSortedTimers(const cTimers *Timers) +:cVector(Timers->Count()) { - for (const cTimer *Timer = Timers.First(); Timer; Timer = Timers.Next(Timer)) + for (const cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) Append(Timer); Sort(CompareTimers); } diff --git a/timers.h b/timers.h index 4ce1cff1..4222c104 100644 --- a/timers.h +++ b/timers.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: timers.h 3.0 2013/03/11 10:35:53 kls Exp $ + * $Id: timers.h 4.3 2015/09/09 10:40:24 kls Exp $ */ #ifndef __TIMERS_H @@ -27,12 +27,13 @@ enum eTimerMatch { tmNone, tmPartial, tmFull }; class cTimer : public cListObject { friend class cMenuEditTimer; private: + int id; mutable time_t startTime, stopTime; - time_t lastSetEvent; + int scheduleState; mutable time_t deferred; ///< Matches(time_t, ...) will return false if the current time is before this value - bool recording, pending, inVpsMargin; + bool pending, inVpsMargin; uint flags; - cChannel *channel; + const 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) int start; @@ -41,15 +42,17 @@ private: int lifetime; mutable char file[NAME_MAX * 2 + 1]; // *2 to be able to hold 'title' and 'episode', which can each be up to 255 characters long char *aux; + char *remote; const cEvent *event; public: - cTimer(bool Instant = false, bool Pause = false, cChannel *Channel = NULL); + cTimer(bool Instant = false, bool Pause = false, const cChannel *Channel = NULL); cTimer(const cEvent *Event); cTimer(const cTimer &Timer); virtual ~cTimer(); cTimer& operator= (const cTimer &Timer); virtual int Compare(const cListObject &ListObject) const; - bool Recording(void) const { return recording; } + int Id(void) const { return id; } + bool Recording(void) const { return HasFlags(tfRecording); } bool Pending(void) const { return pending; } bool InVpsMargin(void) const { return inVpsMargin; } uint Flags(void) const { return flags; } @@ -63,6 +66,8 @@ public: const char *File(void) const { return file; } time_t FirstDay(void) const { return weekdays ? day : 0; } const char *Aux(void) const { return aux; } + const char *Remote(void) const { return remote; } + bool Local(void) const { return !remote; } // convenience time_t Deferred(void) const { return deferred; } cString ToText(bool UseChannelID = false) const; cString ToDescr(void) const; @@ -81,8 +86,9 @@ public: bool Expired(void) const; time_t StartTime(void) const; time_t StopTime(void) const; - void SetEventFromSchedule(const cSchedules *Schedules = NULL); - void SetEvent(const cEvent *Event); + void SetId(int Id); + bool SetEventFromSchedule(const cSchedules *Schedules); + bool SetEvent(const cEvent *Event); void SetRecording(bool Recording); void SetPending(bool Pending); void SetInVpsMargin(bool InVpsMargin); @@ -93,6 +99,7 @@ public: void SetPriority(int Priority); void SetLifetime(int Lifetime); void SetAux(const char *Aux); + void SetRemote(const char *Remote); void SetDeferred(int Seconds); void SetFlags(uint Flags); void ClrFlags(uint Flags); @@ -108,36 +115,104 @@ public: class cTimers : public cConfig { private: - int state; - int beingEdited; - time_t lastSetEvents; + static cTimers timers; + static int lastTimerId; time_t lastDeleteExpired; public: cTimers(void); + static const cTimers *GetTimersRead(cStateKey &StateKey, int TimeoutMs = 0); + ///< Gets the list of timers for read access. If TimeoutMs is given, + ///< it will wait that long to get a write lock before giving up. + ///< Otherwise it will wait indefinitely. If no read lock can be + ///< obtained within the given timeout, NULL will be returned. + ///< The list is locked and a pointer to it is returned if the state + ///< of the list is different than the state of the given StateKey. + ///< If both states are equal, the list of timers has not been modified + ///< since the last call with the same StateKey, and NULL will be + ///< returned (and the list is not locked). After the returned list of + ///< timers is no longer needed, the StateKey's Remove() function must + ///< be called to release the list. The time between calling + ///< cTimers::GetTimersRead() and StateKey.Remove() should be as short + ///< as possible. After calling StateKey.Remove() the list returned from + ///< this call must not be accessed any more. If you need to access the + ///< timers again later, a new call to GetTimersRead() must be made. + ///< A typical code sequence would look like this: + ///< cStateKey StateKey; + ///< if (const cTimers *Timers = cTimers::GetTimersRead(StateKey)) { + ///< // access the timers + ///< StateKey.Remove(); + ///< } + static cTimers *GetTimersWrite(cStateKey &StateKey, int TimeoutMs = 0); + ///< Gets the list of timers for write access. If TimeoutMs is given, + ///< it will wait that long to get a write lock before giving up. + ///< Otherwise it will wait indefinitely. If no write lock can be + ///< obtained within the given timeout, NULL will be returned. + ///< If a write lock can be obtained, the list of timers will be + ///< returned, regardless of the state values of the timers or the + ///< given StateKey. After the returned list of timers is no longer + ///< needed, the StateKey's Remove() function must be called to release + ///< the list. The time between calling cTimers::GetTimersWrite() and + ///< StateKey.Remove() should be as short as possible. After calling + ///< StateKey.Remove() the list returned from this call must not be + ///< accessed any more. If you need to access the timers again later, + ///< a new call to GetTimersWrite() must be made. The call + ///< to StateKey.Remove() will increment the state of the list of + ///< timers and will copy the new state value to the StateKey. You can + ///< suppress this by using 'false' as the parameter to the call, in + ///< which case the state values are left untouched. + ///< A typical code sequence would look like this: + ///< cStateKey StateKey; + ///< if (cTimers *Timers = cTimers::GetTimersWrite(StateKey)) { + ///< // access the timers + ///< StateKey.Remove(); + ///< } + static bool Load(const char *FileName); + static int NewTimerId(void); + const cTimer *GetById(int Id) const; + cTimer *GetById(int Id) { return const_cast(static_cast(this)->GetById(Id)); }; cTimer *GetTimer(cTimer *Timer); - cTimer *GetMatch(time_t t); - cTimer *GetMatch(const cEvent *Event, eTimerMatch *Match = NULL); - cTimer *GetNextActiveTimer(void); - int BeingEdited(void) { return beingEdited; } - void IncBeingEdited(void) { beingEdited++; } - void DecBeingEdited(void) { if (!--beingEdited) lastSetEvents = 0; } - void SetModified(void); - bool Modified(int &State); - ///< Returns true if any of the timers have been modified, which - ///< is detected by State being different than the internal state. - ///< Upon return the internal state will be stored in State. - void SetEvents(void); - void DeleteExpired(void); + const cTimer *GetMatch(time_t t) const; + cTimer *GetMatch(time_t t) { return const_cast(static_cast(this)->GetMatch(t)); }; + const cTimer *GetMatch(const cEvent *Event, eTimerMatch *Match = NULL) const; + cTimer *GetMatch(const cEvent *Event, eTimerMatch *Match = NULL) { return const_cast(static_cast(this)->GetMatch(Event, Match)); } + const cTimer *GetNextActiveTimer(void) const; + const cTimer *UsesChannel(const cChannel *Channel) const; + bool SetEvents(const cSchedules *Schedules); + bool DeleteExpired(void); void Add(cTimer *Timer, cTimer *After = NULL); void Ins(cTimer *Timer, cTimer *Before = NULL); void Del(cTimer *Timer, bool DeleteObject = true); + bool GetRemoteTimers(const char *ServerName = NULL); + ///< Gets the timers from the given remote machine and adds them to this + ///< list. If no ServerName is given, all timers from all known remote + ///< machines will be fetched. This function calls DelRemoteTimers() with + ///< the given ServerName first. + ///< Returns true if any remote timers have been added or deleted + bool DelRemoteTimers(const char *ServerName = NULL); + ///< Deletes all timers of the given remote machine from this list (leaves + ///< them untouched on the remote machine). If no ServerName is given, the + ///< timers of all remote machines will be deleted from the list. + ///< Returns true if any remote timers have been deleted. + void TriggerRemoteTimerPoll(const char *ServerName = NULL); + ///< Sends an SVDRP POLL command to the given remote machine. + ///< If no ServerName is given, the POLL command will be sent to all + ///< known remote machines. }; -extern cTimers Timers; +// Provide lock controlled access to the list: + +DEF_LIST_LOCK(Timers); + +// These macros provide a convenient way of locking the global timers list +// and making sure the lock is released as soon as the current scope is left +// (note that these macros wait forever to obtain the lock!): + +#define LOCK_TIMERS_READ USE_LIST_LOCK_READ(Timers) +#define LOCK_TIMERS_WRITE USE_LIST_LOCK_WRITE(Timers) class cSortedTimers : public cVector { public: - cSortedTimers(void); + cSortedTimers(const cTimers *Timers); }; #endif //__TIMERS_H diff --git a/tools.c b/tools.c index a83965ae..adfff1c3 100644 --- a/tools.c +++ b/tools.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: tools.c 3.4 2015/02/07 15:09:17 kls Exp $ + * $Id: tools.c 4.4 2015/09/10 13:17:55 kls Exp $ */ #include "tools.h" @@ -274,6 +274,28 @@ cString strescape(const char *s, const char *chars) return cString(s, t != NULL); } +cString strgetval(const char *s, const char *name, char d) +{ + if (s && name) { + int l = strlen(name); + const char *t = s; + while (const char *p = strstr(t, name)) { + t = skipspace(p + l); + if (p == s || *(p - 1) <= ' ') { + if (*t == d) { + t = skipspace(t + 1); + const char *v = t; + while (*t > ' ') + t++; + return cString(v, t); + break; + } + } + } + } + return NULL; +} + bool startswith(const char *s, const char *p) { while (*p) { @@ -1061,6 +1083,17 @@ cString &cString::operator=(const char *String) return *this; } +cString &cString::Append(const char *String) +{ + int l1 = strlen(s); + int l2 = strlen(String); + char *p = (char *)realloc(s, l1 + l2 + 1); + if (p != s) + strcpy(p, s); + strcpy(p + l1, String); + return *this; +} + cString &cString::Truncate(int Index) { int l = strlen(s); @@ -1289,6 +1322,20 @@ uchar *RgbToJpeg(uchar *Mem, int Width, int Height, int &Size, int Quality) return jcd.mem; } +// --- GetHostName ----------------------------------------------------------- + +const char *GetHostName(void) +{ + static char buffer[HOST_NAME_MAX] = ""; + if (!*buffer) { + if (gethostname(buffer, sizeof(buffer)) < 0) { + LOG_ERROR; + strcpy(buffer, "vdr"); + } + } + return buffer; +} + // --- cBase64Encoder -------------------------------------------------------- const char *cBase64Encoder::b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; @@ -1440,6 +1487,19 @@ bool cPoller::Add(int FileHandle, bool Out) return false; } +void cPoller::Del(int FileHandle, bool Out) +{ + if (FileHandle >= 0) { + for (int i = 0; i < numFileHandles; i++) { + if (pfd[i].fd == FileHandle && pfd[i].events == (Out ? POLLOUT : POLLIN)) { + if (i < numFileHandles - 1) + memmove(&pfd[i], &pfd[i + 1], (numFileHandles - i - 1) * sizeof(pollfd)); + numFileHandles--; + } + } + } +} + bool cPoller::Poll(int TimeoutMs) { if (numFileHandles) { @@ -1998,12 +2058,58 @@ int cListObject::Index(void) const return i; } +// --- cListGarbageCollector ------------------------------------------------- + +#define LIST_GARBAGE_COLLECTOR_TIMEOUT 5 // seconds + +cListGarbageCollector ListGarbageCollector; + +cListGarbageCollector::cListGarbageCollector(void) +{ + objects = NULL; + lastPut = 0; +} + +cListGarbageCollector::~cListGarbageCollector() +{ + if (objects) + esyslog("ERROR: ListGarbageCollector destroyed without prior Purge()!"); +} + +void cListGarbageCollector::Put(cListObject *Object) +{ + mutex.Lock(); + Object->next = objects; + objects = Object; + lastPut = time(NULL); + mutex.Unlock(); +} + +void cListGarbageCollector::Purge(bool Force) +{ + mutex.Lock(); + if (objects && (time(NULL) - lastPut > LIST_GARBAGE_COLLECTOR_TIMEOUT || Force)) { + // We make sure that any object stays in the garbage collector for at least + // LIST_GARBAGE_COLLECTOR_TIMEOUT seconds, to give objects that have pointers + // to them a chance to drop these references before the object is finally + // deleted. + while (cListObject *Object = objects) { + objects = Object->next; + delete Object; + } + } + mutex.Unlock(); +} + // --- cListBase ------------------------------------------------------------- -cListBase::cListBase(void) +cListBase::cListBase(const char *NeedsLocking) +:stateLock(NeedsLocking) { objects = lastObject = NULL; count = 0; + needsLocking = NeedsLocking; + useGarbageCollector = needsLocking; } cListBase::~cListBase() @@ -2011,6 +2117,15 @@ cListBase::~cListBase() Clear(); } +bool cListBase::Lock(cStateKey &StateKey, bool Write, int TimeoutMs) const +{ + if (needsLocking) + return stateLock.Lock(StateKey, Write, TimeoutMs); + else + esyslog("ERROR: cListBase::Lock() called for a list that doesn't require locking"); + return false; +} + void cListBase::Add(cListObject *Object, cListObject *After) { if (After && After != lastObject) { @@ -2050,8 +2165,12 @@ void cListBase::Del(cListObject *Object, bool DeleteObject) if (Object == lastObject) lastObject = Object->Prev(); Object->Unlink(); - if (DeleteObject) - delete Object; + if (DeleteObject) { + if (useGarbageCollector) + ListGarbageCollector.Put(Object); + else + delete Object; + } count--; } @@ -2095,11 +2214,30 @@ void cListBase::Clear(void) count = 0; } -cListObject *cListBase::Get(int Index) const +bool cListBase::Contains(const cListObject *Object) const +{ + for (const cListObject *o = objects; o; o = o->Next()) { + if (o == Object) + return true; + } + return false; +} + +void cListBase::SetExplicitModify(void) +{ + stateLock.SetExplicitModify(); +} + +void cListBase::SetModified(void) +{ + stateLock.IncState(); +} + +const cListObject *cListBase::Get(int Index) const { if (Index < 0) return NULL; - cListObject *object = objects; + const cListObject *object = objects; while (object && Index-- > 0) object = object->Next(); return object; @@ -2115,7 +2253,9 @@ static int CompareListObjects(const void *a, const void *b) void cListBase::Sort(void) { int n = Count(); - cListObject *a[n]; + cListObject **a = MALLOC(cListObject *, n); + if (a == NULL) + return; cListObject *object = objects; int i = 0; while (object && i < n) { @@ -2129,6 +2269,7 @@ void cListBase::Sort(void) count--; Add(a[i]); } + free(a); } // --- cHashBase ------------------------------------------------------------- diff --git a/tools.h b/tools.h index f068174d..1563db92 100644 --- a/tools.h +++ b/tools.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: tools.h 3.7 2015/02/07 15:12:26 kls Exp $ + * $Id: tools.h 4.3 2015/09/06 10:45:54 kls Exp $ */ #ifndef __TOOLS_H @@ -26,6 +26,7 @@ #include #include #include +#include "thread.h" typedef unsigned char uchar; @@ -178,6 +179,7 @@ public: const char * operator*() const { return s; } // for use in (const void *) context (printf() etc.) cString &operator=(const cString &String); cString &operator=(const char *String); + cString &Append(const char *String); cString &Truncate(int Index); ///< Truncate the string at the given Index (if Index is < 0 it is counted from the end of the string). cString &CompactChars(char c); ///< Compact any sequence of characters 'c' to a single character, and strip all of them from the beginning and end of this string. static cString sprintf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); @@ -209,6 +211,14 @@ char *stripspace(char *s); char *compactspace(char *s); char *compactchars(char *s, char c); ///< removes all occurrences of 'c' from the beginning an end of 's' and replaces sequences of multiple 'c's with a single 'c'. cString strescape(const char *s, const char *chars); +cString strgetval(const char *s, const char *name, char d = '='); + ///< Returns the value part of a 'name=value' pair in s. + ///< name must either be at the beginning of s, or has to be preceded by white space. + ///< There may be any number of white space around the '=' sign. The value is + ///< everyting up to (and excluding) the next white space, or the end of s. + ///< If an other delimiter shall be used (like, e.g., ':'), it can be given + ///< as the third parameter. + ///< If name occurs more than once in s, only the first occurrence is taken. bool startswith(const char *s, const char *p); bool endswith(const char *s, const char *p); bool isempty(const char *s); @@ -283,6 +293,8 @@ uchar *RgbToJpeg(uchar *Mem, int Width, int Height, int &Size, int Quality = 100 ///< 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. +const char *GetHostName(void); + ///< Gets the host name of this machine. class cBase64Encoder { private: @@ -356,12 +368,13 @@ public: class cPoller { private: - enum { MaxPollFiles = 16 }; + enum { MaxPollFiles = 64 }; pollfd pfd[MaxPollFiles]; int numFileHandles; public: cPoller(int FileHandle = -1, bool Out = false); bool Add(int FileHandle, bool Out); + void Del(int FileHandle, bool Out); bool Poll(int TimeoutMs = 0); }; @@ -452,6 +465,7 @@ public: }; class cListObject { + friend class cListGarbageCollector; private: cListObject *prev, *next; public: @@ -468,33 +482,152 @@ public: cListObject *Next(void) const { return next; } }; +class cListGarbageCollector { +private: + cMutex mutex; + cListObject *objects; + time_t lastPut; +public: + cListGarbageCollector(void); + ~cListGarbageCollector(); + void Put(cListObject *Object); + void Purge(bool Force = false); + }; + +extern cListGarbageCollector ListGarbageCollector; + class cListBase { protected: cListObject *objects, *lastObject; - cListBase(void); int count; + mutable cStateLock stateLock; + const char *needsLocking; + bool useGarbageCollector; + cListBase(const char *NeedsLocking = NULL); public: virtual ~cListBase(); + bool Lock(cStateKey &StateKey, bool Write = false, int TimeoutMs = 0) const; + ///< Tries to get a lock on this list and returns true if successful. + ///< By default a read lock is requested. Set Write to true to obtain + ///< a write lock. If TimeoutMs is not zero, it waits for the given + ///< number of milliseconds before giving up. + ///< If you need to lock more than one list at the same time, make sure + ///< you set TimeoutMs to a suitable value in all of the calls to + ///< Lock(), and be prepared to handle situations where you do not get all + ///< of the requested locks. In such cases you should release all the locks + ///< you have obtained so far and try again. StateKey.TimedOut() tells you + ///< whether the lock attempt failed due to a timeout or because the state + ///< of the lock hasn't changed since the previous locking attempt. + ///< To implicitly avoid deadlocks when locking more than one of the global + ///< lists of VDR at the same time, make sure you always lock Timers, Channels, + ///< Recordings and Schedules in this sequence. + ///< You may keep pointers to objects in this list, even after releasing + ///< the lock. However, you may only access such objects if you are + ///< holding a proper lock again. If an object has been deleted from the list + ///< while you did not hold a lock (for instance by an other thread), the + ///< object will still be there, but no longer within this list (it is then + ///< stored in the ListGarbageCollector). That way even if you access the object + ///< after it has been deleted, you won't cause a segfault. You can call the + ///< Contains() function to check whether an object you are holding a pointer + ///< to is still in the list. Note that the garbage collector is purged when + ///< the usual housekeeping is done. + void SetUseGarbageCollector(void) { useGarbageCollector = true; } + void SetExplicitModify(void); + ///< If you have obtained a write lock on this list, and you don't want it to + ///< be automatically marked as modified when the lock is released, a call to + ///< this function will disable this, and you can explicitly call SetModified() + ///< to have the list marked as modified. + void SetModified(void); + ///< Unconditionally marks this list as modified. void Add(cListObject *Object, cListObject *After = NULL); void Ins(cListObject *Object, cListObject *Before = NULL); void Del(cListObject *Object, bool DeleteObject = true); virtual void Move(int From, int To); void Move(cListObject *From, cListObject *To); virtual void Clear(void); - cListObject *Get(int Index) const; + bool Contains(const cListObject *Object) const; + ///< If a pointer to an object contained in this list has been obtained while + ///< holding a lock, and that lock has been released, but the pointer is kept for + ///< later use (after obtaining a new lock), Contains() can be called with that + ///< pointer to make sure the object it points to is still part of this list + ///< (it may have been deleted or otherwise removed from the list after the lock + ///< during which the pointer was initially retrieved has been released). + const cListObject *Get(int Index) const; + cListObject *Get(int Index) { return const_cast(static_cast(this)->Get(Index)); } int Count(void) const { return count; } void Sort(void); }; template class cList : public cListBase { public: - T *Get(int Index) const { return (T *)cListBase::Get(Index); } - T *First(void) const { return (T *)objects; } - T *Last(void) const { return (T *)lastObject; } - T *Prev(const T *object) const { return (T *)object->cListObject::Prev(); } // need to call cListObject's members to - T *Next(const T *object) const { return (T *)object->cListObject::Next(); } // avoid ambiguities in case of a "list of lists" + cList(const char *NeedsLocking = NULL): cListBase(NeedsLocking) {} + ///< Sets up a new cList of the given type T. If NeedsLocking is given, the list + ///< and any of its elements may only be accessed if the caller holds a lock + ///< obtained by a call to Lock() (see cListBase::Lock() for details). + ///< NeedsLocking is used as both a boolean flag to enable locking, and as + ///< a name to identify this list in debug output. It must be a static string + ///< and should be no longer than 10 characters. The string will not be copied! + const T *Get(int Index) const { return (T *)cListBase::Get(Index); } + ///< Returns the list element at the given Index, or NULL if no such element + ///< exists. + const T *First(void) const { return (T *)objects; } + ///< Returns the first element in this list, or NULL if the list is empty. + const T *Last(void) const { return (T *)lastObject; } + ///< Returns the last element in this list, or NULL if the list is empty. + const T *Prev(const T *Object) const { return (T *)Object->cListObject::Prev(); } // need to call cListObject's members to + ///< Returns the element immediately before Object in this list, or NULL + ///< if Object is the first element in the list. Object must not be NULL! + const T *Next(const T *Object) const { return (T *)Object->cListObject::Next(); } // avoid ambiguities in case of a "list of lists" + ///< Returns the element immediately following Object in this list, or NULL + ///< if Object is the last element in the list. Object must not be NULL! + T *Get(int Index) { return const_cast(static_cast *>(this)->Get(Index)); } + ///< Non-const version of Get(). + T *First(void) { return const_cast(static_cast *>(this)->First()); } + ///< Non-const version of First(). + T *Last(void) { return const_cast(static_cast *>(this)->Last()); } + ///< Non-const version of Last(). + T *Prev(const T *Object) { return const_cast(static_cast *>(this)->Prev(Object)); } + ///< Non-const version of Prev(). + T *Next(const T *Object) { return const_cast(static_cast *>(this)->Next(Object)); } + ///< Non-const version of Next(). }; +// The DEF_LIST_LOCK macro defines a convenience class that can be used to obtain +// a lock on a cList and make sure the lock is released when the current scope +// is left: + +#define DEF_LIST_LOCK2(Class, Name) \ +class c##Name##Lock { \ +private: \ + cStateKey stateKey; \ + const c##Class *list; \ +public: \ + c##Name##Lock(bool Write = false) \ + { \ + if (Write) \ + list = c##Class::Get##Name##Write(stateKey); \ + else \ + list = c##Class::Get##Name##Read(stateKey); \ + } \ + ~c##Name##Lock() { stateKey.Remove(); } \ + const c##Class *Name(void) const { return list; } \ + c##Class *Name(void) { return const_cast(list); } \ + } +#define DEF_LIST_LOCK(Class) DEF_LIST_LOCK2(Class, Class) + +// The USE_LIST_LOCK macro sets up a local variable of a class defined by +// a suitable DEF_LIST_LOCK, and also a pointer to the provided list: + +#define USE_LIST_LOCK_READ2(Class, Name) \ +c##Name##Lock Name##Lock(false); \ +const c##Class *Name __attribute__((unused)) = Name##Lock.Name(); +#define USE_LIST_LOCK_READ(Class) USE_LIST_LOCK_READ2(Class, Class) + +#define USE_LIST_LOCK_WRITE2(Class, Name) \ +c##Name##Lock Name##Lock(true); \ +c##Class *Name __attribute__((unused)) = Name##Lock.Name(); +#define USE_LIST_LOCK_WRITE(Class) USE_LIST_LOCK_WRITE2(Class, Class) + template class cVector { ///< cVector may only be used for *simple* types, like int or pointers - not for class objects that allocate additional memory! private: diff --git a/transfer.c b/transfer.c index eb07e829..d0e7ed46 100644 --- a/transfer.c +++ b/transfer.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: transfer.c 3.1 2013/08/22 12:33:02 kls Exp $ + * $Id: transfer.c 4.1 2015/09/05 11:43:58 kls Exp $ */ #include "transfer.h" @@ -38,7 +38,7 @@ void cTransfer::Activate(bool On) #define MAXRETRIES 20 // max. number of retries for a single TS packet #define RETRYWAIT 5 // time (in ms) between two retries -void cTransfer::Receive(uchar *Data, int Length) +void cTransfer::Receive(const uchar *Data, int Length) { if (cPlayer::IsAttached()) { // Transfer Mode means "live tv", so there's no point in doing any additional diff --git a/transfer.h b/transfer.h index ce74898f..21d9dd86 100644 --- a/transfer.h +++ b/transfer.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: transfer.h 3.0 2013/03/01 09:49:46 kls Exp $ + * $Id: transfer.h 4.1 2015/09/05 11:43:08 kls Exp $ */ #ifndef __TRANSFER_H @@ -19,7 +19,7 @@ private: cPatPmtGenerator patPmtGenerator; protected: virtual void Activate(bool On); - virtual void Receive(uchar *Data, int Length); + virtual void Receive(const uchar *Data, int Length); public: cTransfer(const cChannel *Channel); virtual ~cTransfer(); diff --git a/vdr.1 b/vdr.1 index 845cfa33..0ce3eed8 100644 --- a/vdr.1 +++ b/vdr.1 @@ -8,7 +8,7 @@ .\" License as specified in the file COPYING that comes with the .\" vdr distribution. .\" -.\" $Id: vdr.1 3.6 2015/02/17 13:43:24 kls Exp $ +.\" $Id: vdr.1 4.2 2015/04/19 12:39:13 kls Exp $ .\" .TH vdr 1 "19 Feb 2015" "2.2" "Video Disk Recorder" .SH NAME @@ -64,6 +64,8 @@ Run in daemon mode (implies \-\-no\-kbd). .BI \-D\ num ,\ \-\-device= num Use only the given DVB device (\fInum\fR = 0, 1, 2...). There may be several \fB\-D\fR options (by default all DVB devices will be used). +If \fB\-D\-\fR is given, no DVB devices will be used at all, independent of any +other \-D options. .TP .BI \-\-dirnames= path [, name [, enc ]] Set the maximum directory path length to \fIpath\fR (default is the maximum value @@ -204,7 +206,7 @@ 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 vdr can switch to a lesser privileged user id during normal -operation. +operation. \fIuser\fR can be a user name or a numerical id. .TP .BI \-\-updindex= rec Update the index file for the given recording. diff --git a/vdr.5 b/vdr.5 index fa233d72..2a7b9072 100644 --- a/vdr.5 +++ b/vdr.5 @@ -8,7 +8,7 @@ .\" License as specified in the file COPYING that comes with the .\" vdr distribution. .\" -.\" $Id: vdr.5 3.4 2015/02/17 13:43:53 kls Exp $ +.\" $Id: vdr.5 4.0 2015/02/17 13:43:53 kls Exp $ .\" .TH vdr 5 "19 Feb 2015" "2.2" "Video Disk Recorder Files" .SH NAME diff --git a/vdr.c b/vdr.c index 71a72f2e..6b0bf2b0 100644 --- a/vdr.c +++ b/vdr.c @@ -22,7 +22,7 @@ * * The project's page is at http://www.tvdr.de * - * $Id: vdr.c 3.16 2015/02/10 14:13:12 kls Exp $ + * $Id: vdr.c 4.7 2015/09/11 08:02:50 kls Exp $ */ #include @@ -65,6 +65,7 @@ #include "sourceparams.h" #include "sources.h" #include "status.h" +#include "svdrp.h" #include "themes.h" #include "timers.h" #include "tools.h" @@ -92,12 +93,12 @@ static int LastSignal = 0; -static bool SetUser(const char *UserName, bool UserDump) +static bool SetUser(const char *User, bool UserDump) { - if (UserName) { - struct passwd *user = getpwnam(UserName); + if (User) { + struct passwd *user = isnumber(User) ? getpwuid(atoi(User)) : getpwnam(User); if (!user) { - fprintf(stderr, "vdr: unknown user: '%s'\n", UserName); + fprintf(stderr, "vdr: unknown user: '%s'\n", User); return false; } if (setgid(user->pw_gid) < 0) { @@ -301,7 +302,11 @@ int main(int argc, char *argv[]) break; case 'd': DaemonMode = true; break; - case 'D': if (isnumber(optarg)) { + case 'D': if (*optarg == '-') { + cDvbDevice::useDvbDevices = false; + break; + } + if (isnumber(optarg)) { int n = atoi(optarg); if (0 <= n && n < MAXDEVICES) { cDevice::SetUseDevice(n); @@ -371,7 +376,7 @@ int main(int argc, char *argv[]) break; case 'g' | 0x100: return GenerateIndex(optarg) ? 0 : 2; - case 'g': cSVDRP::SetGrabImageDir(*optarg != '-' ? optarg : NULL); + case 'g': SetSVDRPGrabImageDir(*optarg != '-' ? optarg : NULL); break; case 'h': DisplayHelp = true; break; @@ -505,7 +510,7 @@ int main(int argc, char *argv[]) if (VdrUser && geteuid() == 0) { StartedAsRoot = true; - if (strcmp(VdrUser, "root")) { + if (strcmp(VdrUser, "root") && strcmp(VdrUser, "0")) { if (!SetKeepCaps(true)) return 2; if (!SetUser(VdrUser, UserDump)) @@ -536,7 +541,9 @@ int main(int argc, char *argv[]) " -d, --daemon run in daemon mode\n" " -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" + " devices will be used); if -D- is given, no DVB\n" + " devices will be used at all, independent of any\n" + " other -D options\n" " --dirnames=PATH[,NAME[,ENC]]\n" " set the maximum directory path length to PATH\n" " (default: %d); if NAME is also given, it defines\n" @@ -586,7 +593,7 @@ int main(int argc, char *argv[]) " (default: %s)\n" " -t TTY, --terminal=TTY controlling tty\n" " -u USER, --user=USER run as user USER; only applicable if started as\n" - " root\n" + " root; USER can be a user name or a numerical id\n" " --updindex=REC update index for recording REC and exit\n" " --userdump allow coredumps if -u is given (debugging)\n" " -v DIR, --video=DIR use DIR as video directory (default: %s)\n" @@ -741,8 +748,8 @@ int main(int argc, char *argv[]) Sources.Load(AddDirectory(ConfigDirectory, "sources.conf"), true, true); Diseqcs.Load(AddDirectory(ConfigDirectory, "diseqc.conf"), true, Setup.DiSEqC); Scrs.Load(AddDirectory(ConfigDirectory, "scr.conf"), true); - Channels.Load(AddDirectory(ConfigDirectory, "channels.conf"), false, true); - Timers.Load(AddDirectory(ConfigDirectory, "timers.conf")); + cChannels::Load(AddDirectory(ConfigDirectory, "channels.conf"), false, true); + cTimers::Load(AddDirectory(ConfigDirectory, "timers.conf")); Commands.Load(AddDirectory(ConfigDirectory, "commands.conf")); RecordingCommands.Load(AddDirectory(ConfigDirectory, "reccmds.conf")); SVDRPhosts.Load(AddDirectory(ConfigDirectory, "svdrphosts.conf"), true); @@ -758,8 +765,7 @@ int main(int argc, char *argv[]) // Recordings: - Recordings.Update(); - DeletedRecordings.Update(); + cRecordings::Update(); // EPG data: @@ -825,7 +831,7 @@ int main(int argc, char *argv[]) // User interface: - Interface = new cInterface(SVDRPport); + Interface = new cInterface; // Default skins: @@ -872,16 +878,20 @@ int main(int argc, char *argv[]) if (!CamSlots.WaitForAllCamSlotsReady(DEVICEREADYTIMEOUT)) dsyslog("not all CAM slots ready after %d seconds", DEVICEREADYTIMEOUT); if (*Setup.InitialChannel) { + LOCK_CHANNELS_READ; if (isnumber(Setup.InitialChannel)) { // for compatibility with old setup.conf files - if (cChannel *Channel = Channels.GetByNumber(atoi(Setup.InitialChannel))) + if (const cChannel *Channel = Channels->GetByNumber(atoi(Setup.InitialChannel))) Setup.InitialChannel = Channel->GetChannelID().ToString(); } - if (cChannel *Channel = Channels.GetByChannelID(tChannelID::FromString(Setup.InitialChannel))) + if (const cChannel *Channel = Channels->GetByChannelID(tChannelID::FromString(Setup.InitialChannel))) Setup.CurrentChannel = Channel->Number(); } if (Setup.InitialVolume >= 0) Setup.CurrentVolume = Setup.InitialVolume; - Channels.SwitchTo(Setup.CurrentChannel); + { + LOCK_CHANNELS_READ; + Channels->SwitchTo(Setup.CurrentChannel); + } if (MuteAudio) cDevice::PrimaryDevice()->ToggleMute(); else @@ -907,6 +917,13 @@ int main(int argc, char *argv[]) sd_notify(0, "READY=1\nSTATUS=Ready"); #endif + // SVDRP: + + SetSVDRPPorts(SVDRPport, DEFAULTSVDRPPORT); + StartSVDRPServerHandler(); + if (Setup.SVDRPPeering) + StartSVDRPClientHandler(); + // Main program loop: #define DELETE_MENU ((IsInfoMenu &= (Menu == NULL)), delete Menu, Menu = NULL) @@ -925,13 +942,14 @@ int main(int argc, char *argv[]) static time_t lastTime = 0; if (!cDevice::PrimaryDevice()->HasProgramme()) { if (!CamMenuActive() && Now - lastTime > MINCHANNELWAIT) { // !CamMenuActive() to avoid interfering with the CAM if a CAM menu is open - cChannel *Channel = Channels.GetByNumber(cDevice::CurrentChannel()); + LOCK_CHANNELS_READ; + const cChannel *Channel = Channels->GetByNumber(cDevice::CurrentChannel()); if (Channel && (Channel->Vpid() || Channel->Apid(0) || Channel->Dpid(0))) { - if (cDevice::GetDeviceForTransponder(Channel, LIVEPRIORITY) && Channels.SwitchTo(Channel->Number())) // try to switch to the original channel... + if (cDevice::GetDeviceForTransponder(Channel, LIVEPRIORITY) && Channels->SwitchTo(Channel->Number())) // try to switch to the original channel... ; else if (LastTimerChannel > 0) { - Channel = Channels.GetByNumber(LastTimerChannel); - if (Channel && cDevice::GetDeviceForTransponder(Channel, LIVEPRIORITY) && Channels.SwitchTo(LastTimerChannel)) // ...or the one used by the last timer + Channel = Channels->GetByNumber(LastTimerChannel); + if (Channel && cDevice::GetDeviceForTransponder(Channel, LIVEPRIORITY) && Channels->SwitchTo(LastTimerChannel)) // ...or the one used by the last timer ; } } @@ -959,40 +977,59 @@ int main(int argc, char *argv[]) } } // Handle channel and timer modifications: - if (!Channels.BeingEdited() && !Timers.BeingEdited()) { - int modified = Channels.Modified(); - static time_t ChannelSaveTimeout = 0; - static int TimerState = 0; - // Channels and timers need to be stored in a consistent manner, - // therefore if one of them is changed, we save both. - if (modified == CHANNELSMOD_USER || Timers.Modified(TimerState)) - ChannelSaveTimeout = 1; // triggers an immediate save - else if (modified && !ChannelSaveTimeout) - ChannelSaveTimeout = Now + CHANNELSAVEDELTA; - bool timeout = ChannelSaveTimeout == 1 || ChannelSaveTimeout && Now > ChannelSaveTimeout && !cRecordControls::Active(); - if ((modified || timeout) && Channels.Lock(false, 100)) { - if (timeout) { - Channels.Save(); - Timers.Save(); - ChannelSaveTimeout = 0; + { + // Channels and timers need to be stored in a consistent manner, + // therefore if one of them is changed, we save both. + static time_t ChannelSaveTimeout = 0; + static cStateKey TimersStateKey(true); + static cStateKey ChannelsStateKey(true); + static int ChannelsModifiedByUser = 0; + const cTimers *Timers = cTimers::GetTimersRead(TimersStateKey); + const cChannels *Channels = cChannels::GetChannelsRead(ChannelsStateKey); + if (ChannelSaveTimeout != 1) { + if (Channels) { + if (Channels->ModifiedByUser(ChannelsModifiedByUser)) + ChannelSaveTimeout = 1; // triggers an immediate save + else if (!ChannelSaveTimeout) + ChannelSaveTimeout = Now + CHANNELSAVEDELTA; + } + if (Timers) + ChannelSaveTimeout = 1; // triggers an immediate save + } + if (ChannelSaveTimeout && Now > ChannelSaveTimeout && !cRecordControls::Active()) + ChannelSaveTimeout = 1; // triggers an immediate save + if (Timers && Channels) { + Channels->Save(); + Timers->Save(); + ChannelSaveTimeout = 0; + } + if (Channels) { + for (const cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) { + if (Channel->Modification(CHANNELMOD_RETUNE)) { + cRecordControls::ChannelDataModified(Channel); + if (Channel->Number() == cDevice::CurrentChannel() && cDevice::PrimaryDevice()->HasDecoder()) { + if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring()) { + if (cDevice::ActualDevice()->ProvidesTransponder(Channel)) { // avoids retune on devices that don't really access the transponder + isyslog("retuning due to modification of channel %d (%s)", Channel->Number(), Channel->Name()); + Channels->SwitchTo(Channel->Number()); + } + } + } + cStatus::MsgChannelChange(Channel); + } } - for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { - if (Channel->Modification(CHANNELMOD_RETUNE)) { - cRecordControls::ChannelDataModified(Channel); - if (Channel->Number() == cDevice::CurrentChannel() && cDevice::PrimaryDevice()->HasDecoder()) { - if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring()) { - if (cDevice::ActualDevice()->ProvidesTransponder(Channel)) { // avoids retune on devices that don't really access the transponder - isyslog("retuning due to modification of channel %d (%s)", Channel->Number(), Channel->Name()); - Channels.SwitchTo(Channel->Number()); - } - } - } - cStatus::MsgChannelChange(Channel); - } - } - Channels.Unlock(); - } - } + } + // State keys are removed in reverse order! + if (Channels) + ChannelsStateKey.Remove(); + if (Timers) + TimersStateKey.Remove(); + if (ChannelSaveTimeout == 1) { + // Only one of them was modified, so we reset the state keys to handle them both in the next turn: + ChannelsStateKey.Reset(); + TimersStateKey.Reset(); + } + } // Channel display: if (!EITScanner.Active() && cDevice::CurrentChannel() != LastChannel) { if (!Menu) @@ -1002,80 +1039,109 @@ int main(int argc, char *argv[]) } if (Now - LastChannelChanged >= Setup.ZapTimeout && LastChannel != PreviousChannel[PreviousChannelIndex]) PreviousChannel[PreviousChannelIndex ^= 1] = LastChannel; - // Timers and Recordings: - if (!Timers.BeingEdited()) { - // Assign events to timers: - Timers.SetEvents(); - // Must do all following calls with the exact same time! - // Process ongoing recordings: - cRecordControls::Process(Now); - // Start new recordings: - cTimer *Timer = Timers.GetMatch(Now); - if (Timer) { - if (!cRecordControls::Start(Timer)) - Timer->SetPending(true); - else - LastTimerChannel = Timer->Channel()->Number(); - } - // Make sure timers "see" their channel early enough: - static time_t LastTimerCheck = 0; - if (Now - LastTimerCheck > TIMERCHECKDELTA) { // don't do this too often - InhibitEpgScan = false; - for (cTimer *Timer = Timers.First(); Timer; Timer = Timers.Next(Timer)) { - bool InVpsMargin = false; - bool NeedsTransponder = false; - if (Timer->HasFlags(tfActive) && !Timer->Recording()) { - if (Timer->HasFlags(tfVps)) { - if (Timer->Matches(Now, true, Setup.VpsMargin)) { - InVpsMargin = true; - Timer->SetInVpsMargin(InVpsMargin); - } - else if (Timer->Event()) { - InVpsMargin = Timer->Event()->StartTime() <= Now && Now < Timer->Event()->EndTime(); - NeedsTransponder = Timer->Event()->StartTime() - Now < VPSLOOKAHEADTIME * 3600 && !Timer->Event()->SeenWithin(VPSUPTODATETIME); - } - else { - cSchedulesLock SchedulesLock; - const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock); - if (Schedules) { - const cSchedule *Schedule = Schedules->GetSchedule(Timer->Channel()); - InVpsMargin = !Schedule; // we must make sure we have the schedule - NeedsTransponder = Schedule && !Schedule->PresentSeenWithin(VPSUPTODATETIME); - } - } - InhibitEpgScan |= InVpsMargin | NeedsTransponder; - } - else - NeedsTransponder = Timer->Matches(Now, true, TIMERLOOKAHEADTIME); - } - if (NeedsTransponder || InVpsMargin) { - // Find a device that provides the required transponder: - cDevice *Device = cDevice::GetDeviceForTransponder(Timer->Channel(), MINPRIORITY); - if (!Device && InVpsMargin) - Device = cDevice::GetDeviceForTransponder(Timer->Channel(), LIVEPRIORITY); - // Switch the device to the transponder: - if (Device) { - bool HadProgramme = cDevice::PrimaryDevice()->HasProgramme(); - if (!Device->IsTunedToTransponder(Timer->Channel())) { - if (Device == cDevice::ActualDevice() && !Device->IsPrimaryDevice()) - cDevice::PrimaryDevice()->StopReplay(); // stop transfer mode - dsyslog("switching device %d to channel %d (%s)", Device->DeviceNumber() + 1, Timer->Channel()->Number(), Timer->Channel()->Name()); - if (Device->SwitchChannel(Timer->Channel(), false)) - Device->SetOccupied(TIMERDEVICETIMEOUT); - } - if (cDevice::PrimaryDevice()->HasDecoder() && HadProgramme && !cDevice::PrimaryDevice()->HasProgramme()) - Skins.QueueMessage(mtInfo, tr("Upcoming recording!")); // the previous SwitchChannel() has switched away the current live channel - } - } - } - LastTimerCheck = Now; - } - // Delete expired timers: - Timers.DeleteExpired(); - } - if (!Menu && Recordings.NeedsUpdate()) { - Recordings.Update(); - DeletedRecordings.Update(); + { + // Timers and Recordings: + bool TimersModified = false; + bool TriggerRemoteTimerPoll = false; + static cStateKey TimersStateKey(true); + if (cTimers::GetTimersRead(TimersStateKey)) { + TriggerRemoteTimerPoll = true; + TimersStateKey.Remove(); + } + cTimers *Timers = cTimers::GetTimersWrite(TimersStateKey); + // Get remote timers: + TimersModified |= Timers->GetRemoteTimers(); + // Assign events to timers: + static cStateKey SchedulesStateKey; + if (const cSchedules *Schedules = cSchedules::GetSchedulesRead(SchedulesStateKey)) + TimersModified |= Timers->SetEvents(Schedules); + // Must do all following calls with the exact same time! + // Process ongoing recordings: + if (cRecordControls::Process(Timers, Now)) { + TimersModified = true; + TriggerRemoteTimerPoll = true; + } + // Must keep the lock on the schedules until after processing the record + // controls, in order to avoid short interrupts in case the current event + // is replaced by a new one (which some broadcasters do, instead of just + // modifying the current event's data): + if (SchedulesStateKey.InLock()) + SchedulesStateKey.Remove(); + // Start new recordings: + if (cTimer *Timer = Timers->GetMatch(Now)) { + if (!cRecordControls::Start(Timers, Timer)) + Timer->SetPending(true); + else + LastTimerChannel = Timer->Channel()->Number(); + TimersModified = true; + TriggerRemoteTimerPoll = true; + } + // Make sure timers "see" their channel early enough: + static time_t LastTimerCheck = 0; + if (Now - LastTimerCheck > TIMERCHECKDELTA) { // don't do this too often + InhibitEpgScan = false; + for (cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) { + if (Timer->Remote()) + continue; + bool InVpsMargin = false; + bool NeedsTransponder = false; + if (Timer->HasFlags(tfActive) && !Timer->Recording()) { + if (Timer->HasFlags(tfVps)) { + if (Timer->Matches(Now, true, Setup.VpsMargin)) { + InVpsMargin = true; + Timer->SetInVpsMargin(InVpsMargin); + } + else if (Timer->Event()) { + InVpsMargin = Timer->Event()->StartTime() <= Now && Now < Timer->Event()->EndTime(); + NeedsTransponder = Timer->Event()->StartTime() - Now < VPSLOOKAHEADTIME * 3600 && !Timer->Event()->SeenWithin(VPSUPTODATETIME); + } + else { + LOCK_SCHEDULES_READ; + const cSchedule *Schedule = Schedules->GetSchedule(Timer->Channel()); + InVpsMargin = !Schedule; // we must make sure we have the schedule + NeedsTransponder = Schedule && !Schedule->PresentSeenWithin(VPSUPTODATETIME); + } + InhibitEpgScan |= InVpsMargin | NeedsTransponder; + } + else + NeedsTransponder = Timer->Matches(Now, true, TIMERLOOKAHEADTIME); + } + if (NeedsTransponder || InVpsMargin) { + // Find a device that provides the required transponder: + cDevice *Device = cDevice::GetDeviceForTransponder(Timer->Channel(), MINPRIORITY); + if (!Device && InVpsMargin) + Device = cDevice::GetDeviceForTransponder(Timer->Channel(), LIVEPRIORITY); + // Switch the device to the transponder: + if (Device) { + bool HadProgramme = cDevice::PrimaryDevice()->HasProgramme(); + if (!Device->IsTunedToTransponder(Timer->Channel())) { + if (Device == cDevice::ActualDevice() && !Device->IsPrimaryDevice()) + cDevice::PrimaryDevice()->StopReplay(); // stop transfer mode + dsyslog("switching device %d to channel %d (%s)", Device->DeviceNumber() + 1, Timer->Channel()->Number(), Timer->Channel()->Name()); + if (Device->SwitchChannel(Timer->Channel(), false)) + Device->SetOccupied(TIMERDEVICETIMEOUT); + } + if (cDevice::PrimaryDevice()->HasDecoder() && HadProgramme && !cDevice::PrimaryDevice()->HasProgramme()) + Skins.QueueMessage(mtInfo, tr("Upcoming recording!")); // the previous SwitchChannel() has switched away the current live channel + } + } + } + LastTimerCheck = Now; + } + // Delete expired timers: + if (Timers->DeleteExpired()) { + TimersModified = true; + TriggerRemoteTimerPoll = true; + } + // Trigger remote timer polls: + if (TriggerRemoteTimerPoll) + Timers->TriggerRemoteTimerPoll(); + TimersStateKey.Remove(TimersModified); + } + // Recordings: + if (!Menu) { + if (cRecordings::NeedsUpdate()) + cRecordings::Update(); } // CAM control: if (!Menu && !cOsd::IsOpen()) @@ -1244,8 +1310,12 @@ int main(int argc, char *argv[]) // Instant recording: case kRecord: if (!cControl::Control()) { - if (cRecordControls::Start()) - Skins.QueueMessage(mtInfo, tr("Recording started")); + if (Setup.RecordKeyHandling) { + if (Setup.RecordKeyHandling > 1 || Interface->Confirm(tr("Start recording?"))) { + if (cRecordControls::Start()) + Skins.QueueMessage(mtInfo, tr("Recording started")); + } + } key = kNone; // nobody else needs to see this key } break; @@ -1348,7 +1418,8 @@ int main(int argc, char *argv[]) case k0: { if (PreviousChannel[PreviousChannelIndex ^ 1] == LastChannel || LastChannel != PreviousChannel[0] && LastChannel != PreviousChannel[1]) PreviousChannelIndex ^= 1; - Channels.SwitchTo(PreviousChannel[PreviousChannelIndex ^= 1]); + LOCK_CHANNELS_READ; + Channels->SwitchTo(PreviousChannel[PreviousChannelIndex ^= 1]); break; } // Direct Channel Select: @@ -1412,7 +1483,7 @@ int main(int argc, char *argv[]) // Keep the recordings handler alive: RecordingsHandler.Active(); - if ((Now - LastInteract) > ACTIVITYTIMEOUT && !cRecordControls::Active() && !RecordingsHandler.Active() && !Interface->HasSVDRPConnection() && (Now - cRemote::LastActivity()) > ACTIVITYTIMEOUT) { + if ((Now - LastInteract) > ACTIVITYTIMEOUT && !cRecordControls::Active() && !RecordingsHandler.Active() && (Now - cRemote::LastActivity()) > ACTIVITYTIMEOUT) { // Handle housekeeping tasks // Shutdown: @@ -1436,7 +1507,7 @@ int main(int argc, char *argv[]) // Disk housekeeping: RemoveDeletedRecordings(); - ClearVanishedRecordings(); + ListGarbageCollector.Purge(); cSchedules::Cleanup(); // Plugins housekeeping: PluginManager.Housekeeping(); @@ -1460,6 +1531,8 @@ Exit: signal(SIGPIPE, SIG_DFL); signal(SIGALRM, SIG_DFL); + StopSVDRPClientHandler(); + StopSVDRPServerHandler(); PluginManager.StopPlugins(); cRecordControls::Shutdown(); RecordingsHandler.DelAll(); @@ -1480,8 +1553,9 @@ Exit: cPositioner::DestroyPositioner(); cVideoDirectory::Destroy(); EpgHandlers.Clear(); - PluginManager.Shutdown(true); cSchedules::Cleanup(true); + ListGarbageCollector.Purge(true); + PluginManager.Shutdown(true); ReportEpgBugFixStats(true); if (WatchdogTimeout > 0) dsyslog("max. latency time %d seconds", MaxLatencyTime); diff --git a/videodir.c b/videodir.c index 932f8ce3..b2257fa6 100644 --- a/videodir.c +++ b/videodir.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: videodir.c 3.4 2013/10/11 09:38:07 kls Exp $ + * $Id: videodir.c 4.1 2015/08/11 13:39:59 kls Exp $ */ #include "videodir.h" @@ -19,24 +19,31 @@ #include "recording.h" #include "tools.h" +cMutex cVideoDirectory::mutex; cString cVideoDirectory::name; cVideoDirectory *cVideoDirectory::current = NULL; cVideoDirectory::cVideoDirectory(void) { + mutex.Lock(); delete current; current = this; + mutex.Unlock(); } cVideoDirectory::~cVideoDirectory() { + mutex.Lock(); current = NULL; + mutex.Unlock(); } cVideoDirectory *cVideoDirectory::Current(void) { + mutex.Lock(); if (!current) - current = new cVideoDirectory; + new cVideoDirectory; + mutex.Unlock(); return current; } @@ -141,7 +148,8 @@ int cVideoDirectory::VideoDiskSpace(int *FreeMB, int *UsedMB) { int used = 0; int free = Current()->FreeMB(&used); - int deleted = DeletedRecordings.TotalFileSizeMB(); + LOCK_DELETEDRECORDINGS_READ; + int deleted = DeletedRecordings->TotalFileSizeMB(); if (deleted > used) deleted = used; // let's not get beyond 100% free += deleted; @@ -202,7 +210,8 @@ bool cVideoDiskUsage::HasChanged(int &State) if (FreeMB != freeMB) { usedPercent = UsedPercent; freeMB = FreeMB; - double MBperMinute = Recordings.MBperMinute(); + LOCK_RECORDINGS_READ; + double MBperMinute = Recordings->MBperMinute(); if (MBperMinute <= 0) MBperMinute = MB_PER_MINUTE; freeMinutes = int(double(FreeMB) / MBperMinute); diff --git a/videodir.h b/videodir.h index f520d77b..385c822e 100644 --- a/videodir.h +++ b/videodir.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: videodir.h 3.2 2013/10/11 09:37:48 kls Exp $ + * $Id: videodir.h 4.1 2015/08/10 13:21:29 kls Exp $ */ #ifndef __VIDEODIR_H @@ -15,6 +15,7 @@ class cVideoDirectory { private: + static cMutex mutex; static cString name; static cVideoDirectory *current; static cVideoDirectory *Current(void);