diff options
| author | Klaus Schmidinger <vdr@tvdr.de> | 2012-03-10 15:10:43 +0100 | 
|---|---|---|
| committer | Klaus Schmidinger <vdr@tvdr.de> | 2012-03-10 15:10:43 +0100 | 
| commit | 860786f809e55ac36d3f347b132d93118ca201d4 (patch) | |
| tree | 3a99189a28a55ed499dcca562ba3eaabed568ae0 | |
| parent | bc06fc2ce38392605ef094cfe544faafad9c1cb8 (diff) | |
| download | vdr-860786f809e55ac36d3f347b132d93118ca201d4.tar.gz vdr-860786f809e55ac36d3f347b132d93118ca201d4.tar.bz2 | |
Added a new plugin interface for implementing EPG handlers1.7.26
| -rw-r--r-- | HISTORY | 12 | ||||
| -rw-r--r-- | PLUGINS.html | 33 | ||||
| -rw-r--r-- | PLUGINS/src/epgtableid0/COPYING | 340 | ||||
| -rw-r--r-- | PLUGINS/src/epgtableid0/HISTORY | 6 | ||||
| -rw-r--r-- | PLUGINS/src/epgtableid0/Makefile | 114 | ||||
| -rw-r--r-- | PLUGINS/src/epgtableid0/README | 19 | ||||
| -rw-r--r-- | PLUGINS/src/epgtableid0/epgtableid0.c | 98 | ||||
| -rw-r--r-- | eit.c | 80 | ||||
| -rw-r--r-- | epg.c | 154 | ||||
| -rw-r--r-- | epg.h | 67 | ||||
| -rw-r--r-- | vdr.5 | 4 | ||||
| -rw-r--r-- | vdr.c | 3 | 
12 files changed, 878 insertions, 52 deletions
| @@ -6960,7 +6960,7 @@ Video Disk Recorder Revision History    which was made in version 1.1.10, so please report if this has any unwanted    side effects. -2012-03-08: Version 1.7.26 +2012-03-10: Version 1.7.26  - Now checking for NULL in cOsd::AddPixmap() (suggested by Christoph Haubrich).  - Fixed the German translation of "VDR will shut down in %s minutes" (thanks to @@ -6999,3 +6999,13 @@ Video Disk Recorder Revision History    is selected, but it works at any time (and is more obvious).  - Revoked "If the first event in a schedule has a table id of 0x00, any incoming EIT data for    that schedule from the DVB stream will be completely ignored". +- Added a new plugin interface for implementing EPG handlers. +  + A plugin can implement an EPG handler by creating an object derived from +    cEpgHandler and implementing the necessary member functions. +  + The special handling of events with table id 0x00 has been dropped. +    For backwards compatibility EPG events with table ids lower than 0x4E will +    be treated as if they had a table id of 0x4E, and the new plugin 'epgtableid0' +    can be used to have them handled like in previous versions. +  + The default table id for a newly created cEvent has been changed to 0xFF, +    which is higher than any normal table id that is broadcast in the EIT data. +  See PLUGINS.html, section "Electronic Program Guide" for more information. diff --git a/PLUGINS.html b/PLUGINS.html index 4d939430..82164bff 100644 --- a/PLUGINS.html +++ b/PLUGINS.html @@ -102,6 +102,7 @@ structures and allows it to hook itself into specific areas to perform special a  <li><a href="#Audio">Audio</a>  <li><a href="#Remote Control">Remote Control</a>  <li><a href="#Conditional Access">Conditional Access</a> +<li><modified><a href="#Electronic Program Guide">Electronic Program Guide</modified></a>  </ul>  </ul> @@ -2212,5 +2213,37 @@ virtual bool Assign(cDevice *Device, bool Query = false);  See the description of this function in <tt>ci.h</tt> for details. +<div class="modified"> +<hr><h2><a name="Electronic Program Guide">Electronic Program Guide</a></h2> + +<div class="blurb">The grass is always greener on the other side...</div><p> + +In case the <i>Electronic Program Guide (EPG)</i> provided by the broadcaster +isn't sufficient for your taste, you can implement a <tt>cEpgHandler</tt> to +improve it from external sources. For instance, to replace the description +of the broadcaster's EPG with one from some external database, you could do: + +<p><table><tr><td class="code"><pre> +#include <vdr/epg.h> + +class cMyEpgHandler : public cEpgHandler { +public: +  virtual bool SetDescription(cEvent *Event, const char *Description); +  }; + +bool cMyEpgHandler::SetDescription(cEvent *Event, const char *Description) +{ +  Event->SetDescription(DescriptionFromDatabase(Event)); +  return true; +} +</pre></td></tr></table><p> + +where <tt>DescriptionFromDatabase()</tt> would derive the description of the + given event from some external source. Note that the function returns <tt>true</tt> +to signal VDR that no other EPG handlers shall be queried after this one. +<p> +See <tt>VDR/epg.h</tt> for details. +</div modified> +  </body>  </html> diff --git a/PLUGINS/src/epgtableid0/COPYING b/PLUGINS/src/epgtableid0/COPYING new file mode 100644 index 00000000..f90922ee --- /dev/null +++ b/PLUGINS/src/epgtableid0/COPYING @@ -0,0 +1,340 @@ +		    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. + +    <one line to give the program's name and a brief idea of what it does.> +    Copyright (C) <year>  <name of author> + +    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. + +  <signature of Ty Coon>, 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/epgtableid0/HISTORY b/PLUGINS/src/epgtableid0/HISTORY new file mode 100644 index 00000000..7f732add --- /dev/null +++ b/PLUGINS/src/epgtableid0/HISTORY @@ -0,0 +1,6 @@ +VDR Plugin 'epgtableid0' Revision History +----------------------------------------- + +2012-03-10: Version 0.0.1 + +- Initial revision. diff --git a/PLUGINS/src/epgtableid0/Makefile b/PLUGINS/src/epgtableid0/Makefile new file mode 100644 index 00000000..5653e28e --- /dev/null +++ b/PLUGINS/src/epgtableid0/Makefile @@ -0,0 +1,114 @@ +# +# Makefile for a Video Disk Recorder plugin +# +# $Id: Makefile 1.1 2012/03/10 14:23:58 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. +# IMPORTANT: the presence of this macro is important for the Make.config +# file. So it must be defined, even if it is not used here! +# +PLUGIN = epgtableid0 + +### 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 C++ compiler and options: + +CXX      ?= g++ +CXXFLAGS ?= -g -O3 -Wall -Woverloaded-virtual -Wno-parentheses + +### The directory environment: + +VDRDIR ?= ../../.. +LIBDIR ?= ../../lib +TMPDIR ?= /tmp + +### Make sure that necessary options are included: + +include $(VDRDIR)/Make.global + +### Allow user defined options to overwrite defaults: + +-include $(VDRDIR)/Make.config + +### The version number of VDR's plugin API (taken from VDR's "config.h"): + +APIVERSION = $(shell sed -ne '/define APIVERSION/s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h) + +### The name of the distribution archive: + +ARCHIVE = $(PLUGIN)-$(VERSION) +PACKAGE = vdr-$(ARCHIVE) + +### Includes and Defines (add further entries here): + +INCLUDES += -I$(VDRDIR)/include + +DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' + +### The object files (add further files here): + +OBJS = $(PLUGIN).o + +### The main target: + +all: libvdr-$(PLUGIN).so i18n + +### Implicit rules: + +%.o: %.c +	$(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $< + +### Dependencies: + +MAKEDEP = $(CXX) -MM -MG +DEPFILE = .dependencies +$(DEPFILE): Makefile +	@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@ + +-include $(DEPFILE) + +### Internationalization (I18N): + +PODIR     = po +LOCALEDIR = $(VDRDIR)/locale +I18Npo    = $(wildcard $(PODIR)/*.po) +I18Nmsgs  = $(addprefix $(LOCALEDIR)/, $(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='<see README>' -o $@ $^ + +%.po: $(I18Npot) +	msgmerge -U --no-wrap --no-location --backup=none -q $@ $< +	@touch $@ + +$(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo +	@mkdir -p $(dir $@) +	cp $< $@ + +.PHONY: i18n +i18n: $(I18Nmsgs) $(I18Npot) + +### Targets: + +libvdr-$(PLUGIN).so: $(OBJS) +	$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) -o $@ +	@cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION) + +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 $(OBJS) $(DEPFILE) *.so *.tgz core* *~ $(PODIR)/*.mo $(PODIR)/*.pot diff --git a/PLUGINS/src/epgtableid0/README b/PLUGINS/src/epgtableid0/README new file mode 100644 index 00000000..8c2436dd --- /dev/null +++ b/PLUGINS/src/epgtableid0/README @@ -0,0 +1,19 @@ +This is a "plugin" for the Video Disk Recorder (VDR). + +Written by:                  Klaus Schmidinger <kls@tvdr.de> + +Project's homepage:          http://www.tvdr.de + +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 special handling of EPG events with table id 0x00 has been dropped +in VDR version 1.7.26, and replaced by a more felxible "EPG Handler" +concept. You can use this plugin to get the previous functionality back. diff --git a/PLUGINS/src/epgtableid0/epgtableid0.c b/PLUGINS/src/epgtableid0/epgtableid0.c new file mode 100644 index 00000000..d400b48c --- /dev/null +++ b/PLUGINS/src/epgtableid0/epgtableid0.c @@ -0,0 +1,98 @@ +/* + * epgtableid0.c: A plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + * $Id: epgtableid0.c 1.1 2012/03/10 15:10:43 kls Exp $ + */ + +#include <vdr/epg.h> +#include <vdr/plugin.h> + +static const char *VERSION        = "0.0.1"; +static const char *DESCRIPTION    = "EPG handler for events with table id 0x00"; + +// --- cTable0Handler -------------------------------------------------------- + +class cTable0Handler : public cEpgHandler { +private: +  bool Ignore(cEvent *Event) { return Event->TableID() == 0x00; } +public: +  virtual bool SetEventID(cEvent *Event, tEventID EventID); +  virtual bool SetStartTime(cEvent *Event, time_t StartTime); +  virtual bool SetDuration(cEvent *Event, int Duration); +  virtual bool SetTitle(cEvent *Event, const char *Title); +  virtual bool SetShortText(cEvent *Event, const char *ShortText); +  virtual bool SetDescription(cEvent *Event, const char *Description); +  virtual bool SetContents(cEvent *Event, uchar *Contents); +  virtual bool SetParentalRating(cEvent *Event, int ParentalRating); +  virtual bool SetVps(cEvent *Event, time_t Vps); +  virtual bool FixEpgBugs(cEvent *Event); +  }; + +bool cTable0Handler::SetEventID(cEvent *Event, tEventID EventID) +{ +  return Ignore(Event); +} + +bool cTable0Handler::SetStartTime(cEvent *Event, time_t StartTime) +{ +  return Ignore(Event); +} + +bool cTable0Handler::SetDuration(cEvent *Event, int Duration) +{ +  return Ignore(Event); +} + +bool cTable0Handler::SetTitle(cEvent *Event, const char *Title) +{ +  return Ignore(Event); +} + +bool cTable0Handler::SetShortText(cEvent *Event, const char *ShortText) +{ +  return Ignore(Event); +} + +bool cTable0Handler::SetDescription(cEvent *Event, const char *Description) +{ +  return Ignore(Event); +} + +bool cTable0Handler::SetContents(cEvent *Event, uchar *Contents) +{ +  return Ignore(Event); +} + +bool cTable0Handler::SetParentalRating(cEvent *Event, int ParentalRating) +{ +  return Ignore(Event); +} + +bool cTable0Handler::SetVps(cEvent *Event, time_t Vps) +{ +  return Ignore(Event); +} + +bool cTable0Handler::FixEpgBugs(cEvent *Event) +{ +  return Ignore(Event); +} + +// --- cPluginEpgtableid0 ---------------------------------------------------- + +class cPluginEpgtableid0 : public cPlugin { +public: +  virtual const char *Version(void) { return VERSION; } +  virtual const char *Description(void) { return DESCRIPTION; } +  virtual bool Initialize(void); +  }; + +bool cPluginEpgtableid0::Initialize(void) +{ +  new cTable0Handler; +  return true; +} + +VDRPLUGINCREATOR(cPluginEpgtableid0); // Don't touch this! @@ -8,7 +8,7 @@   * Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.   * Adapted to 'libsi' for VDR 1.3.0 by Marcel Wiesweg <marcel.wiesweg@gmx.de>.   * - * $Id: eit.c 2.14 2012/03/08 15:05:45 kls Exp $ + * $Id: eit.c 2.15 2012/03/10 14:43:52 kls Exp $   */  #include "eit.h" @@ -36,12 +36,13 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bo    cChannel *channel = Channels.GetByChannelID(channelID, true);    if (!channel)       return; // only collect data for known channels +  if (EpgHandlers.IgnoreChannel(channel)) +     return;    cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(channel, true);    bool Empty = true;    bool Modified = false; -  bool HasExternalData = false;    time_t SegmentStart = 0;    time_t SegmentEnd = 0;    time_t Now = time(NULL); @@ -53,7 +54,8 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bo    SI::EIT::Event SiEitEvent;    for (SI::Loop::Iterator it; eventLoop.getNext(SiEitEvent, it); ) { -      bool ExternalData = false; +      if (EpgHandlers.HandleEitEvent(pSchedule, &SiEitEvent, Tid, getVersionNumber())) +         continue; // an EPG handler has done all of the processing        time_t StartTime = SiEitEvent.getStartTime();        int Duration = SiEitEvent.getDuration();        // Drop bogus events - but keep NVOD reference events, where all bits of the start time field are set to 1, resulting in a negative number. @@ -72,23 +74,17 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bo           // If we don't have that event yet, we create a new one.           // Otherwise we copy the information into the existing event anyway, because the data might have changed.           pEvent = newEvent = new cEvent(SiEitEvent.getEventId()); -         if (!pEvent) -            continue; +         newEvent->SetStartTime(StartTime); +         newEvent->SetDuration(Duration); +         pSchedule->AddEvent(newEvent);           }        else {           // We have found an existing event, either through its event ID or its start time.           pEvent->SetSeen(); -         // If the existing event has a zero table ID it was defined externally and shall -         // not be overwritten. -         uchar TableID = pEvent->TableID(); -         if (TableID == 0x00) { -            if (pEvent->Version() == getVersionNumber()) -               continue; -            HasExternalData = ExternalData = true; -            } +         uchar TableID = max(pEvent->TableID(), uchar(0x4E)); // for backwards compatibility, table ids less than 0x4E are treated as if they were "present"           // If the new event has a higher table ID, let's skip it.           // The lower the table ID, the more "current" the information. -         else if (Tid > TableID) +         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. @@ -98,21 +94,20 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bo           // 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);           } -      if (!ExternalData) { -         pEvent->SetEventID(SiEitEvent.getEventId()); // unfortunately some stations use different event ids for the same event in different tables :-( +      if (pEvent->TableID() > 0x4E) // for backwards compatibility, table ids less than 0x4E are never overwritten           pEvent->SetTableID(Tid); -         pEvent->SetStartTime(StartTime); -         pEvent->SetDuration(Duration); -         } -      if (newEvent) -         pSchedule->AddEvent(newEvent);        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) +      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 +         }        pEvent->SetVersion(getVersionNumber());        int LanguagePreferenceShort = -1; @@ -124,10 +119,6 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bo        cLinkChannels *LinkChannels = NULL;        cComponents *Components = NULL;        for (SI::Loop::Iterator it2; (d = SiEitEvent.eventDescriptors.getNext(it2)); ) { -          if (ExternalData && d->getDescriptorTag() != SI::ComponentDescriptorTag) { -             delete d; -             continue; -             }            switch (d->getDescriptorTag()) {              case SI::ExtendedEventDescriptorTag: {                   SI::ExtendedEventDescriptor *eed = (SI::ExtendedEventDescriptor *)d; @@ -164,7 +155,7 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bo                          NumContents++;                          }                       } -                 pEvent->SetContents(Contents); +                 EpgHandlers.SetContents(pEvent, Contents);                   }                   break;              case SI::ParentalRatingDescriptorTag: { @@ -183,7 +174,7 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bo                            case 0x13:          ParentalRating = 16; break;                            default:            ParentalRating = 0;                            } -                        pEvent->SetParentalRating(ParentalRating); +                        EpgHandlers.SetParentalRating(pEvent, ParentalRating);                          }                       }                   } @@ -202,7 +193,7 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bo                   else if (month == 0 && t.tm_mon == 11) // current month is jan, but event is in dec                      t.tm_year--;                   time_t vps = mktime(&t); -                 pEvent->SetVps(vps); +                 EpgHandlers.SetVps(pEvent, vps);                   }                   break;              case SI::TimeShiftedEventDescriptorTag: { @@ -213,9 +204,9 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bo                   rEvent = (cEvent *)rSchedule->GetEvent(tsed->getReferenceEventId());                   if (!rEvent)                      break; -                 pEvent->SetTitle(rEvent->Title()); -                 pEvent->SetShortText(rEvent->ShortText()); -                 pEvent->SetDescription(rEvent->Description()); +                 EpgHandlers.SetTitle(pEvent, rEvent->Title()); +                 EpgHandlers.SetShortText(pEvent, rEvent->ShortText()); +                 EpgHandlers.SetDescription(pEvent, rEvent->Description());                   }                   break;              case SI::LinkageDescriptorTag: { @@ -273,30 +264,30 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bo        if (!rEvent) {           if (ShortEventDescriptor) {              char buffer[Utf8BufSize(256)]; -            pEvent->SetTitle(ShortEventDescriptor->name.getText(buffer, sizeof(buffer))); -            pEvent->SetShortText(ShortEventDescriptor->text.getText(buffer, sizeof(buffer))); +            EpgHandlers.SetTitle(pEvent, ShortEventDescriptor->name.getText(buffer, sizeof(buffer))); +            EpgHandlers.SetShortText(pEvent, ShortEventDescriptor->text.getText(buffer, sizeof(buffer)));              } -         else if (!HasExternalData) { -            pEvent->SetTitle(NULL); -            pEvent->SetShortText(NULL); +         else { +            EpgHandlers.SetTitle(pEvent, NULL); +            EpgHandlers.SetShortText(pEvent, NULL);              }           if (ExtendedEventDescriptors) {              char buffer[Utf8BufSize(ExtendedEventDescriptors->getMaximumTextLength(": ")) + 1]; -            pEvent->SetDescription(ExtendedEventDescriptors->getText(buffer, sizeof(buffer), ": ")); +            EpgHandlers.SetDescription(pEvent, ExtendedEventDescriptors->getText(buffer, sizeof(buffer), ": "));              } -         else if (!HasExternalData) -            pEvent->SetDescription(NULL); +         else +            EpgHandlers.SetDescription(pEvent, NULL);           }        delete ExtendedEventDescriptors;        delete ShortEventDescriptor;        pEvent->SetComponents(Components); -      if (!HasExternalData) -         pEvent->FixEpgBugs(); +      EpgHandlers.FixEpgBugs(pEvent);        if (LinkChannels)           channel->SetLinkChannels(LinkChannels);        Modified = true; +      EpgHandlers.HandleEvent(pEvent);        }    if (Tid == 0x4E) {       if (Empty && getSectionNumber() == 0) @@ -307,9 +298,8 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bo    if (OnlyRunningStatus)       return;    if (Modified) { -     pSchedule->Sort(); -     if (!HasExternalData) -        pSchedule->DropOutdated(SegmentStart, SegmentEnd, Tid, getVersionNumber()); +     EpgHandlers.SortSchedule(pSchedule); +     EpgHandlers.DropOutdated(pSchedule, SegmentStart, SegmentEnd, Tid, getVersionNumber());       Schedules->SetModified(pSchedule);       }  } @@ -7,7 +7,7 @@   * Original version (as used in VDR before 1.3.0) written by   * Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.   * - * $Id: epg.c 2.11 2012/02/13 14:58:19 kls Exp $ + * $Id: epg.c 2.12 2012/03/10 13:14:27 kls Exp $   */  #include "epg.h" @@ -114,7 +114,7 @@ cEvent::cEvent(tEventID EventID)  {    schedule = NULL;    eventID = EventID; -  tableID = 0; +  tableID = 0xFF; // actual table ids are 0x4E..0x60    version = 0xFF; // actual version numbers are 0..31    runningStatus = SI::RunningStatusUndefined;    title = NULL; @@ -1296,3 +1296,153 @@ void cEpgDataReader::Action(void)  {    cSchedules::Read();  } + +// --- cEpgHandler ----------------------------------------------------------- + +cEpgHandler::cEpgHandler(void) +{ +  EpgHandlers.Add(this); +} + +cEpgHandler::~cEpgHandler() +{ +  EpgHandlers.Del(this, false); +} + +// --- cEpgHandlers ---------------------------------------------------------- + +cEpgHandlers EpgHandlers; + +bool cEpgHandlers::IgnoreChannel(const cChannel *Channel) +{ +  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) { +      if (eh->IgnoreChannel(Channel)) +         return true; +      } +  return false; +} + +bool cEpgHandlers::HandleEitEvent(cSchedule *Schedule, const SI::EIT::Event *EitEvent, uchar TableID, uchar Version) +{ +  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) { +      if (eh->HandleEitEvent(Schedule, EitEvent, TableID, Version)) +         return true; +      } +  return false; +} + +void cEpgHandlers::SetEventID(cEvent *Event, tEventID EventID) +{ +  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) { +      if (eh->SetEventID(Event, EventID)) +         return; +      } +  Event->SetEventID(EventID); +} + +void cEpgHandlers::SetTitle(cEvent *Event, const char *Title) +{ +  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) { +      if (eh->SetTitle(Event, Title)) +         return; +      } +  Event->SetTitle(Title); +} + +void cEpgHandlers::SetShortText(cEvent *Event, const char *ShortText) +{ +  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) { +      if (eh->SetShortText(Event, ShortText)) +         return; +      } +  Event->SetShortText(ShortText); +} + +void cEpgHandlers::SetDescription(cEvent *Event, const char *Description) +{ +  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) { +      if (eh->SetDescription(Event, Description)) +         return; +      } +  Event->SetDescription(Description); +} + +void cEpgHandlers::SetContents(cEvent *Event, uchar *Contents) +{ +  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) { +      if (eh->SetContents(Event, Contents)) +         return; +      } +  Event->SetContents(Contents); +} + +void cEpgHandlers::SetParentalRating(cEvent *Event, int ParentalRating) +{ +  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) { +      if (eh->SetParentalRating(Event, ParentalRating)) +         return; +      } +  Event->SetParentalRating(ParentalRating); +} + +void cEpgHandlers::SetStartTime(cEvent *Event, time_t StartTime) +{ +  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) { +      if (eh->SetStartTime(Event, StartTime)) +         return; +      } +  Event->SetStartTime(StartTime); +} + +void cEpgHandlers::SetDuration(cEvent *Event, int Duration) +{ +  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) { +      if (eh->SetDuration(Event, Duration)) +         return; +      } +  Event->SetDuration(Duration); +} + +void cEpgHandlers::SetVps(cEvent *Event, time_t Vps) +{ +  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) { +      if (eh->SetVps(Event, Vps)) +         return; +      } +  Event->SetVps(Vps); +} + +void cEpgHandlers::FixEpgBugs(cEvent *Event) +{ +  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) { +      if (eh->FixEpgBugs(Event)) +         return; +      } +  Event->FixEpgBugs(); +} + +void cEpgHandlers::HandleEvent(cEvent *Event) +{ +  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) { +      if (eh->HandleEvent(Event)) +         break; +      } +} + +void cEpgHandlers::SortSchedule(cSchedule *Schedule) +{ +  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) { +      if (eh->SortSchedule(Schedule)) +         return; +      } +  Schedule->Sort(); +} + +void cEpgHandlers::DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version) +{ +  for (cEpgHandler *eh = First(); eh; eh = Next(eh)) { +      if (eh->DropOutdated(Schedule, SegmentStart, SegmentEnd, TableID, Version)) +         return; +      } +  Schedule->DropOutdated(SegmentStart, SegmentEnd, TableID, Version); +} @@ -7,13 +7,14 @@   * Original version (as used in VDR before 1.3.0) written by   * Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.   * - * $Id: epg.h 2.7 2012/02/26 13:58:26 kls Exp $ + * $Id: epg.h 2.8 2012/03/10 13:50:10 kls Exp $   */  #ifndef __EPG_H  #define __EPG_H  #include "channels.h" +#include "libsi/section.h"  #include "thread.h"  #include "tools.h" @@ -221,4 +222,68 @@ public:  void ReportEpgBugFixStats(bool Reset = false); +class cEpgHandler : public cListObject { +public: +  cEpgHandler(void); +          ///< Constructs a new EPG handler and adds it to the list of EPG handlers. +          ///< Whenever an event is received from the EIT data stream, the EPG +          ///< handlers are queried in the order they have been created. +          ///< As soon as one of the EPG handlers returns true in a member function, +          ///< none of the remaining handlers will be queried. If none of the EPG +          ///< handlers returns true in a particular call, the default processing +          ///< will take place. +          ///< EPG handlers will be deleted automatically at the end of the program. +  virtual ~cEpgHandler(); +  virtual bool IgnoreChannel(const cChannel *Channel) { return false; } +          ///< Before any EIT data for the given Channel is processed, the EPG handlers +          ///< are asked whether this Channel shall be completely ignored. If any of +          ///< the EPG handlers returns true in this function, no EIT data at all will +          ///< be processed for this Channel. +  virtual bool HandleEitEvent(cSchedule *Schedule, const SI::EIT::Event *EitEvent, uchar TableID, uchar Version) { return false; } +          ///< Before the raw EitEvent for the given Schedule is processed, the +          ///< EPG handlers are queried to see if any of them would like to do the +          ///< complete processing by itself. TableID and Version are from the +          ///< incoming section data. +  virtual bool SetEventID(cEvent *Event, tEventID EventID) { return false; } +  virtual bool SetTitle(cEvent *Event, const char *Title) { return false; } +  virtual bool SetShortText(cEvent *Event, const char *ShortText) { return false; } +  virtual bool SetDescription(cEvent *Event, const char *Description) { return false; } +  virtual bool SetContents(cEvent *Event, uchar *Contents) { return false; } +  virtual bool SetParentalRating(cEvent *Event, int ParentalRating) { return false; } +  virtual bool SetStartTime(cEvent *Event, time_t StartTime) { return false; } +  virtual bool SetDuration(cEvent *Event, int Duration) { return false; } +  virtual bool SetVps(cEvent *Event, time_t Vps) { return false; } +  virtual bool FixEpgBugs(cEvent *Event) { return false; } +          ///< Fixes some known problems with EPG data. +  virtual bool HandleEvent(cEvent *Event) { return false; } +          ///< After all modifications of the Event have been done, the EPG handler +          ///< can take a final look at it. +  virtual bool SortSchedule(cSchedule *Schedule) { return false; } +          ///< Sorts the Schedule after the complete table has been processed. +  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. +  }; + +class cEpgHandlers : public cList<cEpgHandler> { +public: +  bool IgnoreChannel(const cChannel *Channel); +  bool HandleEitEvent(cSchedule *Schedule, const SI::EIT::Event *EitEvent, uchar TableID, uchar Version); +  void SetEventID(cEvent *Event, tEventID EventID); +  void SetTitle(cEvent *Event, const char *Title); +  void SetShortText(cEvent *Event, const char *ShortText); +  void SetDescription(cEvent *Event, const char *Description); +  void SetContents(cEvent *Event, uchar *Contents); +  void SetParentalRating(cEvent *Event, int ParentalRating); +  void SetStartTime(cEvent *Event, time_t StartTime); +  void SetDuration(cEvent *Event, int Duration); +  void SetVps(cEvent *Event, time_t Vps); +  void FixEpgBugs(cEvent *Event); +  void HandleEvent(cEvent *Event); +  void SortSchedule(cSchedule *Schedule); +  void DropOutdated(cSchedule *Schedule, time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version); +  }; + +extern cEpgHandlers EpgHandlers; +  #endif //__EPG_H @@ -8,7 +8,7 @@  .\" License as specified in the file COPYING that comes with the  .\" vdr distribution.  .\" -.\" $Id: vdr.5 2.28 2012/02/27 11:05:15 kls Exp $ +.\" $Id: vdr.5 2.29 2012/03/10 14:56:01 kls Exp $  .\"  .TH vdr 5 "10 Feb 2008" "1.6" "Video Disk Recorder Files"  .SH NAME @@ -861,7 +861,7 @@ l l.  <event id>     @is a 32 bit unsigned int, uniquely identifying this event  <start time>   @is the time (as a time_t integer) in UTC when this event starts  <duration>     @is the time (in seconds) that this event will take -<table id>     @is a hex number that indicates the table this event is contained in (if this is left empty or 0 this event will not be overwritten or modified by data that comes from the DVB stream) +<table id>     @is a hex number that indicates the table this event is contained in (if this is left empty it will be set to 0x00; and value less than 0x4E it will be treated as if it were 0x4E)  <version>      @is a hex number that indicates the event's version number inside its table (optional, ignored when reading EPG data)  <title>        @is the title of the event  <short text>   @is the short text of the event (typically the name of the episode etc.) @@ -22,7 +22,7 @@   *   * The project's page is at http://www.tvdr.de   * - * $Id: vdr.c 2.33 2012/03/08 09:51:52 kls Exp $ + * $Id: vdr.c 2.34 2012/03/09 09:55:15 kls Exp $   */  #include <getopt.h> @@ -1300,6 +1300,7 @@ Exit:       Setup.Save();       }    cDevice::Shutdown(); +  EpgHandlers.Clear();    PluginManager.Shutdown(true);    cSchedules::Cleanup(true);    ReportEpgBugFixStats(); | 
