[vdr-plugin-dvbsddevice] 01/02: Import Upstream version 2.2.0

Tobias Grimm tiber-guest at moszumanska.debian.org
Sat Jan 7 20:05:46 UTC 2017


This is an automated email from the git hooks/post-receive script.

tiber-guest pushed a commit to branch master
in repository vdr-plugin-dvbsddevice.

commit 966595e23dfd3e4c49da090b42ee36697303d247
Author: Tobias Grimm <git at e-tobi.net>
Date:   Sat Jan 7 20:10:25 2017 +0100

    Import Upstream version 2.2.0
---
 COPYING         | 340 ++++++++++++++++++++++++
 HISTORY         |  65 +++++
 Makefile        |  94 +++++++
 README          |  20 ++
 dvbsddevice.c   |  61 +++++
 dvbsdffdevice.c | 784 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 dvbsdffdevice.h | 115 +++++++++
 dvbsdffosd.c    | 211 +++++++++++++++
 dvbsdffosd.h    |  22 ++
 9 files changed, 1712 insertions(+)

diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..f90922e
--- /dev/null
+++ b/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/HISTORY b/HISTORY
new file mode 100644
index 0000000..68e3e6d
--- /dev/null
+++ b/HISTORY
@@ -0,0 +1,65 @@
+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/Makefile b/Makefile
new file mode 100644
index 0000000..ddffdc1
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,94 @@
+#
+# Makefile for a Video Disk Recorder plugin
+#
+# $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.
+# 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/README b/README
new file mode 100644
index 0000000..9b1280f
--- /dev/null
+++ b/README
@@ -0,0 +1,20 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Written by:                  Klaus Schmidinger <Klaus.Schmidinger at tvdr.de>
+
+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/dvbsddevice.c b/dvbsddevice.c
new file mode 100644
index 0000000..5c21a8d
--- /dev/null
+++ b/dvbsddevice.c
@@ -0,0 +1,61 @@
+/*
+ * 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 4.0 2015/02/17 13:11:55 kls Exp $
+ */
+
+#include <getopt.h>
+#include <vdr/plugin.h>
+#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/dvbsdffdevice.c b/dvbsdffdevice.c
new file mode 100644
index 0000000..2d98fb6
--- /dev/null
+++ b/dvbsdffdevice.c
@@ -0,0 +1,784 @@
+/*
+ * 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 4.0 2014/03/15 12:35:21 kls Exp $
+ */
+
+#include "dvbsdffdevice.h"
+#include <errno.h>
+#include <limits.h>
+#include <linux/videodev2.h>
+#include <linux/dvb/audio.h>
+#include <linux/dvb/dmx.h>
+#include <linux/dvb/video.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <vdr/eitscan.h>
+#include <vdr/transfer.h>
+#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 <marco at lordzodiac.de> 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/dvbsdffdevice.h b/dvbsdffdevice.h
new file mode 100644
index 0000000..b1e4be5
--- /dev/null
+++ b/dvbsdffdevice.h
@@ -0,0 +1,115 @@
+/*
+ * 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 4.0 2014/03/15 12:36:35 kls Exp $
+ */
+
+#ifndef __DVBSDFFDEVICE_H
+#define __DVBSDFFDEVICE_H
+
+#include <vdr/dvbdevice.h>
+#include <vdr/dvbspu.h>
+
+/// 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/dvbsdffosd.c b/dvbsdffosd.c
new file mode 100644
index 0000000..098f27f
--- /dev/null
+++ b/dvbsdffosd.c
@@ -0,0 +1,211 @@
+/*
+ * 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 4.0 2011/04/17 12:55:09 kls Exp $
+ */
+
+#include "dvbsdffosd.h"
+#include <linux/dvb/osd.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/unistd.h>
+#include <vdr/tools.h>
+
+// --- 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/dvbsdffosd.h b/dvbsdffosd.h
new file mode 100644
index 0000000..bb82a7e
--- /dev/null
+++ b/dvbsdffosd.h
@@ -0,0 +1,22 @@
+/*
+ * 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 4.0 2012/12/03 13:43:55 kls Exp $
+ */
+
+#ifndef __DVBSDFFODF_H
+#define __DVBSDFFODF_H
+
+#include <vdr/osd.h>
+
+class cDvbOsdProvider : public cOsdProvider {
+private:
+  int osdDev;
+public:
+  cDvbOsdProvider(int OsdDev);
+  virtual cOsd *CreateOsd(int Left, int Top, uint Level);
+  };
+
+#endif //__DVBSDFFODF_H

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-vdr-dvb/vdr-plugin-dvbsddevice.git



More information about the pkg-vdr-dvb-changes mailing list