[vdr-plugin-play] 01/05: Imported Upstream version 0.0.14+git20130924.0841

Tobias Grimm tiber-guest at moszumanska.debian.org
Sat Feb 14 16:52:41 UTC 2015


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

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

commit 80f73804f9cb17a1f2f13d54c6a91cbd15093159
Author: etobi <git at e-tobi.net>
Date:   Sat Feb 14 17:31:03 2015 +0100

    Imported Upstream version 0.0.14+git20130924.0841
---
 .gitattributes                 |    6 +
 .gitignore                     |   10 +
 .indent.pro                    |   37 +
 AGPL-3.0.txt                   |  662 +++++++++++++++
 ChangeLog                      |   13 +
 Makefile                       |  188 +++++
 Makefile-pre1.7.36             |  169 ++++
 README.txt                     |  122 +++
 TODO.txt                       |   10 +
 misc.h                         |  157 ++++
 play.cpp                       | 1831 ++++++++++++++++++++++++++++++++++++++++
 play_service.h                 |   30 +
 player.c                       |  984 +++++++++++++++++++++
 player.h                       |  160 ++++
 po/.gitignore                  |    3 +
 readdir.c                      |  401 +++++++++
 readdir.h                      |   43 +
 vdr-play-9999-pre1.7.36.ebuild |   63 ++
 video.c                        |  775 +++++++++++++++++
 video.h                        |   62 ++
 20 files changed, 5726 insertions(+)

diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..c6a39fd
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,6 @@
+# gitattributes(5) file
+*.[15]	ident
+*.[ch]	ident
+*.cpp	ident
+*.txt	ident
+Makefile	ident
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e9d5e10
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+# gitignore(5) file
+*.[oa]
+*~
+.*.swp
+.gdb_history
+# work directory
+.chaos
+# generated files
+.dependencies
+libvdr-play.so*
diff --git a/.indent.pro b/.indent.pro
new file mode 100644
index 0000000..1e2b436
--- /dev/null
+++ b/.indent.pro
@@ -0,0 +1,37 @@
+--blank-lines-before-block-comments
+--blank-lines-after-declarations
+--blank-lines-after-procedures
+--no-blank-lines-after-commas
+--braces-on-if-line
+--no-blank-before-sizeof
+--comment-indentation41
+--declaration-comment-column41
+--no-comment-delimiters-on-blank-lines
+--swallow-optional-blank-lines
+--dont-format-comments
+--parameter-indentation4
+--indent-level4
+--line-comments-indentation0
+--cuddle-else
+--cuddle-do-while
+--brace-indent0
+--case-brace-indentation0
+//--start-left-side-of-comments
+--leave-preprocessor-space
+//--continuation-indentation8
+--case-indentation4
+--else-endif-column0
+--no-space-after-casts
+--declaration-indentation1
+--dont-line-up-parentheses
+--no-space-after-function-call-names
+--space-special-semicolon
+--tab-size8
+--use-tabs
+--line-length79
+--comment-line-length79
+--honour-newlines
+--dont-break-procedure-type
+--break-before-boolean-operator
+--continuation-indentation4
+--ignore-newlines
diff --git a/AGPL-3.0.txt b/AGPL-3.0.txt
new file mode 100644
index 0000000..1489b72
--- /dev/null
+++ b/AGPL-3.0.txt
@@ -0,0 +1,662 @@
+                    GNU AFFERO GENERAL PUBLIC LICENSE
+                       Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU Affero General Public License is a free, copyleft license
+for software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+  The licenses for most software and other practical works are
+designed to take away your freedom to share and change the works.  By
+contrast, our General Public Licenses are intended to guarantee your
+freedom to share and change all versions of a program--to make sure it
+remains free software for all its users.
+
+  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
+them 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.
+
+  Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+  A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate.  Many developers of free software are heartened and
+encouraged by the resulting cooperation.  However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+  The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community.  It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server.  Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+  An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals.  This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU Affero General Public
+License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds
+of works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey 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;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further restriction,
+you may remove that term.  If a license document contains a further
+restriction but permits relicensing or conveying under this License, you
+may add to a covered work material governed by the terms of that license
+document, provided that the further restriction does not survive such
+relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Remote Network Interaction; Use with the GNU General Public License.
+
+  Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software.  This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+  Notwithstanding any other provision of this License, you have permission
+to link or combine any covered work with a work licensed under version 3
+of the GNU General Public License into a single combined work, and to
+convey the resulting work.  The terms of this License will continue to
+apply to the part which is the covered work, but the work with which it is
+combined will remain governed by version 3 of the GNU General Public
+License.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero 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 that a certain numbered version of the GNU Affero
+General Public License "or any later version" applies to it, you have
+the option of following the terms and conditions either of that
+numbered version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number
+of the GNU Affero General Public License, you may choose any version
+ever published by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that
+proxy's public statement of acceptance of a version permanently
+authorizes you to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  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.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+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.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                       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
+state 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 Affero General Public License as
+    published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source.  For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code.  There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..611d60c
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,13 @@
+User Dennis Bendlin 
+Date: Thu Sep  5 12:02:29 CEST 2013
+
+    Add replay info.
+
+User johns
+Date: Mon Aug 26 10:12:45 CEST 2013
+
+    Allow filenames starting with '-'.
+    Add dia show image/picture viewer.
+    Add player thread for events and pipe output.
+    Support DVDNAV buttons.
+    Colorkey becomes parameter (f.e. mplayer2).
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..ac85bc6
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,188 @@
+#
+# Makefile for a Video Disk Recorder plugin
+#
+# $Id$
+
+# 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 = play
+
+### Configuration (edit this for your needs)
+
+    # support avfs a virtual file system
+# FIXME: AVFS isn't working, corrupts memory
+#AVFS ?= $(shell test -x /usr/bin/avfs-config && echo 1)
+    # use ffmpeg libswscale
+SWSCALE ?= $(shell pkg-config --exists libswscale && echo 1)
+    # support png images
+PNG ?= $(shell pkg-config --exists libpng && echo 1)
+    # support jpg images
+JPG ?= $(shell test -r /usr/include/jpeglib.h && echo 1)
+
+CONFIG := #-DDEBUG			# uncomment to build DEBUG
+
+ifeq ($(AVFS),1)
+CONFIG += -DUSE_AVFS
+_CFLAGS += $(shell /usr/bin/avfs-config --cflags)
+LIBS += $(shell /usr/bin/avfs-config --libs)
+endif
+ifeq ($(SWSCALE),1)
+CONFIG += -DUSE_SWSCALE
+_CFLAGS += $(shell pkg-config --cflags libswscale)
+LIBS += $(shell pkg-config --libs libswscale)
+endif
+ifeq ($(PNG),1)
+CONFIG += -DUSE_PNG
+_CFLAGS += $(shell pkg-config --cflags libpng)
+LIBS += $(shell pkg-config --libs libpng)
+endif
+ifeq ($(JPG),1)
+CONFIG += -DUSE_JPG
+_CFLAGS += -I/usr/include
+LIBS += -Ljpeg
+endif
+
+_CFLAGS += $(shell pkg-config --cflags xcb xcb-image xcb-keysyms xcb-icccm)
+LIBS += -lrt $(shell pkg-config --libs xcb xcb-image xcb-keysyms xcb-icccm)
+
+### The version number of this plugin (taken from the main source file):
+
+VERSION = $(shell grep 'static const char \*const VERSION *=' $(PLUGIN).cpp | awk '{ print $$7 }' | sed -e 's/[";]//g')
+GIT_REV = $(shell git describe --always 2>/dev/null)
+
+### 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 --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
+LIBDIR = $(call PKGCFG,libdir)
+LOCDIR = $(call PKGCFG,locdir)
+PLGCFG = $(call PKGCFG,plgcfg)
+#
+TMPDIR ?= /tmp
+
+### The compiler options:
+
+export CFLAGS	= $(call PKGCFG,cflags)
+export CXXFLAGS = $(call PKGCFG,cxxflags)
+
+ifeq ($(CFLAGS),)
+$(error CFLAGS not set)
+endif
+ifeq ($(CXXFLAGS),)
+$(error CXXFLAGS not set)
+endif
+
+### 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)"' -D_GNU_SOURCE $(CONFIG) \
+	$(if $(GIT_REV), -DGIT_REV='"$(GIT_REV)"')
+
+### Make it standard
+
+override CXXFLAGS += $(_CFLAGS) $(DEFINES) $(INCLUDES) \
+    -g -W -Wall -Wextra -Winit-self -Werror=overloaded-virtual
+override CFLAGS	  += $(_CFLAGS) $(DEFINES) $(INCLUDES) \
+    -g -W -Wall -Wextra -Winit-self -Wdeclaration-after-statement
+
+### The object files (add further files here):
+
+OBJS = $(PLUGIN).o player.o video.o readdir.o
+
+SRCS = $(wildcard $(OBJS:.o=.c)) $(PLUGIN).cpp
+
+### The main target:
+
+all: $(SOFILE) i18n
+
+### Dependencies:
+
+MAKEDEP = $(CXX) -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): Makefile
+	@$(MAKEDEP) $(CXXFLAGS) $(SRCS) > $@
+
+-include $(DEPFILE)
+
+### Internationalization (I18N):
+
+PODIR	  = po
+I18Npo	  = $(wildcard $(PODIR)/*.po)
+I18Nmo	  = $(addsuffix .mo, $(foreach file, $(I18Npo), $(basename $(file))))
+I18Nmsgs  = $(addprefix $(DESTDIR)$(LOCDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
+I18Npot	  = $(PODIR)/$(PLUGIN).pot
+
+%.mo: %.po
+	msgfmt -c -o $@ $<
+
+$(I18Npot): $(SRCS)
+	xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP \
+	-k_ -k_N --package-name=vdr-$(PLUGIN) --package-version=$(VERSION) \
+	--msgid-bugs-address='<see README>' -o $@ `ls $^`
+
+%.po: $(I18Npot)
+	msgmerge -U --no-wrap --no-location --backup=none -q -N $@ $<
+	@touch $@
+
+$(I18Nmsgs): $(DESTDIR)$(LOCDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
+	install -D -m644 $< $@
+
+.PHONY: i18n
+i18n: $(I18Nmo) $(I18Npot)
+
+install-i18n: $(I18Nmsgs)
+
+### Targets:
+
+$(OBJS): Makefile
+
+$(SOFILE): $(OBJS)
+	$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) $(LIBS) -o $@
+
+install-lib: $(SOFILE)
+	install -D $^ $(DESTDIR)$(LIBDIR)/$^.$(APIVERSION)
+
+install: install-lib install-i18n
+
+dist: $(I18Npo) clean
+	@-rm -rf $(TMPDIR)/$(ARCHIVE)
+	@mkdir $(TMPDIR)/$(ARCHIVE)
+	@cp -a * $(TMPDIR)/$(ARCHIVE)
+	@tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE)
+	@-rm -rf $(TMPDIR)/$(ARCHIVE)
+	@echo Distribution package created as $(PACKAGE).tgz
+
+clean:
+	@-rm -f $(PODIR)/*.mo $(PODIR)/*.pot
+	@-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~
+
+## Private Targets:
+
+HDRS=	$(wildcard *.h)
+
+indent:
+	for i in $(SRCS) $(HDRS); do \
+		indent $$i; \
+		unexpand -a $$i | sed -e s/constconst/const/ > $$i.up; \
+		mv $$i.up $$i; \
+	done
diff --git a/Makefile-pre1.7.36 b/Makefile-pre1.7.36
new file mode 100644
index 0000000..81269ba
--- /dev/null
+++ b/Makefile-pre1.7.36
@@ -0,0 +1,169 @@
+#
+# Makefile for a Video Disk Recorder plugin
+#
+# $Id: 14fbc0969bcd384cea8b8b420b49d49a6c3ef381 $
+
+# 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 = play
+
+### The version number of this plugin (taken from the main source file):
+
+VERSION = $(shell grep 'static const char \*const VERSION *=' $(PLUGIN).cpp | awk '{ print $$7 }' | sed -e 's/[";]//g')
+GIT_REV = $(shell git describe --always 2>/dev/null)
+
+### Configuration (edit this for your needs)
+
+CONFIG := #-DDEBUG
+	# autodetect: use a virtual file system (don't use corrupts memory)
+#CONFIG += $(shell test -x /usr/bin/avfs-config && echo "-DUSE_AVFS")
+	# autodetect: use ffmpeg software scale  
+CONFIG += $(shell pkg-config --exists libswscale && echo "-DUSE_SWSCALE")
+	# autodetect: support png images
+CONFIG += $(shell pkg-config --exists libpng && echo "-DUSE_PNG")
+	# autodetect: support jpg images
+CONFIG += $(shell test -r /usr/include/jpeglib.h && echo "-DUSE_JPG")
+
+### The C++ compiler and options:
+
+CC	?= gcc
+CXX	?= g++
+CFLAGS	?= -g -O2 -W -Wall -Wextra -Winit-self \
+	-Wdeclaration-after-statement -fPIC
+CXXFLAGS ?= -g -O2 -W -Wall -Wextra -Werror=overloaded-virtual -fPIC
+
+### 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, Defines and dependencies (add further entries here):
+
+INCLUDES += -I$(VDRDIR)/include
+
+DEFINES += $(CONFIG) -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' \
+	$(if $(GIT_REV), -DGIT_REV='"$(GIT_REV)"')
+
+_CFLAGS := $(DEFINES) $(INCLUDES) \
+	$(shell pkg-config --cflags xcb xcb-event xcb-keysyms xcb-icccm \
+		xcb-image) \
+	$(if $(findstring USE_AVFS,$(CONFIG)), `avfs-config --cflags`) \
+	$(if $(findstring USE_SWSCALE,$(CONFIG)), \
+		`pkg-config --cflags libswscale`) \
+	$(if $(findstring USE_PNG,$(CONFIG)), `pkg-config --cflags libpng`)
+
+#_CFLAGS  += -Werror
+override CFLAGS	  += $(_CFLAGS)
+override CXXFLAGS += $(_CFLAGS)
+
+_LIBS := $(shell pkg-config --libs xcb xcb-keysyms xcb-event xcb-icccm \
+		xcb-image) \
+	$(if $(findstring USE_AVFS,$(CONFIG)), `avfs-config --libs`) \
+	$(if $(findstring USE_SWSCALE,$(CONFIG)), \
+		`pkg-config --libs libswscale`) \
+	$(if $(findstring USE_PNG,$(CONFIG)), `pkg-config --libs libpng`) \
+	$(if $(findstring USE_JPG,$(CONFIG)), -ljpeg)
+
+override LIBS += $(_LIBS)
+
+### The object files (add further files here):
+
+OBJS = $(PLUGIN).o player.o video.o readdir.o
+SRCS = $(wildcard $(OBJS:.o=.c)) $(PLUGIN).cpp
+
+### The main target:
+
+all: libvdr-$(PLUGIN).so i18n
+
+### Implicit rules:
+#
+#%.o: %.cpp
+#	$(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
+
+### Dependencies:
+
+MAKEDEP = $(CC) -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): Makefile
+	@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(SRCS) >$@
+
+$(OBJS): Makefile
+
+-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 *.cpp)  $(wildcard *.c)
+	xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP \
+	-k_ -k_N --package-name=VDR --package-version=$(VDRVERSION) \
+	--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) Makefile
+	$(CXX) $(CXXFLAGS) $(LDFLAGS) -shared -fPIC $(OBJS) -o $@ $(LIBS)
+	@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
+
+install:	libvdr-$(PLUGIN).so
+	cp --remove-destination libvdr-$(PLUGIN).so \
+		/usr/lib/vdr/plugins/libvdr-$(PLUGIN).so.$(APIVERSION)
+
+HDRS=	$(wildcard *.h)
+
+indent:
+	for i in $(wildcard $(OBJS:.o=.c)) $(HDRS); do \
+		indent $$i; unexpand -a $$i > $$i.up; mv $$i.up $$i; \
+	done
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..a9b6812
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,122 @@
+ at file README.txt		@brief A play plugin for VDR
+
+Copyright (c) 2012 by Johns.  All Rights Reserved.
+
+Contributor(s):
+
+License: AGPLv3
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License.
+
+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 Affero General Public License for more details.
+
+$Id$
+
+A play plugin for VDR
+
+
+To compile you must have the 'requires' installed.
+
+Good luck
+johns
+
+Quickstart:
+-----------
+
+Just type make and use.
+
+Install:
+--------
+	1a) git
+
+	git clone git://projects.vdr-developer.org/vdr-plugin-play.git
+	cd vdr-plugin-play
+	make VDRDIR=<path-to-your-vdr-files> LIBDIR=.
+	gentoo: make VDRDIR=/usr/include/vdr LIBDIR=.
+
+	2a) tarball
+
+	Download latest version from:
+	    http://projects.vdr-developer.org/projects/plg-play/files
+
+	tar vxf vdr-play-*.tar.bz2
+	cd play-*
+	make VDRDIR=<path-to-your-vdr-files> LIBDIR=.
+
+	You can edit Makefile to enable/disable some features.
+
+Setup:	environment
+------
+	Following is supported:
+
+	DISPLAY=:0.0
+		x11 display name
+
+Setup: /etc/vdr/setup.conf
+------
+	Following is supported:
+
+	play.HideMainMenuEntry = 0
+	0 = show play main menu entry, 1 = hide entry
+
+Commandline:
+------------
+
+	Use vdr -h to see the command line arguments supported by the plugin.
+
+    -a audio_device
+
+	Selects audio output module and device.
+
+SVDRP:
+------
+
+	Use 'svdrpsend.pl plug play HELP'
+	or 'svdrpsend plug play HELP' to see the SVDRP commands help
+	and which are supported by the plugin.
+
+Keymacros:
+----------
+
+	See keymacros.conf how to setup the macros.
+
+	This are the supported key sequences:
+
+Running:
+--------
+
+Known Bugs:
+-----------
+
+Requires:
+---------
+	x11-libs/libxcb,
+		X C-language Bindings library
+		http://xcb.freedesktop.org
+	x11-libs/xcb-util,
+	x11-libs/xcb-util-image,
+	x11-libs/xcb-util-keysyms,
+	x11-libs/xcb-util-wm,
+		X C-language Bindings library
+		http://xcb.freedesktop.org
+		Only versions >= 0.3.8 are good supported
+
+	media-video/mplayer
+		Media Player for Linux
+		http://www.mplayerhq.hu/
+    or
+	media-video/mplayer2
+		Media Player for Linux
+		http://www.mplayer2.org/
+
+	GNU Make 3.xx
+		http://www.gnu.org/software/make/make.html
+
+Optional:
+---------
diff --git a/TODO.txt b/TODO.txt
new file mode 100644
index 0000000..2293f6a
--- /dev/null
+++ b/TODO.txt
@@ -0,0 +1,10 @@
+video:
+	add support for _NET_WM_STATE = _NET_WM_STATE_FULLSCREEN
+
+mplayer:
+	Can autodetect color key.
+	ffodivxvdpau not always supported.
+
+Feature requests:
+	resume
+	service interface to play files
diff --git a/misc.h b/misc.h
new file mode 100644
index 0000000..3ced30c
--- /dev/null
+++ b/misc.h
@@ -0,0 +1,157 @@
+///
+///	@file misc.h	@brief Misc function header file
+///
+///	Copyright (c) 2009 - 2012 by Lutz Sammer.  All Rights Reserved.
+///
+///	Contributor(s):
+///		Copied from vdr-softhddevice.
+///
+///	License: AGPLv3
+///
+///	This program is free software: you can redistribute it and/or modify
+///	it under the terms of the GNU Affero General Public License as
+///	published by the Free Software Foundation, either version 3 of the
+///	License.
+///
+///	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 Affero General Public License for more details.
+///
+///	$Id$
+//////////////////////////////////////////////////////////////////////////////
+
+/// @addtogroup misc
+/// @{
+
+#include <syslog.h>
+#include <stdarg.h>
+#include <time.h>			// clock_gettime
+
+//////////////////////////////////////////////////////////////////////////////
+//	Defines
+//////////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////////
+//	Declares
+//////////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////////
+//	Variables
+//////////////////////////////////////////////////////////////////////////////
+
+extern int SysLogLevel;			///< how much information wanted
+
+//////////////////////////////////////////////////////////////////////////////
+//	Prototypes
+//////////////////////////////////////////////////////////////////////////////
+
+static inline void Syslog(const int, const char *format, ...)
+    __attribute__ ((format(printf, 2, 3)));
+
+//////////////////////////////////////////////////////////////////////////////
+//	Inlines
+//////////////////////////////////////////////////////////////////////////////
+
+#ifdef DEBUG
+#define DebugLevel 4			/// private debug level
+#else
+#define DebugLevel 0			/// private debug level
+#endif
+
+/**
+**	Syslog output function.
+**
+**	- 0	fatal errors and errors
+**	- 1	warnings
+**	- 2	info
+**	- 3	important debug and fixme's
+*/
+static inline void Syslog(const int level, const char *format, ...)
+{
+    if (SysLogLevel > level || DebugLevel > level) {
+	va_list ap;
+
+	va_start(ap, format);
+	vsyslog(LOG_ERR, format, ap);
+	va_end(ap);
+    }
+}
+
+/**
+**	Show error.
+*/
+#define Error(fmt...)	Syslog(0, fmt)
+
+/**
+**	Show fatal error.
+*/
+#define Fatal(fmt...)	do { Error(fmt); abort(); } while (0)
+
+/**
+**	Show warning.
+*/
+#define Warning(fmt...)	Syslog(1, fmt)
+
+/**
+**	Show info.
+*/
+#define Info(fmt...)	Syslog(2, fmt)
+
+/**
+**	Show debug.
+*/
+#ifdef DEBUG
+#define Debug(level, fmt...)	Syslog(level, fmt)
+#else
+#define Debug(level, fmt...)		/* disabled */
+#endif
+
+#ifndef AV_NOPTS_VALUE
+#define AV_NOPTS_VALUE INT64_C(0x8000000000000000)
+#endif
+
+/**
+**	Nice time-stamp string.
+**
+**	@param ts	dvb time stamp
+*/
+static inline const char *Timestamp2String(int64_t ts)
+{
+    static char buf[4][16];
+    static int idx;
+
+    if (ts == (int64_t) AV_NOPTS_VALUE) {
+	return "--:--:--.---";
+    }
+    idx = (idx + 1) % 3;
+    snprintf(buf[idx], sizeof(buf[idx]), "%2d:%02d:%02d.%03d",
+	(int)(ts / (90 * 3600000)), (int)((ts / (90 * 60000)) % 60),
+	(int)((ts / (90 * 1000)) % 60), (int)((ts / 90) % 1000));
+
+    return buf[idx];
+}
+
+/**
+**	Get ticks in ms.
+**
+**	@returns ticks in ms,
+*/
+static inline uint32_t GetMsTicks(void)
+{
+#ifdef CLOCK_MONOTONIC
+    struct timespec tspec;
+
+    clock_gettime(CLOCK_MONOTONIC, &tspec);
+    return (tspec.tv_sec * 1000) + (tspec.tv_nsec / (1000 * 1000));
+#else
+    struct timeval tval;
+
+    if (gettimeofday(&tval, NULL) < 0) {
+	return 0;
+    }
+    return (tval.tv_sec * 1000) + (tval.tv_usec / 1000);
+#endif
+}
+
+/// @}
diff --git a/play.cpp b/play.cpp
new file mode 100644
index 0000000..269426d
--- /dev/null
+++ b/play.cpp
@@ -0,0 +1,1831 @@
+///
+///	@file play.cpp		@brief A play plugin for VDR.
+///
+///	Copyright (c) 2012, 2013 by Johns.  All Rights Reserved.
+///
+///	Contributor(s): Dennis Bendlin
+///
+///	License: AGPLv3
+///
+///	This program is free software: you can redistribute it and/or modify
+///	it under the terms of the GNU Affero General Public License as
+///	published by the Free Software Foundation, either version 3 of the
+///	License.
+///
+///	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 Affero General Public License for more details.
+///
+///	$Id$
+//////////////////////////////////////////////////////////////////////////////
+
+#include <vdr/interface.h>
+#include <vdr/plugin.h>
+#include <vdr/player.h>
+#include <vdr/osd.h>
+#include <vdr/shutdown.h>
+#include <vdr/status.h>
+#include <vdr/videodir.h>
+
+#ifdef HAVE_CONFIG
+#include "config.h"
+#endif
+
+#include "play_service.h"
+extern "C"
+{
+#include "readdir.h"
+#include "video.h"
+#include "player.h"
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+    /// vdr-plugin version number.
+    /// Makefile extracts the version number for generating the file name
+    /// for the distribution archive.
+static const char *const VERSION = "0.0.14"
+#ifdef GIT_REV
+    "-GIT" GIT_REV
+#endif
+    ;
+
+    /// vdr-plugin description.
+static const char *const DESCRIPTION = trNOOP("A play plugin");
+
+    /// vdr-plugin text of main menu entry
+static const char *MAINMENUENTRY = trNOOP("Play");
+
+//////////////////////////////////////////////////////////////////////////////
+
+static char ConfigHideMainMenuEntry;	///< hide main menu entry
+char ConfigDisableRemote;		///< disable remote during external play
+
+static volatile int DoMakePrimary;	///< switch primary device to this
+
+//////////////////////////////////////////////////////////////////////////////
+//	C Callbacks
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+**	Device plugin remote class.
+*/
+class cMyRemote:public cRemote
+{
+  public:
+
+    /**
+    **	Soft device remote class constructor.
+    **
+    **	@param name	remote name
+    */
+    cMyRemote(const char *name):cRemote(name)
+    {
+    }
+
+    /**
+    **	Put keycode into vdr event queue.
+    **
+    **	@param code	key code
+    **	@param repeat	flag key repeated
+    **	@param release	flag key released
+    */
+    bool Put(const char *code, bool repeat = false, bool release = false) {
+	return cRemote::Put(code, repeat, release);
+    }
+};
+
+/**
+**	Feed key press as remote input (called from C part).
+**
+**	@param keymap	target keymap "XKeymap" name
+**	@param key	pressed/released key name
+**	@param repeat	repeated key flag
+**	@param release	released key flag
+*/
+extern "C" void FeedKeyPress(const char *keymap, const char *key, int repeat,
+    int release)
+{
+    cRemote *remote;
+    cMyRemote *csoft;
+
+    if (!keymap || !key) {
+	return;
+    }
+    // find remote
+    for (remote = Remotes.First(); remote; remote = Remotes.Next(remote)) {
+	if (!strcmp(remote->Name(), keymap)) {
+	    break;
+	}
+    }
+    // if remote not already exists, create it
+    if (remote) {
+	csoft = (cMyRemote *) remote;
+    } else {
+	dsyslog("[play]%s: remote '%s' not found\n", __FUNCTION__, keymap);
+	csoft = new cMyRemote(keymap);
+    }
+
+    //dsyslog("[play]%s %s, %s\n", __FUNCTION__, keymap, key);
+    if (key[1]) {			// no single character
+	csoft->Put(key, repeat, release);
+    } else if (!csoft->Put(key, repeat, release)) {
+	cRemote::Put(KBDKEY(key[0]));	// feed it for edit mode
+    }
+}
+
+/**
+**	Disable remotes.
+*/
+void RemoteDisable(void)
+{
+    dsyslog("[play]: remote disabled\n");
+    cRemote::SetEnabled(false);
+}
+
+/**
+**	Enable remotes.
+*/
+void RemoteEnable(void)
+{
+    dsyslog("[play]: remote enabled\n");
+    cRemote::SetEnabled(false);
+    cRemote::SetEnabled(true);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//	C Callbacks for diashow
+//////////////////////////////////////////////////////////////////////////////
+
+#if 0
+
+/**
+**	Draw rectangle.
+*/
+extern "C" void DrawRectangle(int x1, int y1, int x2, int y2, uint32_t argb)
+{
+    //GlobalDiashow->Osd->DrawRectangle(x1, y1, x2, y2, argb);
+}
+
+/**
+**	Draw text.
+**
+**	@param FIXME:
+*/
+extern "C" void DrawText(int x, int y, const char *s, uint32_t fg, uint32_t bg,
+    int w, int h, int align)
+{
+    const cFont *font;
+
+    font = cFont::GetFont(fontOsd);
+    //GlobalDiashow->Osd->DrawText(x, y, s, fg, bg, font, w, h, align);
+}
+
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//	cPlayer
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+**	Player class.
+*/
+class cMyPlayer:public cPlayer
+{
+  private:
+    char *FileName;			///< file to play
+
+  public:
+     cMyPlayer(const char *);		///< player constructor
+     virtual ~ cMyPlayer();		///< player destructor
+    void Activate(bool);		///< player attached/detached
+    /// get current replay mode
+    virtual bool GetReplayMode(bool &, bool &, int &);
+};
+
+/**
+**	Player constructor.
+**
+**	@param filename	path and name of file to play
+*/
+cMyPlayer::cMyPlayer(const char *filename)
+:cPlayer(pmExtern_THIS_SHOULD_BE_AVOIDED)
+{
+    dsyslog("[play]%s: '%s'\n", __FUNCTION__, filename);
+
+    PlayerSetVolume(cDevice::CurrentVolume());
+    dsyslog("[play]: initial volume %d\n", cDevice::CurrentVolume());
+
+    FileName = strdup(filename);
+    if (ConfigDisableRemote) {
+	RemoteDisable();
+    }
+}
+
+/**
+**	Player destructor.
+*/
+cMyPlayer::~cMyPlayer()
+{
+    dsyslog("[play]%s: end\n", __FUNCTION__);
+
+    PlayerStop();
+    free(FileName);
+    if (ConfigDisableRemote) {
+	RemoteEnable();
+    }
+    // FIXME: wait until primary device is switched?
+    dsyslog("[play]: device %d->%d\n",
+	cDevice::PrimaryDevice()->DeviceNumber(), DoMakePrimary);
+}
+
+/**
+**	Player attached or detached.
+**
+**	@param on	flag turn player on or off
+*/
+void cMyPlayer::Activate(bool on)
+{
+    dsyslog("[play]%s: '%s' %d\n", __FUNCTION__, FileName, on);
+
+    if (on) {
+	PlayerStart(FileName);
+    } else {
+	PlayerStop();
+    }
+}
+
+/**
+**	Get current replay mode.
+*/
+bool cMyPlayer::GetReplayMode(bool & play, bool & forward, int &speed)
+{
+    play = !PlayerPaused;
+    forward = true;
+    if (PlayerSpeed == 1) {
+	speed = -1;
+    } else {
+	speed = PlayerSpeed;
+    }
+    return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//	cStatus
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+**	Status class.
+**
+**	To get volume changes.
+*/
+class cMyStatus:public cStatus
+{
+  private:
+    int Volume;				///< current volume
+
+  public:
+    cMyStatus(void);			///< my status constructor
+
+  protected:
+    virtual void SetVolume(int, bool);	///< volume changed
+};
+
+cMyStatus *Status;			///< status monitor for volume
+
+/**
+**	Status constructor.
+*/
+cMyStatus::cMyStatus(void)
+{
+    Volume = cDevice::CurrentVolume();
+
+    dsyslog("[play]: status volume %d\n", Volume);
+}
+
+/**
+**	Called if volume is set.
+*/
+void cMyStatus::SetVolume(int volume, bool absolute)
+{
+    dsyslog("[play]: volume %d %s\n", volume, absolute ? "abs" : "rel");
+
+    if (absolute) {
+	Volume = volume;
+    } else {
+	Volume += volume;
+    }
+
+    PlayerSetVolume(Volume);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//	cControl
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+**	Our player control class.
+*/
+class cMyControl:public cControl
+{
+  private:
+    cMyPlayer * Player;			///< our player
+    cSkinDisplayReplay *Display;	///< our osd display
+    void ShowReplayMode(void);		///< display replay mode
+    void ShowProgress(void);		///< display progress bar
+    virtual void Show(void);		///< show replay control
+    virtual void Hide(void);		///< hide replay control
+    bool infoVisible;			///< RecordingInfo visible
+    time_t timeoutShow;			///< timeout shown control
+
+  public:
+    cMyControl(const char *);		///< player control constructor
+    virtual ~ cMyControl();		///< player control destructor
+
+    virtual eOSState ProcessKey(eKeys);	///< handle keyboard input
+
+};
+
+/**
+**	Show replay mode.
+*/
+void cMyControl::ShowReplayMode(void)
+{
+    dsyslog("[play]%s: %d - %d\n", __FUNCTION__, Setup.ShowReplayMode,
+	cOsd::IsOpen());
+
+    // use vdr setup
+    if (Display || (Setup.ShowReplayMode && !cOsd::IsOpen())) {
+	bool play;
+	bool forward;
+	int speed;
+
+	if (GetReplayMode(play, forward, speed)) {
+	    if (!Display) {
+		// no need to show normal play
+		if (play && forward && speed == 1) {
+		    return;
+		}
+		Display = Skins.Current()->DisplayReplay(true);
+	    }
+	    Display->SetMode(play, forward, speed);
+	}
+    }
+}
+
+/**
+**	Show progress.
+*/
+void cMyControl::ShowProgress(void)
+{
+    if (Display || (!cOsd::IsOpen())) {
+	bool play;
+	bool forward;
+	int speed;
+
+	if (GetReplayMode(play, forward, speed)) {
+	    if (!Display) {
+		Display = Skins.Current()->DisplayReplay(false);
+	    }
+
+	    if (!infoVisible) {
+		infoVisible = true;
+		timeoutShow = time(0) + Setup.ChannelInfoTime;
+		PlayerGetLength();
+		PlayerGetMetaTitle();
+		PlayerGetFilename();
+	    }
+
+	    PlayerGetCurrentPosition();
+	    if (strcmp(PlayerTitle, "") != 0) {
+		Display->SetTitle(PlayerTitle);
+	    } else {
+		Display->SetTitle(PlayerFilename);
+	    }
+	    Display->SetProgress(PlayerCurrent, PlayerTotal);
+	    Display->SetMode(play, forward, speed);
+	    Display->SetCurrent(IndexToHMSF(PlayerCurrent, false, 1));
+	    Display->SetTotal(IndexToHMSF(PlayerTotal, false, 1));
+	}
+	SetNeedsFastResponse(true);
+	Skins.Flush();
+    }
+}
+
+/**
+**	Show control.
+*/
+void cMyControl::Show(void)
+{
+    dsyslog("[play]%s:\n", __FUNCTION__);
+    if (Setup.ShowReplayMode)
+	ShowReplayMode();
+    else
+	ShowProgress();
+}
+
+/**
+**	Control constructor.
+**
+**	@param filename	pathname of file to play.
+*/
+cMyControl::cMyControl(const char *filename)
+:cControl(Player = new cMyPlayer(filename))
+{
+    Display = NULL;
+    Status = new cMyStatus;		// start monitoring volume
+    infoVisible = false;
+
+    //LastSkipKey = kNone;
+    //LastSkipSeconds = REPLAYCONTROLSKIPSECONDS;
+    //LastSkipTimeout.Set(0);
+    cStatus::MsgReplaying(this, filename, filename, true);
+
+    cDevice::PrimaryDevice()->ClrAvailableTracks(true);
+}
+
+/**
+**	Control destructor.
+*/
+cMyControl::~cMyControl()
+{
+    dsyslog("[play]%s\n", __FUNCTION__);
+
+    delete Player;
+
+    //delete Display;
+    delete Status;
+
+    Hide();
+    cStatus::MsgReplaying(this, NULL, NULL, false);
+    //Stop();
+}
+
+/**
+**	Hide control.
+*/
+void cMyControl::Hide(void)
+{
+    dsyslog("[play]%s:\n", __FUNCTION__);
+
+    if (Display) {
+	delete Display;
+
+	Display = NULL;
+	SetNeedsFastResponse(false);
+    }
+}
+
+/**
+**	Process keyboard input.
+**
+**	@param key	pressed or releaded key
+*/
+eOSState cMyControl::ProcessKey(eKeys key)
+{
+    eOSState state;
+
+    if (key != kNone) {
+	dsyslog("[play]%s: key=%d\n", __FUNCTION__, key);
+    }
+
+    if (!PlayerIsRunning()) {		// check if player is still alive
+	dsyslog("[play]: player died\n");
+	Hide();
+	//FIXME: Stop();
+	cControl::Shutdown();
+	return osEnd;
+    }
+
+    if (infoVisible) {			// if RecordingInfo visible then update
+	if (timeoutShow && time(0) > timeoutShow) {
+	    Hide();
+	    timeoutShow = 0;
+	    infoVisible = false;
+	} else
+	    ShowProgress();
+    }
+    //state=cOsdMenu::ProcessKey(key);
+    state = osContinue;
+    switch ((int)key) {			// cast to shutup g++ warnings
+	case kUp:
+	    if (PlayerDvdNav) {
+		PlayerSendDvdNavUp();
+		break;
+	    }
+	case kPlay:
+	    Hide();
+	    if (PlayerSpeed != 1) {
+		PlayerSendSetSpeed(PlayerSpeed = 1);
+	    }
+	    if (PlayerPaused) {
+		PlayerSendPause();
+		PlayerPaused ^= 1;
+	    }
+	    Show();
+	    break;
+
+	case kDown:
+	    if (PlayerDvdNav) {
+		PlayerSendDvdNavDown();
+		break;
+	    }
+	case kPause:
+	    PlayerSendPause();
+	    PlayerPaused ^= 1;
+	    Show();
+	    break;
+
+	case kFastRew | k_Release:
+	case kLeft | k_Release:
+	    if (Setup.MultiSpeedMode) {
+		break;
+	    }
+	    // FIXME:
+	    break;
+	case kLeft:
+	    if (PlayerDvdNav) {
+		PlayerSendDvdNavLeft();
+		break;
+	    }
+	case kFastRew:
+	    if (PlayerSpeed > 1) {
+		PlayerSendSetSpeed(PlayerSpeed /= 2);
+	    } else {
+		PlayerSendSeek(-10);
+	    }
+	    Show();
+	    break;
+	case kRight:
+	    if (PlayerDvdNav) {
+		PlayerSendDvdNavRight();
+		break;
+	    }
+	case kFastFwd:
+	    if (PlayerSpeed < 32) {
+		PlayerSendSetSpeed(PlayerSpeed *= 2);
+	    }
+	    Show();
+	    break;
+
+	case kRed:
+	    // FIXME: TimeSearch();
+	    break;
+
+#ifdef USE_JUMPINGSECONDS
+	case kGreen | k_Repeat:
+	    PlayerSendSeek(-Setup.JumpSecondsRepeat);
+	    break;
+	case kGreen:
+	    PlayerSendSeek(-Setup.JumpSeconds);
+	    break;
+	case k1 | k_Repeat:
+	case k1:
+	    PlayerSendSeek(-Setup.JumpSecondsSlow);
+	    break;
+	case k3 | k_Repeat:
+	case k3:
+	    PlayerSendSeek(Setup.JumpSecondsSlow);
+	    break;
+	case kYellow | k_Repeat:
+	    PlayerSendSeek(Setup.JumpSecondsRepeat);
+	    break;
+	case kYellow:
+	    PlayerSendSeek(Setup.JumpSeconds);
+	    break;
+#else
+	case kGreen | k_Repeat:
+	case kGreen:
+	    PlayerSendSeek(-60);
+	    break;
+	case kYellow | k_Repeat:
+	case kYellow:
+	    PlayerSendSeek(+60);
+	    break;
+#endif /* JUMPINGSECONDS */
+#ifdef USE_LIEMIKUUTIO
+#ifndef USE_JUMPINGSECONDS
+	case k1 | k_Repeat:
+	case k1:
+	    PlayerSendSeek(-20);
+	    break;
+	case k3 | k_Repeat:
+	case k3:
+	    PlayerSendSeek(+20);
+	    break;
+#endif /* JUMPINGSECONDS */
+#endif
+
+	case kStop:
+	case kBlue:
+	    dsyslog("[play]: player stopped\n");
+	    Hide();
+	    // FIXME: Stop();
+	    cControl::Shutdown();
+	    return osEnd;
+
+	case kOk:
+	    if (PlayerDvdNav) {
+		PlayerSendDvdNavSelect();
+		// FIXME: PlayerDvdNav = 0;
+		break;
+	    }
+	    if (infoVisible) {
+		Hide();
+		infoVisible = false;
+	    } else
+		Show();
+	    break;
+
+	case kBack:
+	    if (PlayerDvdNav > 1) {
+		PlayerSendDvdNavPrev();
+		break;
+	    }
+	    PlayerSendQuit();
+	    // FIXME: need to select old directory and index
+	    // FIXME: this shows a half drawn OSD
+	    cRemote::CallPlugin("play");
+	    return osBack;
+
+	case kMenu:			// VDR: eats the keys
+	case k5:
+	    if (PlayerDvdNav) {
+		PlayerSendDvdNavMenu();
+		break;
+	    }
+	    break;
+
+	case kAudio:			// VDR: eats the keys
+	case k7:
+	    // FIXME: audio menu
+	    PlayerSendSwitchAudio();
+	    break;
+	case kSubtitles:		// VDR: eats the keys
+	case k9:
+	    // FIXME: subtitle menu
+	    PlayerSendSubSelect();
+	    break;
+
+	default:
+	    break;
+    }
+
+    return state;
+}
+
+/**
+**	Play a file.
+**
+**	@param filename	path and file name
+*/
+static void PlayFile(const char *filename)
+{
+    dsyslog("[play]: play file '%s'\n", filename);
+    cControl::Launch(new cMyControl(filename));
+}
+
+/**
+**	Check if filename is an iso dvd image.
+**
+**	@param filename	path and file name
+**
+**	@retval	true	archive
+**	@retval false	no archive
+*/
+static int IsIsoImage(const char *filename)
+{
+
+    /**
+    **	Table of supported iso image suffixes.
+    */
+    static const NameFilter IsoFilters[] = {
+#define FILTER(x) { sizeof(x) - 1, x }
+	FILTER(".bin"),
+	FILTER(".dvd"),
+	FILTER(".img"),
+	FILTER(".iso"),
+	FILTER(".mdf"),
+	FILTER(".nrg"),
+#undef FILTER
+	{0, NULL}
+    };
+    int i;
+    int len;
+
+    len = strlen(filename);
+    for (i = 0; IsoFilters[i].String; ++i) {
+	if (len >= IsoFilters[i].Length
+	    && !strcasecmp(filename + len - IsoFilters[i].Length,
+		IsoFilters[i].String)) {
+	    return 1;
+	}
+    }
+    return 0;
+}
+
+/**
+**	Play a file, with type detection.
+**
+**	@param filename	path and file name
+*/
+static void PlayFileHandleType(const char *filename)
+{
+    if (IsIsoImage(filename)) {		// handle dvd iso images
+	char *tmp;
+
+	tmp = (char *)malloc(sizeof("dvdnav:///") + strlen(filename));
+	stpcpy(stpcpy(tmp, "dvdnav:///"), filename);
+	PlayFile(tmp);
+	free(tmp);
+	return;
+    }
+    PlayFile(filename);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//	cOsdMenu
+//////////////////////////////////////////////////////////////////////////////
+
+static char ShowBrowser;		///< flag show browser
+static const char *BrowserStartDir;	///< browser start directory
+static const NameFilter *BrowserFilters;	///< browser name filters
+static int DirStackSize;		///< size of directory stack
+static int DirStackUsed;		///< entries used of directory stack
+static char **DirStack;			///< current path directory stack
+
+/**
+**	Table of supported video suffixes.
+*/
+static const NameFilter VideoFilters[] = {
+#define FILTER(x) { sizeof(x) - 1, x }
+    FILTER(".ts"), FILTER(".avi"), FILTER(".flv"), FILTER(".iso"),
+    FILTER(".m4v"), FILTER(".mkv"), FILTER(".mov"), FILTER(".mp4"),
+    FILTER(".mpg"), FILTER(".vdr"), FILTER(".vob"), FILTER(".wmv"),
+#undef FILTER
+    {
+	0, NULL}
+};
+
+/**
+**	Table of supported audio suffixes.
+*/
+static const NameFilter AudioFilters[] = {
+#define FILTER(x) { sizeof(x) - 1, x }
+    FILTER(".flac"), FILTER(".mp3"), FILTER(".ogg"), FILTER(".wav"),
+#undef FILTER
+    {
+	0, NULL}
+};
+
+/**
+**	Table of supported image suffixes.
+*/
+static const NameFilter ImageFilters[] = {
+#define FILTER(x) { sizeof(x) - 1, x }
+    FILTER(".cbr"), FILTER(".cbz"), FILTER(".zip"), FILTER(".rar"),
+    FILTER(".jpg"), FILTER(".png"),
+#undef FILTER
+    {
+	0, NULL}
+};
+
+/**
+**	Menu class.
+*/
+class cBrowser:public cOsdMenu
+{
+  private:
+    const NameFilter *Filter;		///< current filter
+
+    /// Create a browser menu for current directory
+    void CreateMenu(void);
+    /// Create a browser menu for new directory
+    void NewDir(const char *, const NameFilter *);
+    /// Handle menu level up
+    eOSState LevelUp(void);
+    /// Handle menu item selection
+    eOSState Selected(void);
+
+  public:
+    /// File browser constructor
+    cBrowser(const char *, const char *, const NameFilter *);
+    /// File browser destructor
+    virtual ~ cBrowser();
+    /// Process keyboard input
+    virtual eOSState ProcessKey(eKeys);
+};
+
+/**
+**	Add item to menu.  Called from C.
+**
+**	@param obj	cBrowser object
+**	@param text	menu text
+*/
+extern "C" void cBrowser__Add(void *obj, const char *text)
+{
+    cBrowser *menu;
+
+    // fucking stupid C++ can't assign void* without warning:
+    menu = (typeof(menu)) obj;
+    menu->Add(new cOsdItem(text));
+}
+
+/**
+**	Create browser directory menu.
+*/
+void cBrowser::CreateMenu(void)
+{
+    Clear();				// start with empty directory
+    // FIXME: should show only directory name in title
+    //SetTitle(DirStack[0]);
+    Skins.Message(mtStatus, tr("Scanning directory..."));
+
+    if (DirStackUsed > 1) {
+	// FIXME: should show only path
+	Add(new cOsdItem(DirStack[0]));
+    }
+    ReadDirectory(DirStack[0], 1, NULL, cBrowser__Add, this);
+    ReadDirectory(DirStack[0], 0, Filter, cBrowser__Add, this);
+    // FIXME: handle errors!
+
+    Display();				// display build menu
+    Skins.Message(mtStatus, NULL);	// clear read message
+}
+
+/**
+**	Create directory menu.
+**
+**	@param path	directory path file name
+**	@param filter	name selection filter
+*/
+void cBrowser::NewDir(const char *path, const NameFilter * filter)
+{
+    int n;
+    char *pathname;
+
+    n = strlen(path);
+#if 1
+    // FIXME: force caller to do
+    if (path[n - 1] == '/') {		// force '/' terminated
+	pathname = strdup(path);
+    } else {
+	pathname = (char *)malloc(n + 2);
+	stpcpy(stpcpy(pathname, path), "/");
+    }
+#endif
+
+    // push on directory stack
+    if (DirStackUsed >= DirStackSize) {	// increase stack size
+	DirStackSize = DirStackUsed + 1;
+	DirStack =
+	    (typeof(DirStack)) realloc(DirStack,
+	    DirStackSize * sizeof(*DirStack));
+    }
+    memmove(DirStack + 1, DirStack, DirStackUsed * sizeof(*DirStack));
+    DirStackUsed++;
+    DirStack[0] = pathname;
+
+    Filter = filter;
+
+    CreateMenu();
+}
+
+/**
+**	Menu constructor.
+**
+**	@param title	menu title
+**	@param path	directory path file name
+**	@param filter	name selection filter
+*/
+cBrowser::cBrowser(const char *title, const char *path,
+    const NameFilter * filter)
+:cOsdMenu(title)
+{
+    dsyslog("[play]%s:\n", __FUNCTION__);
+
+    if (path) {				// clear stack, start new
+	int i;
+
+	// free the stored directory stack
+	for (i = 0; i < DirStackUsed; ++i) {
+	    free(DirStack[i]);
+	}
+	//free(DirStack);
+	//DirStack = NULL;		// reuse the old stack
+	DirStackUsed = 0;
+
+	NewDir(path, filter);
+	return;
+    }
+
+    Filter = filter;
+
+    CreateMenu();
+}
+
+/**
+**	Menu destructor.
+*/
+cBrowser::~cBrowser()
+{
+    dsyslog("[play]%s:\n", __FUNCTION__);
+}
+
+/**
+**	Handle level up.
+*/
+eOSState cBrowser::LevelUp(void)
+{
+    char *down;
+    char *name;
+
+    if (DirStackUsed <= 1) {		// top level reached
+	return osEnd;
+    }
+    // go level up
+    --DirStackUsed;
+    down = DirStack[0];
+    memmove(DirStack, DirStack + 1, DirStackUsed * sizeof(*DirStack));
+
+    CreateMenu();
+
+    // select item, where we gone down
+    down[strlen(down) - 1] = '\0';	// remove trailing '/'
+    name = strrchr(down, '/');
+    if (name) {
+	cOsdItem *item;
+	const char *text;
+	int i;
+
+	for (i = 0; (item = Get(i)); ++i) {
+	    text = item->Text();
+	    if (!strcmp(text, name + 1)) {
+		SetCurrent(item);
+		// FIXME: Display already called!
+		Display();		// display build menu
+		break;
+	    }
+	}
+    }
+
+    free(down);
+
+    return osContinue;
+}
+
+/**
+**	Handle selected item.
+*/
+eOSState cBrowser::Selected(void)
+{
+    int current;
+    const cOsdItem *item;
+    const char *text;
+    char *filename;
+    char *tmp;
+
+    current = Current();		// get current menu item index
+    item = Get(current);
+    text = item->Text();
+
+    if (current == 0 && DirStackUsed > 1) {
+	return LevelUp();
+    }
+    // +2: \0 + #
+    filename = (char *)malloc(strlen(DirStack[0]) + strlen(text) + 2);
+    // path is '/' terminated
+    tmp = stpcpy(stpcpy(filename, DirStack[0]), text);
+    if (!IsDirectory(filename)) {
+	if (IsArchive(filename)) {	// handle archives
+	    stpcpy(tmp, "#");
+	    NewDir(filename, Filter);
+	    free(filename);
+	    // FIXME: if dir fails use keep old!
+	    return osContinue;
+	}
+	PlayFileHandleType(filename);
+	free(filename);
+	return osEnd;
+    }
+    // handle DVD image
+    if (!strcmp(text, "AUDIO_TS") || !strcmp(text, "VIDEO_TS")) {
+	free(filename);
+	tmp = (char *)malloc(sizeof("dvdnav:///") + strlen(DirStack[0]));
+	strcpy(stpcpy(tmp, "dvdnav:///"), DirStack[0]);
+	PlayFile(tmp);
+	free(tmp);
+	return osEnd;
+    }
+    stpcpy(tmp, "/");			// append '/'
+    NewDir(filename, Filter);
+    free(filename);
+    // FIXME: if dir fails use keep old!
+    return osContinue;
+}
+
+/**
+**	Handle Menu key event.
+**
+**	@param key	key event
+*/
+eOSState cBrowser::ProcessKey(eKeys key)
+{
+    eOSState state;
+
+    // call standard function
+    state = cOsdMenu::ProcessKey(key);
+    if (state || key != kNone) {
+	dsyslog("[play]%s: state=%d key=%d\n", __FUNCTION__, state, key);
+    }
+
+    switch (state) {
+	case osUnknown:
+	    switch (key) {
+		case kOk:
+		    return Selected();
+		case kBack:
+		    return LevelUp();
+		default:
+		    break;
+	    }
+	    break;
+	case osBack:
+	    state = LevelUp();
+	    if (state == osEnd) {	// top level reached
+		ShowBrowser = 0;
+		return osPlugin;
+	    }
+	default:
+	    break;
+    }
+    return state;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//	cOsdMenu
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+**	Play plugin menu class.
+*/
+class cPlayMenu:public cOsdMenu
+{
+  private:
+  public:
+    cPlayMenu(const char *, int = 0, int = 0, int = 0, int = 0, int = 0);
+    virtual ~ cPlayMenu();
+    virtual eOSState ProcessKey(eKeys);
+};
+
+/**
+**	Play menu constructor.
+*/
+cPlayMenu::cPlayMenu(const char *title, int c0, int c1, int c2, int c3, int c4)
+:cOsdMenu(title, c0, c1, c2, c3, c4)
+{
+    SetHasHotkeys();
+
+    Add(new cOsdItem(hk(tr("Browse")), osUser1));
+    Add(new cOsdItem(hk(tr("Play optical disc")), osUser2));
+    Add(new cOsdItem(""));
+    Add(new cOsdItem(""));
+    Add(new cOsdItem(hk(tr("Play audio CD")), osUser5));
+    Add(new cOsdItem(hk(tr("Play video DVD")), osUser6));
+    Add(new cOsdItem(hk(tr("Browse audio")), osUser7));
+    Add(new cOsdItem(hk(tr("Browse image")), osUser8));
+    Add(new cOsdItem(hk(tr("Browse video")), osUser9));
+}
+
+/**
+**	Play menu destructor.
+*/
+cPlayMenu::~cPlayMenu()
+{
+}
+
+/**
+**	Handle play plugin menu key event.
+**
+**	@param key	key event
+*/
+eOSState cPlayMenu::ProcessKey(eKeys key)
+{
+    eOSState state;
+
+    if (key != kNone) {
+	dsyslog("[play]%s: key=%d\n", __FUNCTION__, key);
+    }
+    // call standard function
+    state = cOsdMenu::ProcessKey(key);
+
+    switch (state) {
+	case osUser1:
+	    ShowBrowser = 1;
+	    BrowserStartDir = ConfigBrowserRoot;
+	    BrowserFilters = NULL;
+	    return osPlugin;		// restart with OSD browser
+
+	case osUser2:
+	    Skins.Message(mtStatus,
+		tr("Function not working yet, use 3 or 4"));
+	    return osContinue;
+
+	case osUser5:			// play audio cdrom
+	    PlayFile("cdda://");
+	    return osEnd;
+	case osUser6:			// play dvd
+	    PlayFile("dvdnav://");
+	    return osEnd;
+
+	case osUser7:			// browse audio
+	    ShowBrowser = 1;
+	    BrowserStartDir = ConfigBrowserRoot;
+	    BrowserFilters = AudioFilters;
+	    return osPlugin;		// restart with OSD browser
+	case osUser8:			// browse images
+	    ShowBrowser = 1;
+	    BrowserStartDir = ConfigBrowserRoot;
+	    BrowserFilters = ImageFilters;
+	    return osPlugin;		// restart with OSD browser
+	case osUser9:			// browse videos
+	    ShowBrowser = 1;
+	    BrowserStartDir = ConfigBrowserRoot;
+	    BrowserFilters = VideoFilters;
+	    return osPlugin;		// restart with OSD browser
+
+#if 0
+	case osUser9:
+	    free(ShowDiashow);
+	    ShowDiashow = strdup(VideoDirectory);
+	    return osPlugin;		// restart with OSD browser
+#endif
+
+	default:
+	    break;
+    }
+    return state;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//	cOsd
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+**	My device plugin OSD class.
+*/
+class cMyOsd:public cOsd
+{
+  public:
+    static volatile char Dirty;		///< flag force redraw everything
+    int OsdLevel;			///< current osd level
+
+    cMyOsd(int, int, uint);		///< osd constructor
+    virtual ~ cMyOsd(void);		///< osd destructor
+    virtual void Flush(void);		///< commits all data to the hardware
+    virtual void SetActive(bool);	///< sets OSD to be the active one
+};
+
+volatile char cMyOsd::Dirty;		///< flag force redraw everything
+
+/**
+**	Sets this OSD to be the active one.
+**
+**	@param on	true on, false off
+**
+**	@note only needed as workaround for text2skin plugin with
+**	undrawn areas.
+*/
+void cMyOsd::SetActive(bool on)
+{
+    dsyslog("[play]%s: %d\n", __FUNCTION__, on);
+
+    if (Active() == on) {
+	return;				// already active, no action
+    }
+    cOsd::SetActive(on);
+
+    // ignore sub-title, if menu is open
+    if (OsdLevel >= OSD_LEVEL_SUBTITLES && IsOpen()) {
+	return;
+    }
+
+    if (on) {
+	Dirty = 1;
+	// only flush here if there are already bitmaps
+	//if (GetBitmap(0)) {
+	//    Flush();
+	//}
+	OsdOpen();
+    } else {
+	OsdClose();
+    }
+}
+
+/**
+**	Constructor OSD.
+**
+**	Initializes the OSD with the given coordinates.
+**
+**	@param left	x-coordinate of osd on display
+**	@param top	y-coordinate of osd on display
+**	@param level	level of the osd (smallest is shown)
+*/
+cMyOsd::cMyOsd(int left, int top, uint level)
+:cOsd(left, top, level)
+{
+    /* FIXME: OsdWidth/OsdHeight not correct!
+       dsyslog("[play]%s: %dx%d+%d+%d, %d\n", __FUNCTION__, OsdWidth(),
+       OsdHeight(), left, top, level);
+     */
+
+    OsdLevel = level;
+    SetActive(true);
+}
+
+/**
+**	OSD Destructor.
+**
+**	Shuts down the OSD.
+*/
+cMyOsd::~cMyOsd(void)
+{
+    dsyslog("[play]%s:\n", __FUNCTION__);
+    SetActive(false);
+    // done by SetActive: OsdClose();
+}
+
+/**
+**	Actually commits all data to the OSD hardware.
+*/
+void cMyOsd::Flush(void)
+{
+    cPixmapMemory *pm;
+
+    dsyslog("[play]%s: level %d active %d\n", __FUNCTION__, OsdLevel,
+	Active());
+
+    if (!Active()) {			// this osd is not active
+	return;
+    }
+    // don't draw sub-title if menu is active
+    if (OsdLevel >= OSD_LEVEL_SUBTITLES && IsOpen()) {
+	return;
+    }
+    //
+    //	VDR draws subtitle without clearing the old
+    //
+    if (OsdLevel >= OSD_LEVEL_SUBTITLES) {
+	OsdClear();
+	cMyOsd::Dirty = 1;
+	dsyslog("[play]%s: subtitle clear\n", __FUNCTION__);
+    }
+
+    if (!IsTrueColor()) {
+	cBitmap *bitmap;
+	int i;
+
+	// draw all bitmaps
+	for (i = 0; (bitmap = GetBitmap(i)); ++i) {
+	    uint8_t *argb;
+	    int x;
+	    int y;
+	    int w;
+	    int h;
+	    int x1;
+	    int y1;
+	    int x2;
+	    int y2;
+
+	    // get dirty bounding box
+	    if (Dirty) {		// forced complete update
+		x1 = 0;
+		y1 = 0;
+		x2 = bitmap->Width() - 1;
+		y2 = bitmap->Height() - 1;
+	    } else if (!bitmap->Dirty(x1, y1, x2, y2)) {
+		continue;		// nothing dirty continue
+	    }
+	    // convert and upload only dirty areas
+	    w = x2 - x1 + 1;
+	    h = y2 - y1 + 1;
+	    if (1) {			// just for the case it makes trouble
+		int width;
+		int height;
+		double video_aspect;
+
+		::GetOsdSize(&width, &height, &video_aspect);
+		if (w > width) {
+		    w = width;
+		    x2 = x1 + width - 1;
+		}
+		if (h > height) {
+		    h = height;
+		    y2 = y1 + height - 1;
+		}
+	    }
+#ifdef DEBUG
+	    if (w > bitmap->Width() || h > bitmap->Height()) {
+		esyslog(tr("[play]: dirty area too big\n"));
+		abort();
+	    }
+#endif
+	    argb = (uint8_t *) malloc(w * h * sizeof(uint32_t));
+	    for (y = y1; y <= y2; ++y) {
+		for (x = x1; x <= x2; ++x) {
+		    ((uint32_t *) argb)[x - x1 + (y - y1) * w] =
+			bitmap->GetColor(x, y);
+		}
+	    }
+	    dsyslog("[play]%s: draw %dx%d%+d%+d bm\n", __FUNCTION__, w, h,
+		Left() + bitmap->X0() + x1, Top() + bitmap->Y0() + y1);
+	    OsdDrawARGB(Left() + bitmap->X0() + x1, Top() + bitmap->Y0() + y1,
+		w, h, argb);
+
+	    bitmap->Clean();
+	    // FIXME: reuse argb
+	    free(argb);
+	}
+	cMyOsd::Dirty = 0;
+	return;
+    }
+
+    LOCK_PIXMAPS;
+    while ((pm = RenderPixmaps())) {
+	int x;
+	int y;
+	int w;
+	int h;
+
+	x = Left() + pm->ViewPort().X();
+	y = Top() + pm->ViewPort().Y();
+	w = pm->ViewPort().Width();
+	h = pm->ViewPort().Height();
+
+	dsyslog("[play]%s: draw %dx%d%+d%+d %p\n", __FUNCTION__, w, h, x, y,
+	    pm->Data());
+	OsdDrawARGB(x, y, w, h, pm->Data());
+
+	delete pm;
+    }
+    cMyOsd::Dirty = 0;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//	cOsdProvider
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+**	My device plugin OSD provider class.
+*/
+class cMyOsdProvider:public cOsdProvider
+{
+  private:
+    static cOsd *Osd;
+
+  public:
+    virtual cOsd * CreateOsd(int, int, uint);
+    virtual bool ProvidesTrueColor(void);
+    cMyOsdProvider(void);
+};
+
+cOsd *cMyOsdProvider::Osd;		///< single osd
+
+/**
+**	Create a new OSD.
+**
+**	@param left	x-coordinate of OSD
+**	@param top	y-coordinate of OSD
+**	@param level	layer level of OSD
+*/
+cOsd *cMyOsdProvider::CreateOsd(int left, int top, uint level)
+{
+    dsyslog("[play]%s: %d, %d, %d\n", __FUNCTION__, left, top, level);
+
+    return Osd = new cMyOsd(left, top, level);
+}
+
+/**
+**	Check if this OSD provider is able to handle a true color OSD.
+**
+**	@returns true we are able to handle a true color OSD.
+*/
+bool cMyOsdProvider::ProvidesTrueColor(void)
+{
+    return true;
+}
+
+/**
+**	Create cOsdProvider class.
+*/
+cMyOsdProvider::cMyOsdProvider(void)
+:  cOsdProvider()
+{
+    dsyslog("[play]%s:\n", __FUNCTION__);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//	cMenuSetupPage
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+**	Play plugin menu setup page class.
+*/
+class cMyMenuSetupPage:public cMenuSetupPage
+{
+  protected:
+    ///
+    /// local copies of global setup variables:
+    /// @{
+    int HideMainMenuEntry;
+    int DisableRemote;
+
+    /// @}
+    virtual void Store(void);
+
+  public:
+     cMyMenuSetupPage(void);
+    virtual eOSState ProcessKey(eKeys);	// handle input
+};
+
+/**
+**	Process key for setup menu.
+*/
+eOSState cMyMenuSetupPage::ProcessKey(eKeys key)
+{
+    eOSState state;
+
+    state = cMenuSetupPage::ProcessKey(key);
+
+    return state;
+}
+
+/**
+**	Constructor setup menu.
+**
+**	Import global config variables into setup.
+*/
+cMyMenuSetupPage::cMyMenuSetupPage(void)
+{
+    HideMainMenuEntry = ConfigHideMainMenuEntry;
+    DisableRemote = ConfigDisableRemote;
+
+    Add(new cMenuEditBoolItem(tr("Hide main menu entry"), &HideMainMenuEntry,
+	    trVDR("no"), trVDR("yes")));
+    Add(new cMenuEditBoolItem(tr("Disable remote"), &DisableRemote,
+	    trVDR("no"), trVDR("yes")));
+}
+
+/**
+**	Store setup.
+*/
+void cMyMenuSetupPage::Store(void)
+{
+    SetupStore("HideMainMenuEntry", ConfigHideMainMenuEntry =
+	HideMainMenuEntry);
+    SetupStore("DisableRemote", ConfigDisableRemote = DisableRemote);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//	cDevice
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+**	Dummy device class.
+*/
+class cMyDevice:public cDevice
+{
+  public:
+    cMyDevice(void);
+    virtual ~ cMyDevice(void);
+
+    virtual void GetOsdSize(int &, int &, double &);
+
+  protected:
+    virtual void MakePrimaryDevice(bool);
+};
+
+/**
+**	Device constructor.
+*/
+cMyDevice::cMyDevice(void)
+{
+    dsyslog("[play]%s\n", __FUNCTION__);
+}
+
+/**
+**	Device destructor. (never called!)
+*/
+cMyDevice::~cMyDevice(void)
+{
+    dsyslog("[play]%s:\n", __FUNCTION__);
+}
+
+/**
+**	Informs a device that it will be the primary device.
+**
+**	@param on	flag if becoming or loosing primary
+*/
+void cMyDevice::MakePrimaryDevice(bool on)
+{
+    dsyslog("[play]%s: %d\n", __FUNCTION__, on);
+
+    cDevice::MakePrimaryDevice(on);
+    if (on) {
+	new cMyOsdProvider();
+    }
+}
+
+/**
+**	Returns the width, height and pixel_aspect ratio the OSD.
+**
+**	FIXME: Called every second, for nothing (no OSD displayed)?
+*/
+void cMyDevice::GetOsdSize(int &width, int &height, double &pixel_aspect)
+{
+    if (!&width || !&height || !&pixel_aspect) {
+	esyslog(tr("[play]: GetOsdSize invalid pointer(s)\n"));
+	return;
+    }
+    ::GetOsdSize(&width, &height, &pixel_aspect);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//	cPlugin
+//////////////////////////////////////////////////////////////////////////////
+
+static cMyDevice *MyDevice;		///< dummy device needed for osd
+
+class cMyPlugin:public cPlugin
+{
+  public:
+    cMyPlugin(void);
+    virtual ~ cMyPlugin(void);
+    virtual const char *Version(void);
+    virtual const char *Description(void);
+    virtual const char *CommandLineHelp(void);
+    virtual bool ProcessArgs(int, char *[]);
+    virtual bool Initialize(void);
+    virtual void MainThreadHook(void);
+    virtual const char *MainMenuEntry(void);
+    virtual cOsdObject *MainMenuAction(void);
+    virtual cMenuSetupPage *SetupMenu(void);
+    virtual bool SetupParse(const char *, const char *);
+    virtual bool Service(const char *, void * = NULL);
+    virtual const char **SVDRPHelpPages(void);
+    virtual cString SVDRPCommand(const char *, const char *, int &);
+};
+
+/**
+**	Initialize any member variables here.
+**
+**	@note DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
+**	VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
+*/
+cMyPlugin::cMyPlugin(void)
+{
+    dsyslog("[play]%s:\n", __FUNCTION__);
+}
+
+/**
+**	Clean up after yourself!
+*/
+cMyPlugin::~cMyPlugin(void)
+{
+    dsyslog("[play]%s:\n", __FUNCTION__);
+}
+
+/**
+**	Return plugin version number.
+**
+**	@returns version number as constant string.
+*/
+const char *cMyPlugin::Version(void)
+{
+    return VERSION;
+}
+
+/**
+**	Return plugin short description.
+**
+**	@returns short description as constant string.
+*/
+const char *cMyPlugin::Description(void)
+{
+    return tr(DESCRIPTION);
+}
+
+/**
+**	Return a string that describes all known command line options.
+**
+**	@returns command line help as constant string.
+*/
+const char *cMyPlugin::CommandLineHelp(void)
+{
+    return::CommandLineHelp();
+}
+
+/**
+**	Process the command line arguments.
+*/
+bool cMyPlugin::ProcessArgs(int argc, char *argv[])
+{
+    return::ProcessArgs(argc, argv);
+}
+
+/**
+**	Start any background activities the plugin shall perform.
+*/
+bool cMyPlugin::Initialize(void)
+{
+    //dsyslog("[play]%s:\n", __FUNCTION__);
+
+    // FIXME: can delay until needed?
+    //Status = new cMyStatus;		// start monitoring
+    // FIXME: destructs memory
+
+    MyDevice = new cMyDevice;
+    return true;
+}
+
+/**
+**	Create main menu entry.
+*/
+const char *cMyPlugin::MainMenuEntry(void)
+{
+    return ConfigHideMainMenuEntry ? NULL : tr(MAINMENUENTRY);
+}
+
+/**
+**	Perform the action when selected from the main VDR menu.
+*/
+cOsdObject *cMyPlugin::MainMenuAction(void)
+{
+    //dsyslog("[play]%s:\n", __FUNCTION__);
+
+#if 0
+    if (ShowDiashow) {
+	return new cDiashow(ShowDiashow);
+    }
+#endif
+    if (ShowBrowser) {
+	const char *start;
+
+	start = BrowserStartDir;	// root only one time
+	BrowserStartDir = NULL;
+	return new cBrowser("Browse", start, BrowserFilters);
+    }
+    return new cPlayMenu("Play");
+}
+
+/**
+**	Receive requests or messages.
+**
+**	@param id	unique identification string that identifies the
+**			service protocol
+**	@param data	custom data structure
+*/
+bool cMyPlugin::Service(const char *id, void *data)
+{
+    if (strcmp(id, PLAY_OSD_3DMODE_SERVICE) == 0) {
+	VideoSetOsd3DMode(0);
+	Play_Osd3DModeService_v1_0_t *r =
+	    (Play_Osd3DModeService_v1_0_t *) data;
+	VideoSetOsd3DMode(r->Mode);
+	return true;
+    }
+    return false;
+}
+
+/**
+**	Return SVDRP commands help pages.
+**
+**	return a pointer to a list of help strings for all of the plugin's
+**	SVDRP commands.
+*/
+const char **cMyPlugin::SVDRPHelpPages(void)
+{
+    static const char *HelpPages[] = {
+	"3DOF\n" "	  TURN OFF 3D", "3DTB\n" "    TURN ON 3D TB",
+	"3DSB\n" "	  TURN ON 3D SBS", NULL
+    };
+    return HelpPages;
+}
+
+/**
+**	Handle SVDRP commands.
+**
+**	@param command		SVDRP command
+**	@param option		all command arguments
+**	@param reply_code	reply code
+*/
+cString cMyPlugin::SVDRPCommand(const char *command,
+    __attribute__ ((unused)) const char *option,
+    __attribute__ ((unused)) int &reply_code)
+{
+    if (!strcasecmp(command, "3DOF")) {
+	VideoSetOsd3DMode(0);
+	return "3d off";
+    }
+    if (!strcasecmp(command, "3DSB")) {
+	VideoSetOsd3DMode(1);
+	return "3d sbs";
+    }
+    if (!strcasecmp(command, "3DTB")) {
+	VideoSetOsd3DMode(2);
+	return "3d tb";
+    }
+    return NULL;
+}
+
+/**
+**	Called for every plugin once during every cycle of VDR's main program
+**	loop.
+*/
+void cMyPlugin::MainThreadHook(void)
+{
+    // dsyslog("[play]%s:\n", __FUNCTION__);
+
+    if (DoMakePrimary) {
+	dsyslog("[play]: switching primary device to %d\n", DoMakePrimary);
+	cDevice::SetPrimaryDevice(DoMakePrimary);
+	DoMakePrimary = 0;
+    }
+}
+
+/**
+**	Return our setup menu.
+*/
+cMenuSetupPage *cMyPlugin::SetupMenu(void)
+{
+    //dsyslog("[play]%s:\n", __FUNCTION__);
+
+    return new cMyMenuSetupPage;
+}
+
+/**
+**	Parse setup parameters
+**
+**	@param name	paramter name (case sensetive)
+**	@param value	value as string
+**
+**	@returns true if the parameter is supported.
+*/
+bool cMyPlugin::SetupParse(const char *name, const char *value)
+{
+    dsyslog("[play]%s: '%s' = '%s'\n", __FUNCTION__, name, value);
+
+    if (!strcasecmp(name, "HideMainMenuEntry")) {
+	ConfigHideMainMenuEntry = atoi(value);
+	return true;
+    }
+    if (!strcasecmp(name, "DisableRemote")) {
+	ConfigDisableRemote = atoi(value);
+	return true;
+    }
+#if 0
+    if (!strncasecmp(name, "Dia.", 4)) {
+	return DiaConfigParse(name + 4, value);
+    }
+#endif
+
+    return false;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+int OldPrimaryDevice;			///< old primary device
+
+/**
+**	Enable dummy device.
+*/
+extern "C" void EnableDummyDevice(void)
+{
+    OldPrimaryDevice = cDevice::PrimaryDevice()->DeviceNumber() + 1;
+
+    dsyslog("[play]: primary device %d to new %d\n", OldPrimaryDevice,
+	MyDevice->DeviceNumber() + 1);
+
+    //if (!cDevice::SetPrimaryDevice(MyDevice->DeviceNumber() + 1)) {
+    DoMakePrimary = MyDevice->DeviceNumber() + 1;
+    cOsdProvider::Shutdown();
+    //}
+}
+
+/**
+**	Disable dummy device.
+*/
+extern "C" void DisableDummyDevice(void)
+{
+    dsyslog("[play]: primary device %d to old %d\n",
+	cDevice::PrimaryDevice()->DeviceNumber() + 1, OldPrimaryDevice);
+
+    //if (!cDevice::SetPrimaryDevice(OldPrimaryDevice)) {
+    DoMakePrimary = OldPrimaryDevice;
+    OldPrimaryDevice = 0;
+    cOsdProvider::Shutdown();
+    //}
+}
+
+VDRPLUGINCREATOR(cMyPlugin);		// Don't touch this!
diff --git a/play_service.h b/play_service.h
new file mode 100644
index 0000000..d903996
--- /dev/null
+++ b/play_service.h
@@ -0,0 +1,30 @@
+///
+///	@file play_service.h @brief Play service header file.
+///
+///	Copyright (c) 2012 by durchflieger.  All Rights Reserved.
+///
+///	Contributor(s):
+///
+///	License: AGPLv3
+///
+///	This program is free software: you can redistribute it and/or modify
+///	it under the terms of the GNU Affero General Public License as
+///	published by the Free Software Foundation, either version 3 of the
+///	License.
+///
+///	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 Affero General Public License for more details.
+///
+///	$Id$
+//////////////////////////////////////////////////////////////////////////////
+
+#pragma once
+
+#define PLAY_OSD_3DMODE_SERVICE	"Play-Osd3DModeService-v1.0"
+
+typedef struct
+{
+    int Mode;
+} Play_Osd3DModeService_v1_0_t;
diff --git a/player.c b/player.c
new file mode 100644
index 0000000..23f35b1
--- /dev/null
+++ b/player.c
@@ -0,0 +1,984 @@
+///
+///	@file player.c	@brief A play plugin for VDR.
+///
+///	Copyright (c) 2012, 2013 by Johns.  All Rights Reserved.
+///
+///	Contributor(s): Dennis Bendlin
+///
+///	License: AGPLv3
+///
+///	This program is free software: you can redistribute it and/or modify
+///	it under the terms of the GNU Affero General Public License as
+///	published by the Free Software Foundation, either version 3 of the
+///	License.
+///
+///	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 Affero General Public License for more details.
+///
+///	$Id$
+//////////////////////////////////////////////////////////////////////////////
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <poll.h>
+#include <pthread.h>
+
+#include <libintl.h>
+#define _(str) gettext(str)		///< gettext shortcut
+#define _N(str) str			///< gettext_noop shortcut
+
+#include "player.h"
+#include "video.h"
+#include "misc.h"
+
+//////////////////////////////////////////////////////////////////////////////
+
+const char *ConfigBrowserRoot = "/";	///< browser starting point
+
+static char ConfigOsdOverlay;		///< show osd overlay
+static char ConfigUseSlave;		///< external player use slave mode
+static char ConfigFullscreen;		///< external player uses fullscreen
+static char *ConfigVideoOut;		///< video out device
+static char *ConfigAudioOut;		///< audio out device
+static char *ConfigAudioMixer;		///< audio mixer device
+static char *ConfigMixerChannel;	///< audio mixer channel
+static const char *ConfigMplayer = "/usr/bin/mplayer";	///< mplayer executable
+static const char *ConfigMplayerArguments;	///< extra mplayer arguments
+static const char *ConfigX11Display = ":0.0";	///< x11 display
+
+    /// DVD-Drive for mplayer
+static const char *ConfigMplayerDevice = "/dev/dvd";
+static uint32_t ConfigColorKey = 0x00020507;	///< color key
+
+//////////////////////////////////////////////////////////////////////////////
+//	Osd
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+**	Open OSD.
+*/
+void OsdOpen(void)
+{
+    Debug(3, "play: %s\n", __FUNCTION__);
+
+    VideoWindowShow();
+}
+
+/**
+**	Close OSD.
+*/
+void OsdClose(void)
+{
+    Debug(3, "play: %s\n", __FUNCTION__);
+
+    VideoWindowHide();
+    VideoWindowClear();
+}
+
+/**
+**	Clear OSD.
+*/
+void OsdClear(void)
+{
+    Debug(3, "play: %s\n", __FUNCTION__);
+
+    VideoWindowClear();
+}
+
+/**
+**	Get OSD size and aspect.
+**
+**	@param width[OUT]	width of OSD
+**	@param height[OUT]	height of OSD
+**	@param aspect[OUT]	aspect ratio (4/3, 16/9, ...) of OSD
+*/
+void GetOsdSize(int *width, int *height, double *aspect)
+{
+    VideoGetOsdSize(width, height);
+    *aspect = 16.0 / 9.0 / (double)*width * (double)*height;
+}
+
+/**
+**	Draw osd pixmap
+*/
+void OsdDrawARGB(int x, int y, int w, int h, const uint8_t * argb)
+{
+    Debug(3, "play: %s %d,%d %d,%d\n", __FUNCTION__, x, y, w, h);
+
+    VideoDrawARGB(x, y, w, h, argb);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//	External player
+//////////////////////////////////////////////////////////////////////////////
+
+static pid_t PlayerPid;			///< player pid
+static pthread_t PlayerThread;		///< player decode thread
+
+static char PlayerPipeBuf[4096];	///< pipe buffer
+static int PlayerPipeCnt;		///< pipe buffer count
+static int PlayerPipeIdx;		///< pipe buffer index
+static int PlayerPipeOut[2];		///< player write pipe
+static int PlayerPipeIn[2];		///< player read pipe
+
+static int PlayerVolume = -1;		///< volume 0 - 100
+
+char PlayerDvdNav;			///< dvdnav active
+char PlayerPaused;			///< player paused
+char PlayerSpeed;			///< player playback speed
+int PlayerCurrent;			///< current postion in seconds
+int PlayerTotal;			///< total length in seconds
+char PlayerTitle[256];			///< title from meta data
+char PlayerFilename[256];		///< filename
+
+//////////////////////////////////////////////////////////////////////////////
+//	Slave
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+**	Parse player output.
+**
+**	@param data	line pointer (\0 terminated)
+**	@param size	line length
+*/
+static void PlayerParseLine(const char *data, int size)
+{
+    Debug(4, "play/parse: |%.*s|\n", size, data);
+    (void)size;
+
+    // data is \0 terminated
+    if (!strncasecmp(data, "DVDNAV_TITLE_IS_MENU", 20)) {
+	PlayerDvdNav = 1;
+    } else if (!strncasecmp(data, "DVDNAV_TITLE_IS_MOVIE", 21)) {
+	PlayerDvdNav = 2;
+    } else if (!strncasecmp(data, "ID_DVD_VOLUME_ID=", 17)) {
+	Debug(3, "DVD_VOLUME = %s\n", data + 17);
+    } else if (!strncasecmp(data, "ID_AID_", 7)) {
+	char lang[64];
+	int aid;
+
+	if (sscanf(data, "ID_AID_%d_LANG=%s", &aid, lang) == 2) {
+	    Debug(3, "AID(%d) = %s\n", aid, lang);
+	}
+    } else if (!strncasecmp(data, "ID_SID_", 7)) {
+	char lang[64];
+	int sid;
+
+	if (sscanf(data, "ID_SID_%d_LANG=%s", &sid, lang) == 2) {
+	    Debug(3, "SID(%d) = %s\n", sid, lang);
+	}
+    } else if (!strncasecmp(data, "ANS_META_TITLE=", 14)) {
+	if (sscanf(data, "ANS_META_TITLE='%[^\t\n]", PlayerTitle) == 1) {
+	    PlayerTitle[strlen(PlayerTitle) - 1] = 0;
+	    Debug(3, "PlayerTitle= %s\n", PlayerTitle);
+	}
+    } else if (!strncasecmp(data, "ANS_FILENAME=", 12)) {
+	if (sscanf(data, "ANS_FILENAME='%[^\t\n]", PlayerFilename) == 1) {
+	    PlayerFilename[strlen(PlayerFilename) - 1] = 0;
+	    Debug(3, "PlayerFilename= %s\n", PlayerFilename);
+	}
+    } else if (!strncasecmp(data, "ANS_LENGTH=", 10)) {
+	if (sscanf(data, "ANS_LENGTH=%d", &PlayerTotal) == 1) {
+	    Debug(3, "PlayerTotal=%d\n", PlayerTotal);
+	}
+    } else if (!strncasecmp(data, "ANS_TIME_POSITION=", 17)) {
+	if (sscanf(data, "ANS_TIME_POSITION=%d", &PlayerCurrent) == 1) {
+	    Debug(3, "PlayerCurrent=%d\n", PlayerCurrent);
+	}
+    }
+}
+
+/**
+**	Poll input pipe.
+*/
+static void PlayerPollPipe(void)
+{
+    struct pollfd poll_fds[1];
+    int n;
+    int i;
+    int l;
+
+    // check if something to read
+    poll_fds[0].fd = PlayerPipeOut[0];
+    poll_fds[0].events = POLLIN;
+
+    switch (poll(poll_fds, 1, 0)) {
+	case 0:			// timeout
+	    return;
+	case -1:			// error
+	    Error(_("play/player: poll failed: %s\n"), strerror(errno));
+	    return;
+	default:			// ready
+	    break;
+    }
+
+    // fill buffer
+    if ((n = read(PlayerPipeOut[0], PlayerPipeBuf + PlayerPipeCnt,
+		sizeof(PlayerPipeBuf) - PlayerPipeCnt)) < 0) {
+	Error(_("play/player: read failed: %s\n"), strerror(errno));
+	return;
+    }
+
+    PlayerPipeCnt += n;
+    l = 0;
+    for (i = PlayerPipeIdx; i < PlayerPipeCnt; ++i) {
+	if (PlayerPipeBuf[i] == '\n') {
+	    PlayerPipeBuf[i] = '\0';
+	    PlayerParseLine(PlayerPipeBuf + PlayerPipeIdx, i - PlayerPipeIdx);
+	    PlayerPipeIdx = i + 1;
+	    l = PlayerPipeIdx;
+	}
+    }
+
+    if (l) {				// remove consumed bytes
+	if (PlayerPipeCnt - l) {
+	    memmove(PlayerPipeBuf, PlayerPipeBuf + l, PlayerPipeCnt - l);
+	}
+	PlayerPipeCnt -= l;
+	PlayerPipeIdx -= l;
+    } else if (PlayerPipeCnt == sizeof(PlayerPipeBuf)) {
+	// no '\n' in buffer use it as single line
+	PlayerPipeBuf[sizeof(PlayerPipeBuf) - 1] = '\0';
+	PlayerParseLine(PlayerPipeBuf + PlayerPipeIdx, PlayerPipeCnt);
+	PlayerPipeIdx = 0;
+	PlayerPipeCnt = 0;
+    }
+}
+
+#define MPLAYER_MAX_ARGS 64		///< number of arguments supported
+
+/**
+**	Execute external player.
+**
+**	@param filename	path and name of file to play
+*/
+static void PlayerExec(const char *filename)
+{
+    const char *args[MPLAYER_MAX_ARGS];
+    int argn;
+    char wid_buf[32];
+    char volume_buf[32];
+    int i;
+
+    if (ConfigUseSlave) {		// connect pipe to stdin/stdout
+	dup2(PlayerPipeIn[0], STDIN_FILENO);
+	close(PlayerPipeIn[0]);
+	close(PlayerPipeIn[1]);
+	close(PlayerPipeOut[0]);
+	dup2(PlayerPipeOut[1], STDOUT_FILENO);
+	dup2(PlayerPipeOut[1], STDERR_FILENO);
+	close(PlayerPipeOut[1]);
+    }
+    // close all file handles
+    for (i = getdtablesize() - 1; i > STDERR_FILENO; i--) {
+	close(i);
+    }
+
+    // export DISPLAY=
+
+    args[0] = ConfigMplayer;
+    args[1] = "-quiet";
+    args[2] = "-msglevel";
+    // FIXME: play with the options
+#ifdef DEBUG
+    args[3] = "all=6:global=4:cplayer=4:identify=4";
+#else
+    args[3] = "all=2:global=4:cplayer=2:identify=4";
+#endif
+    if (ConfigOsdOverlay) {
+	args[4] = "-noontop";
+    } else {
+	args[4] = "-ontop";
+    }
+    args[5] = "-noborder";
+    if (ConfigDisableRemote) {
+	args[6] = "-lirc";
+    } else {
+	args[6] = "-nolirc";
+    }
+    args[7] = "-nojoystick";		// disable all unwanted inputs
+    args[8] = "-noar";
+    args[9] = "-nomouseinput";
+    args[10] = "-nograbpointer";
+    args[11] = "-noconsolecontrols";
+    args[12] = "-fixed-vo";
+    args[13] = "-sid";			// subtitle selection
+    args[14] = "0";
+    args[15] = "-slang";
+    args[16] = "de,en";			// FIXME: use VDR config
+    args[17] = "-alang";
+    args[18] = "de,en";			// FIXME: use VDR config
+    argn = 19;
+    if (ConfigMplayerDevice) {		// dvd-device
+	args[argn++] = "-dvd-device";
+	args[argn++] = ConfigMplayerDevice;
+    }
+    if (!strncasecmp(filename, "cdda://", 7)) {
+	args[argn++] = "-cache";	// cdrom needs cache
+	args[argn++] = "1000";
+    } else {
+	args[argn++] = "-nocache";	// dvdnav needs nocache
+    }
+    if (ConfigUseSlave) {
+	args[argn++] = "-slave";
+	//args[argn++] = "-idle";
+    }
+    if (ConfigOsdOverlay) {		// no mplayer osd with overlay
+	args[argn++] = "-osdlevel";
+	args[argn++] = "0";
+    }
+    if (ConfigFullscreen) {
+	args[argn++] = "-fs";
+	args[argn++] = "-zoom";
+    } else {
+	args[argn++] = "-nofs";
+    }
+    if (VideoGetPlayWindow()) {
+	snprintf(wid_buf, sizeof(wid_buf), "%d", VideoGetPlayWindow());
+	args[argn++] = "-wid";
+	args[argn++] = wid_buf;
+    }
+    if (ConfigVideoOut) {
+	args[argn++] = "-vo";
+	args[argn++] = ConfigVideoOut;
+	// add options based on selected video out
+	if (!strncmp(ConfigVideoOut, "vdpau", 5)) {
+	    args[argn++] = "-vc";
+	    args[argn++] =
+		"ffmpeg12vdpau,ffwmv3vdpau,ffvc1vdpau,ffh264vdpau,ffodivxvdpau,";
+	} else if (!strncmp(ConfigVideoOut, "vaapi", 5)) {
+	    args[argn++] = "-va";
+	    args[argn++] = "vaapi";
+	}
+    }
+    if (ConfigAudioOut) {
+	args[argn++] = "-ao";
+	args[argn++] = ConfigAudioOut;
+	// FIXME: -ac hwac3,hwdts,hwmpa,
+    }
+    if (ConfigAudioMixer) {
+	args[argn++] = "-mixer";
+	args[argn++] = ConfigAudioMixer;
+    }
+    if (ConfigMixerChannel) {
+	args[argn++] = "-mixer-channel";
+	args[argn++] = ConfigMixerChannel;
+    }
+    if (ConfigX11Display) {
+	args[argn++] = "-display";
+	args[argn++] = ConfigX11Display;
+    }
+    if (PlayerVolume != -1) {
+	// FIXME: here could be a problem with LANG
+	snprintf(volume_buf, sizeof(volume_buf), "%.2f",
+	    (PlayerVolume * 100.0) / 255);
+	args[argn++] = "-volume";
+	args[argn++] = volume_buf;
+    }
+    //	split X server arguments string into words
+    if (ConfigMplayerArguments) {
+	const char *sval;
+	char *buf;
+	char *s;
+
+	sval = ConfigMplayerArguments;
+#ifndef __FreeBSD__
+	s = buf = strdupa(sval);
+#else
+	s = buf = alloca(strlen(sval) + 1);
+	strcpy(buf, sval);
+#endif
+	while ((sval = strsep(&s, " \t"))) {
+	    args[argn++] = sval;
+
+	    if (argn == MPLAYER_MAX_ARGS - 3) {	// argument overflow
+		Error(_("play: too many arguments for mplayer\n"));
+		// argn = 1;
+		break;
+	    }
+	}
+    }
+    args[argn++] = "--";
+    args[argn++] = filename;
+    args[argn] = NULL;
+#ifdef DEBUG
+    if (argn + 1 >= (int)(sizeof(args) / sizeof(*args))) {
+	Debug(3, "play: too many arguments %d\n", argn);
+    }
+    for (i = 0; i < argn; ++i) {
+	Debug(3, "%s", args[i]);
+    }
+#endif
+
+    execvp(args[0], (char *const *)args);
+
+    // shouldn't be reached
+    Error(_("play: execvp of '%s' failed: %s\n"), args[0], strerror(errno));
+    exit(-1);
+}
+
+/**
+**	Execute external player.
+**
+**	@param filename	path and name of file to play
+*/
+static void PlayerForkAndExec(const char *filename)
+{
+    pid_t pid;
+
+    if (ConfigUseSlave) {
+	if (pipe(PlayerPipeIn)) {
+	    Error(_("play: pipe failed: %s\n"), strerror(errno));
+	    return;
+	}
+	if (pipe(PlayerPipeOut)) {
+	    Error(_("play: pipe failed: %s\n"), strerror(errno));
+	    return;
+	}
+    }
+
+    if ((pid = fork()) == -1) {
+	Error(_("play: fork failed: %s\n"), strerror(errno));
+	return;
+    }
+    if (!pid) {				// child
+	PlayerExec(filename);
+	return;
+    }
+    PlayerPid = pid;			// parent
+    setpgid(pid, 0);
+
+    if (ConfigUseSlave) {
+	close(PlayerPipeIn[0]);
+	close(PlayerPipeOut[1]);
+    }
+
+    Debug(3, "play: child pid=%d\n", pid);
+}
+
+/**
+**	Close pipes.
+*/
+static void PlayerClosePipes(void)
+{
+    if (ConfigUseSlave) {
+	if (PlayerPipeIn[0] != -1) {
+	    close(PlayerPipeIn[0]);
+	}
+	if (PlayerPipeOut[1] != -1) {
+	    close(PlayerPipeOut[1]);
+	}
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//	Thread
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+**	External player handler thread.
+**
+**	@param dummy	dummy pointer
+*/
+static void *PlayerHandlerThread(void *dummy)
+{
+    Debug(3, "play: player thread started\n");
+
+    // Need: thread for video poll: while (PlayerIsRunning())
+    for (;;) {
+	if (ConfigUseSlave && PlayerIsRunning()) {
+	    PlayerPollPipe();
+	    // FIXME: wait only if pipe not ready
+	}
+	if (ConfigOsdOverlay) {
+	    VideoPollEvents(10);
+	} else {
+	    usleep(10 * 1000);
+	}
+    }
+
+    Debug(3, "play: player thread stopped\n");
+    PlayerThread = 0;
+
+    return dummy;
+}
+
+/**
+**	Initialize player threads.
+*/
+static void PlayerThreadInit(void)
+{
+    pthread_create(&PlayerThread, NULL, PlayerHandlerThread, NULL);
+    //pthread_setname_np(PlayerThread, "play player");
+}
+
+/**
+**	Exit and cleanup player threads.
+*/
+static void PlayerThreadExit(void)
+{
+    if (PlayerThread) {
+	void *retval;
+
+	Debug(3, "play: player thread canceled\n");
+	if (pthread_cancel(PlayerThread)) {
+	    Error(_("play: can't queue cancel player thread\n"));
+	}
+	if (pthread_join(PlayerThread, &retval) || retval != PTHREAD_CANCELED) {
+	    Error(_("play: can't cancel player thread\n"));
+	}
+	PlayerThread = 0;
+    }
+}
+
+/**
+**	Send command to player.
+**
+**	@param formst	printf format string
+*/
+static void SendCommand(const char *format, ...)
+{
+    va_list va;
+    char buf[256];
+    int n;
+
+    if (!PlayerPid) {
+	return;
+    }
+    if (PlayerPipeIn[1] == -1) {
+	Error(_("play: no pipe to send command available\n"));
+	return;
+    }
+    va_start(va, format);
+    n = vsnprintf(buf, sizeof(buf), format, va);
+    va_end(va);
+
+    Debug(3, "play: send '%.*s'\n", n - 1, buf);
+
+    // FIXME: poll pipe if ready
+    if (write(PlayerPipeIn[1], buf, n) != n) {
+	fprintf(stderr, "play: write failed\n");
+    }
+}
+
+/**
+**	Send player quit.
+*/
+void PlayerSendQuit(void)
+{
+    if (ConfigUseSlave) {
+	SendCommand("quit\n");
+    }
+}
+
+/**
+**	Send player pause.
+*/
+void PlayerSendPause(void)
+{
+    if (ConfigUseSlave) {
+	SendCommand("pause\n");
+    }
+}
+
+/**
+**	Send player speed_set.
+**
+**	@param speed	mplayer speed
+*/
+void PlayerSendSetSpeed(int speed)
+{
+    if (ConfigUseSlave) {
+	SendCommand("pausing_keep speed_set %d\n", speed);
+    }
+}
+
+/**
+**	Send player seek.
+**
+**	@param seconds	seek in seconds relative to current position
+*/
+void PlayerSendSeek(int seconds)
+{
+    if (ConfigUseSlave) {
+	SendCommand("pausing_keep seek %+d 0\n", seconds);
+    }
+}
+
+/**
+**	Send player volume.
+*/
+void PlayerSendVolume(void)
+{
+    if (ConfigUseSlave) {
+	// FIXME: %.2f could have a problem with LANG
+	SendCommand("pausing_keep volume %.2f 1\n",
+	    (PlayerVolume * 100.0) / 255);
+    }
+}
+
+/**
+**	Send switch audio track.
+*/
+void PlayerSendSwitchAudio(void)
+{
+    if (ConfigUseSlave) {
+	SendCommand("pausing_keep switch_audio\n");
+    }
+}
+
+/**
+**	Send select subtitle.
+*/
+void PlayerSendSubSelect(void)
+{
+    if (ConfigUseSlave) {
+	SendCommand("pausing_keep sub_select\n");
+    }
+}
+
+/**
+**	Send up for dvdnav.
+*/
+void PlayerSendDvdNavUp(void)
+{
+    if (ConfigUseSlave) {
+	SendCommand("pausing_keep dvdnav up\n");
+    }
+}
+
+/**
+**	Send down for dvdnav.
+*/
+void PlayerSendDvdNavDown(void)
+{
+    if (ConfigUseSlave) {
+	SendCommand("pausing_keep dvdnav down\n");
+    }
+}
+
+/**
+**	Send left for dvdnav.
+*/
+void PlayerSendDvdNavLeft(void)
+{
+    if (ConfigUseSlave) {
+	SendCommand("pausing_keep dvdnav left\n");
+    }
+}
+
+/**
+**	Send right for dvdnav.
+*/
+void PlayerSendDvdNavRight(void)
+{
+    if (ConfigUseSlave) {
+	SendCommand("pausing_keep dvdnav right\n");
+    }
+}
+
+/**
+**	Send select for dvdnav.
+*/
+void PlayerSendDvdNavSelect(void)
+{
+    if (ConfigUseSlave) {
+	SendCommand("pausing_keep dvdnav select\n");
+    }
+}
+
+/**
+**	Send prev for dvdnav.
+*/
+void PlayerSendDvdNavPrev(void)
+{
+    if (ConfigUseSlave) {
+	SendCommand("pausing_keep dvdnav prev\n");
+    }
+}
+
+/**
+**	Send menu for dvdnav.
+*/
+void PlayerSendDvdNavMenu(void)
+{
+    if (ConfigUseSlave) {
+	SendCommand("pausing_keep dvdnav menu\n");
+    }
+}
+
+/**
+**	Get length in seconds.
+*/
+void PlayerGetLength(void)
+{
+    if (ConfigUseSlave) {
+	SendCommand("get_time_length\n");
+    }
+}
+
+/**
+**	Get current position in seconds.
+*/
+void PlayerGetCurrentPosition(void)
+{
+    if (ConfigUseSlave) {
+	SendCommand("get_time_pos\n");
+    }
+}
+
+/**
+**	Get title from meta data.
+*/
+void PlayerGetMetaTitle(void)
+{
+    if (ConfigUseSlave) {
+	SendCommand("get_meta_title\n");
+    }
+}
+
+/**
+**	Get filename.
+*/
+void PlayerGetFilename(void)
+{
+    if (ConfigUseSlave) {
+	SendCommand("get_file_name\n");
+    }
+}
+
+/**
+**	Start external player.
+**
+**	@param filename	path and name of file to play
+*/
+void PlayerStart(const char *filename)
+{
+    PlayerPipeCnt = 0;			// reset to defaults
+    PlayerPipeIdx = 0;
+
+    PlayerPipeIn[0] = -1;
+    PlayerPipeIn[1] = -1;
+    PlayerPipeOut[0] = -1;
+    PlayerPipeOut[1] = -1;
+    PlayerPid = 0;
+
+    PlayerPaused = 0;
+    PlayerSpeed = 1;
+
+    PlayerDvdNav = 0;
+
+    if (ConfigOsdOverlay) {		// overlay wanted
+	VideoSetColorKey(ConfigColorKey);
+	VideoInit(ConfigX11Display);
+	EnableDummyDevice();
+    }
+    PlayerForkAndExec(filename);
+
+    if (ConfigOsdOverlay || ConfigUseSlave) {
+	PlayerThreadInit();
+    }
+}
+
+/**
+**	Stop external player.
+*/
+void PlayerStop(void)
+{
+    PlayerThreadExit();
+
+    //
+    //	stop mplayer, if it is still running.
+    //
+    if (PlayerIsRunning()) {
+	int waittime;
+	int timeout;
+
+	waittime = 0;
+	timeout = 500;			// 0.5s
+
+	kill(PlayerPid, SIGTERM);
+	// wait for player finishing, with timeout
+	while (PlayerIsRunning() && waittime++ < timeout) {
+	    usleep(1 * 1000);
+	}
+	if (PlayerIsRunning()) {	// still running
+	    waittime = 0;
+	    timeout = 500;		// 0.5s
+
+	    kill(PlayerPid, SIGKILL);
+	    // wait for player finishing, with timeout
+	    while (PlayerIsRunning() && waittime++ < timeout) {
+		usleep(1 * 1000);
+	    }
+	    if (PlayerIsRunning()) {
+		Error(_("play: can't stop player\n"));
+	    }
+	}
+    }
+    PlayerPid = 0;
+    PlayerClosePipes();
+
+    if (ConfigOsdOverlay) {
+	DisableDummyDevice();
+	VideoExit();
+    }
+}
+
+/**
+**	Is external player still running?
+*/
+int PlayerIsRunning(void)
+{
+    pid_t wpid;
+    int status;
+
+    if (!PlayerPid) {			// no player
+	return 0;
+    }
+
+    wpid = waitpid(PlayerPid, &status, WNOHANG);
+    if (wpid <= 0) {
+	return 1;
+    }
+    if (WIFEXITED(status)) {
+	Debug(3, "play: player exited (%d)\n", WEXITSTATUS(status));
+    }
+    if (WIFSIGNALED(status)) {
+	Debug(3, "play: player killed (%d)\n", WTERMSIG(status));
+    }
+    PlayerPid = 0;
+    return 0;
+}
+
+/**
+**	Set player volume.
+**
+**	@param volume	new volume (0..255)
+*/
+void PlayerSetVolume(int volume)
+{
+    Debug(3, "player: set volume=%d\n", volume);
+
+    if (PlayerVolume != volume) {
+	PlayerVolume = volume;
+	if (PlayerPid) {
+	    PlayerSendVolume();
+	}
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//	Device/Plugin C part
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+**	Return command line help string.
+*/
+const char *CommandLineHelp(void)
+{
+    return "  -% device\tmplayer dvd device\n"
+	"  -/\t/dir\tbrowser root directory\n"
+	"  -a audio\tmplayer -ao (alsa:device=hw=0.0) overwrites mplayer.conf\n"
+	"  -d display\tX11 display (default :0.0) overwrites $DISPLAY\n"
+	"  -f\t\tmplayer fullscreen playback\n"
+	"  -g geometry\tx11 window geometry wxh+x+y\n"
+	"  -k colorkey\tvideo color key (default=0x020507, mplayer2=0x76B901)\n"
+	"  -m mplayer\tfilename of mplayer executable\n"
+	"  -M args\targuments for mplayer\n"
+	"  -o\t\tosd overlay experiments\n" "  -s\t\tmplayer slave mode\n"
+	"  -v video\tmplayer -vo (vdpau:deint=4:hqscaling=1) overwrites mplayer.conf\n";
+}
+
+/**
+**	Process the command line arguments.
+**
+**	@param argc	number of arguments
+**	@param argv	arguments vector
+*/
+int ProcessArgs(int argc, char *const argv[])
+{
+    const char *s;
+
+    //
+    //	Parse arguments.
+    //
+#ifdef __FreeBSD__
+    if (!strcmp(*argv, "play")) {
+	++argv;
+	--argc;
+    }
+#endif
+    if ((s = getenv("DISPLAY"))) {
+	ConfigX11Display = s;
+    }
+
+    for (;;) {
+	switch (getopt(argc, argv, "-%:/:a:b:d:fg:k:m:M:osv:")) {
+	    case '%':			// dvd-device
+		ConfigMplayerDevice = optarg;
+		continue;
+	    case '/':			// browser root
+		ConfigBrowserRoot = optarg;
+		continue;
+	    case 'a':			// audio out
+		ConfigAudioOut = optarg;
+		continue;
+	    case 'd':			// display x11
+		ConfigX11Display = optarg;
+		continue;
+	    case 'f':			// fullscreen mode
+		ConfigFullscreen = 1;
+		continue;
+	    case 'g':			// geometry
+		VideoSetGeometry(optarg);
+		continue;
+	    case 'k':			// color key
+		ConfigColorKey = strtol(optarg, NULL, 0);
+		continue;
+	    case 'm':			// mplayer executable
+		ConfigMplayer = optarg;
+		continue;
+	    case 'M':			// mplayer extra arguments
+		ConfigMplayerArguments = optarg;
+		continue;
+	    case 'o':			// osd / overlay
+		ConfigOsdOverlay = 1;
+		continue;
+	    case 's':			// slave mode
+		ConfigUseSlave = 1;
+		continue;
+	    case 'v':			// video out
+		ConfigVideoOut = optarg;
+		continue;
+	    case EOF:
+		break;
+	    case '-':
+		fprintf(stderr, _("We need no long options\n"));
+		return 0;
+	    case ':':
+		fprintf(stderr, _("Missing argument for option '%c'\n"),
+		    optopt);
+		return 0;
+	    default:
+		fprintf(stderr, _("Unkown option '%c'\n"), optopt);
+		return 0;
+	}
+	break;
+    }
+    while (optind < argc) {
+	fprintf(stderr, _("Unhandled argument '%s'\n"), argv[optind++]);
+    }
+
+    return 1;
+}
diff --git a/player.h b/player.h
new file mode 100644
index 0000000..0c86be2
--- /dev/null
+++ b/player.h
@@ -0,0 +1,160 @@
+///
+///	@file player.h	@brief A play plugin header file.
+///
+///	Copyright (c) 2012, 2013 by Johns.  All Rights Reserved.
+///
+///	Contributor(s): Dennis Bendlin
+///
+///	License: AGPLv3
+///
+///	This program is free software: you can redistribute it and/or modify
+///	it under the terms of the GNU Affero General Public License as
+///	published by the Free Software Foundation, either version 3 of the
+///	License.
+///
+///	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 Affero General Public License for more details.
+///
+///	$Id$
+//////////////////////////////////////////////////////////////////////////////
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+    /// C callback feed key press
+    extern void FeedKeyPress(const char *, const char *, int, int);
+
+    /// C callback enable dummy device
+    extern void EnableDummyDevice(void);
+    /// C callback disable dummy device
+    extern void DisableDummyDevice(void);
+
+    /// C plugin get osd size and ascpect
+    extern void GetOsdSize(int *, int *, double *);
+
+    /// C plugin open osd
+    extern void OsdOpen(void);
+    /// C plugin close osd
+    extern void OsdClose(void);
+    /// C plugin clear osd
+    extern void OsdClear(void);
+    /// C plugin draw osd pixmap
+    extern void OsdDrawARGB(int, int, int, int, const uint8_t *);
+
+    /// C plugin play audio packet
+    extern int PlayAudio(const uint8_t *, int, uint8_t);
+    /// C plugin play TS audio packet
+    extern int PlayTsAudio(const uint8_t *, int);
+    /// C plugin set audio volume
+    extern void SetVolumeDevice(int);
+
+    /// C plugin play video packet
+    extern int PlayVideo(const uint8_t *, int);
+    /// C plugin play TS video packet
+    extern void PlayTsVideo(const uint8_t *, int);
+    /// C plugin grab an image
+    extern uint8_t *GrabImage(int *, int, int, int, int);
+
+    /// C plugin set play mode
+    extern int SetPlayMode(int);
+    /// C plugin get current system time counter
+    extern int64_t GetSTC(void);
+    /// C plugin get video stream size and aspect
+    extern void GetVideoSize(int *, int *, double *);
+    /// C plugin set trick speed
+    extern void TrickSpeed(int);
+    /// C plugin clears all video and audio data from the device
+    extern void Clear(void);
+    /// C plugin sets the device into play mode
+    extern void Play(void);
+    /// C plugin sets the device into "freeze frame" mode
+    extern void Freeze(void);
+    /// C plugin mute audio
+    extern void Mute(void);
+    /// C plugin display I-frame as a still picture.
+    extern void StillPicture(const uint8_t *, int);
+    /// C plugin poll if ready
+    extern int Poll(int);
+    /// C plugin flush output buffers
+    extern int Flush(int);
+
+    /// C plugin command line help
+    extern const char *CommandLineHelp(void);
+    /// C plugin process the command line arguments
+    extern int ProcessArgs(int, char *const[]);
+
+    /// C plugin exit + cleanup
+    extern void PlayExit(void);
+    /// C plugin start code
+    extern int Start(void);
+    /// C plugin stop code
+    extern void Stop(void);
+    /// C plugin house keeping
+    extern void Housekeeping(void);
+    /// C plugin main thread hook
+    extern void MainThreadHook(void);
+
+    /// Browser root=start directory
+    extern const char *ConfigBrowserRoot;
+    ///< Disable remote during external play
+    extern char ConfigDisableRemote;
+    extern const char *X11DisplayName;	///< x11 display name
+    extern char PlayerDvdNav;		///< dvdnav active
+    extern char PlayerPaused;		///< player paused
+    extern char PlayerSpeed;		///< player playback speed
+    extern int PlayerCurrent;		///< current postion in seconds
+    extern int PlayerTotal;		///< total length in seconds
+    extern char PlayerTitle[256];	///< title from meta data
+    extern char PlayerFilename[256];	///< filename
+
+    /// Start external player
+    extern void PlayerStart(const char *name);
+    /// Stop external player
+    extern void PlayerStop(void);
+    /// Is external player still running
+    extern int PlayerIsRunning(void);
+
+    /// Set player volume
+    extern void PlayerSetVolume(int);
+
+    /// Player send quit command
+    extern void PlayerSendQuit(void);
+    /// Player send toggle pause command
+    extern void PlayerSendPause(void);
+    /// Player send set play speed
+    extern void PlayerSendSetSpeed(int);
+    /// Player send seek
+    extern void PlayerSendSeek(int);
+    /// Player send switch audio track
+    extern void PlayerSendSwitchAudio(void);
+    /// Player send select subtitle
+    extern void PlayerSendSubSelect(void);
+    /// Player send dvd-nav up
+    extern void PlayerSendDvdNavUp(void);
+    /// Player send dvd-nav down
+    extern void PlayerSendDvdNavDown(void);
+    /// Player send dvd-nav left
+    extern void PlayerSendDvdNavLeft(void);
+    /// Player send dvd-nav right
+    extern void PlayerSendDvdNavRight(void);
+    /// Player send dvd-nav menu select
+    extern void PlayerSendDvdNavSelect(void);
+    /// Player send dvd-nav menu prev
+    extern void PlayerSendDvdNavPrev(void);
+    /// Player send dvd-nav prev
+    extern void PlayerSendDvdNavMenu(void);
+    /// Get length in seconds.
+    extern void PlayerGetLength(void);
+    /// Get current position in seconds.
+    extern void PlayerGetCurrentPosition(void);
+    /// Get title from meta data.
+    extern void PlayerGetMetaTitle(void);
+    /// Get filename.
+    extern void PlayerGetFilename(void);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/po/.gitignore b/po/.gitignore
new file mode 100644
index 0000000..1952639
--- /dev/null
+++ b/po/.gitignore
@@ -0,0 +1,3 @@
+# gitignore(5) file
+*.mo
+*.pot
diff --git a/readdir.c b/readdir.c
new file mode 100644
index 0000000..f150783
--- /dev/null
+++ b/readdir.c
@@ -0,0 +1,401 @@
+///
+///	@file readdir.c		@brief directory reading module
+///
+///	Copyright (c) 2012, 2013 by Johns.  All Rights Reserved.
+///
+///	Contributor(s):
+///
+///	License: AGPLv3
+///
+///	This program is free software: you can redistribute it and/or modify
+///	it under the terms of the GNU Affero General Public License as
+///	published by the Free Software Foundation, either version 3 of the
+///	License.
+///
+///	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 Affero General Public License for more details.
+///
+///	$Id$
+//////////////////////////////////////////////////////////////////////////////
+
+#define __USE_ZZIPLIB			///< zip archives support
+#define __USE_AVFS			///< A Virtual File System support
+
+#ifdef USE_AVFS
+#error "Version 1.0.1 of AVFS has memory corruption".
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <dirent.h>
+
+#include <libintl.h>
+#define _(str) gettext(str)		///< gettext shortcut
+#define _N(str) str			///< gettext_noop shortcut
+
+#ifdef USE_ZZIPLIB
+#include <zzip/lib.h>
+#endif
+#ifdef USE_AVFS
+#include <virtual.h>
+#else
+#define virt_stat	stat		///< universal stat
+#define virt_fstat	fstat		///< universal fstat
+#define virt_opendir	opendir		///< universal opendir
+#define virt_readdir	readdir		///< universal readdir
+#define virt_closedir	closedir	///< universal closedir
+#endif
+
+#include "misc.h"
+#include "readdir.h"
+
+//////////////////////////////////////////////////////////////////////////////
+//	Variables
+//////////////////////////////////////////////////////////////////////////////
+
+const char ConfigShowHiddenFiles = 0;	///< config show hidden files
+static const char *BaseDir;		///< current directory
+static int BaseDirLen;			///< length of current directory name
+static const NameFilter *NameFilters;	///< current name filter table
+
+//////////////////////////////////////////////////////////////////////////////
+//	Functions
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+**	Check if filename is a directory.
+**
+**	@param filename	path and file name
+**
+**	@retval	true	directory
+**	@retval false	no directory
+*/
+int IsDirectory(const char *filename)
+{
+    struct stat stat_buf;
+
+    if (virt_stat(filename, &stat_buf) < 0) {
+	Error("play/readdir: can't stat '%s': %s\n", filename,
+	    strerror(errno));
+	return -1;
+    }
+    return S_ISDIR(stat_buf.st_mode);
+}
+
+/**
+**	Check if filename is an archive.
+**
+**	@param filename	path and file name
+**
+**	@retval	true	archive
+**	@retval false	no archive
+*/
+int IsArchive(const char *filename)
+{
+#ifdef USE_AVFS
+
+    /**
+    **	Table of supported archive suffixes.
+    */
+    static const NameFilter ArchiveFilters[] = {
+#define FILTER(x) { sizeof(x) - 1, x }
+	FILTER(".cbz"),
+	FILTER(".cbr"),
+	FILTER(".zip"),
+	FILTER(".rar"),
+	FILTER(".tar"),
+	FILTER(".tar.gz"),
+	FILTER(".tgz"),
+#undef FILTER
+	{0, NULL}
+    };
+    int i;
+    int len;
+
+    len = strlen(filename);
+    for (i = 0; ArchiveFilters[i].String; ++i) {
+	if (len >= ArchiveFilters[i].Length
+	    && !strcasecmp(filename + len - ArchiveFilters[i].Length,
+		ArchiveFilters[i].String)) {
+	    return 1;
+	}
+    }
+#else
+    (void)filename;
+#endif
+    return 0;
+}
+
+/**
+**	Filter for scandir, only directories.
+**
+**	@param dirent	current directory entry
+**
+**	@returns true if the @p dirent is a directories.
+*/
+static int FilterIsDirectory(const struct dirent *dirent)
+{
+    char *tmp;
+    int dir;
+    size_t len;
+
+    len = _D_EXACT_NAMLEN(dirent);
+    if (len && dirent->d_name[0] == '.') {
+	// hide hidden files
+	if (!ConfigShowHiddenFiles) {
+	    return 0;
+	}
+	// ignore . and ..
+	if (len == 1 || (len == 2 && dirent->d_name[1] == '.')) {
+	    return 0;
+	}
+    }
+#ifdef _DIRENT_HAVE_D_TYPE
+    if (dirent->d_type == DT_DIR) {	// only directories files
+	return 1;
+#ifdef DT_LNK
+    } else if (dirent->d_type == DT_LNK) {	// symbolic link
+#endif
+    } else if (dirent->d_type != DT_UNKNOWN) {	// no looser filesystem
+	return 0;
+    }
+#endif
+
+    // DT_UNKOWN or DT_LNK
+    tmp = malloc(BaseDirLen + len + 1);
+    stpcpy(stpcpy(tmp, BaseDir), dirent->d_name);
+    dir = IsDirectory(tmp);
+    free(tmp);
+    return dir;
+}
+
+/**
+**	Filter for scandir, only files.
+**
+**	@param dirent	current directory entry
+**
+**	@returns true if the @p dirent is a video.
+*/
+static int FilterIsFile(const struct dirent *dirent)
+{
+    char *tmp;
+    int dir;
+    int len;
+    int i;
+
+    len = _D_EXACT_NAMLEN(dirent);
+    if (len && dirent->d_name[0] == '.') {
+	if (!ConfigShowHiddenFiles) {	// hide hidden files
+	    return 0;
+	}
+    }
+    // look through name filter table
+    if (NameFilters) {
+	for (i = 0; NameFilters[i].String; ++i) {
+	    if (len >= NameFilters[i].Length
+		&& !strcasecmp(dirent->d_name + len - NameFilters[i].Length,
+		    NameFilters[i].String)) {
+		goto found;
+	    }
+	}
+	// no file name matched
+	return 0;
+    }
+  found:
+
+#ifdef _DIRENT_HAVE_D_TYPE
+    if (dirent->d_type == DT_REG) {	// only regular files
+	return 1;
+#ifdef DT_LNK
+    } else if (dirent->d_type == DT_LNK) {	// symbolic link
+#endif
+    } else if (dirent->d_type != DT_UNKNOWN) {	// no looser filesystem
+	return 0;
+    }
+#endif
+
+    // DT_UNKOWN or DT_LNK
+    tmp = malloc(BaseDirLen + len + 1);
+    stpcpy(stpcpy(tmp, BaseDir), dirent->d_name);
+    dir = IsDirectory(tmp);
+    free(tmp);
+    return !dir;
+}
+
+/**
+**	ScanDirectory qsort compare function.
+**
+**	@param s1	table index 1
+**	@param s2	table index 2
+**
+**	@returns an integer less than, equal to, or greater than zero if s1
+**	is found, respectively, to be less than, to match, or be greater
+**	than s2.
+*/
+static int q_cmp(const void *s1, const void *s2)
+{
+    return strcmp(*(char *const *)s1, *(char *const *)s2);
+}
+
+/**
+**	Scan a directory for matching entries.
+**
+**	@param name		directory path and name
+**	@param flag_dir		only directories or files
+**	@param filter		list of name suffix filters
+**	@param[out] namelist	list of matching names in directory
+**
+**	@retval <0	if any error occurs
+**	@retval 0	empty directory, no errors occurs
+**	@retval n	number of files
+**
+**	@todo support reading and sorting files and directories
+**	@todo flag disable sort
+*/
+int ScanDirectory(const char *name, int flag_dir, const NameFilter * filter,
+    char ***namelist)
+{
+    DIR *dir;
+    struct dirent *entry;
+    struct stat stat_buf;
+    int n;
+    char **names;
+    int arraysz;
+    int save;
+
+    Debug(3, "play/scandir: scan directory '%s'\n", name);
+
+    // FIXME: threads remove global variables
+    BaseDir = name;
+    BaseDirLen = strlen(BaseDir);
+    NameFilters = filter;
+
+    if (!(dir = virt_opendir(name))) {
+	Error("play/scandir: can't open dir '%s': %s\n", name,
+	    strerror(errno));
+	return -1;
+    }
+    if (virt_fstat(dirfd(dir), &stat_buf) < 0) {
+	Error("play/scandir: can't stat dir '%s': %s\n", name,
+	    strerror(errno));
+	return -1;
+    }
+    // approximate size of name array
+    if (stat_buf.st_size) {
+	arraysz = stat_buf.st_size / (3 * 8);
+    } else {
+	arraysz = 16;
+    }
+    if (!(names = malloc(arraysz * sizeof(*names)))) {
+	Error("play/scandir: dir '%s': out of memory\n", name);
+	return -1;
+    }
+
+    n = 0;
+    errno = 0;
+    while ((entry = virt_readdir(dir))) {
+	int len;
+	char *tmp;
+
+	// skip hidden files, wrong kind, wrong suffix
+	save = errno;
+	if (flag_dir ? !FilterIsDirectory(entry) : !FilterIsFile(entry)) {
+	    errno = save;
+	    continue;
+	}
+	errno = save;
+
+	len = _D_ALLOC_NAMLEN(entry);
+	if (!(tmp = malloc(len))) {
+	    Error("play/scandir: dir '%s': out of memory\n", name);
+	    break;
+	}
+	memcpy(tmp, entry->d_name, len);
+
+	if (++n >= arraysz) {		// array full
+	    char **new;
+
+	    if (virt_fstat(dirfd(dir), &stat_buf) < 0) {
+		Error("play/scandir: can't stat dir '%s': %s\n", name,
+		    strerror(errno));
+		--n;
+		break;
+	    }
+	    // dir size grown and valid
+	    if (stat_buf.st_size / (3 * 4) > arraysz) {
+		arraysz = stat_buf.st_size / (3 * 4);
+	    } else {
+		arraysz *= 2;
+	    }
+	    if (!(new = realloc(names, arraysz * sizeof(*names)))) {
+		Error("play/scandir: dir '%s': out of memory\n", name);
+		--n;
+		break;
+	    }
+	    names = new;
+	}
+	names[n - 1] = tmp;
+    }
+
+    save = errno;
+    virt_closedir(dir);
+
+    if (save) {				// error happened
+	while (n > 0) {			// free used memory
+	    free(names[--n]);
+	}
+	free(names);
+	errno = save;
+	*namelist = NULL;
+	return -1;
+    }
+    // sort names
+    qsort(names, n, sizeof(*names), (int (*)(const void *,
+		const void *))q_cmp);
+    *namelist = names;
+
+    return n;
+}
+
+/**
+**	Read directory for menu.
+**
+**	@param name	directory path and name
+**	@param flag_dir	only directories or files
+**	@param filter	list of name suffix filters
+**	@param cb_add	call back to handle directory entries
+**	@param opaque	privat parameter for the call back
+**
+**	@retval <0	if any error occurs
+**	@retval false	if no errors occurs
+*/
+int ReadDirectory(const char *name, int flag_dir, const NameFilter * filter,
+    void (*cb_add) (void *, const char *), void *opaque)
+{
+    int i;
+    int n;
+    char **names;
+
+    n = ScanDirectory(name, flag_dir, filter, &names);
+    if (n >= 0) {
+
+	for (i = 0; i < n; ++i) {	// add names to menu
+	    cb_add(opaque, names[i]);
+	    free(names[i]);
+	}
+
+	free(names);
+    }
+
+    return n;
+}
diff --git a/readdir.h b/readdir.h
new file mode 100644
index 0000000..e8fa22e
--- /dev/null
+++ b/readdir.h
@@ -0,0 +1,43 @@
+///
+///	@file readdir.h		@brief directory reading module header file
+///
+///	Copyright (c) 2012 by Johns.  All Rights Reserved.
+///
+///	Contributor(s):
+///
+///	License: AGPLv3
+///
+///	This program is free software: you can redistribute it and/or modify
+///	it under the terms of the GNU Affero General Public License as
+///	published by the Free Software Foundation, either version 3 of the
+///	License.
+///
+///	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 Affero General Public License for more details.
+///
+///	$Id$
+//////////////////////////////////////////////////////////////////////////////
+
+///
+///	Readdir name filter typedef
+///
+typedef struct __name_filter_
+{
+    int Length;				///< filter string length
+    const char *String;			///< filter string
+} NameFilter;
+
+    /// check if filename is a directory
+extern int IsDirectory(const char *);
+
+    /// check if filename is an archive
+extern int IsArchive(const char *);
+
+    /// scan a directory
+extern int ScanDirectory(const char *, int, const NameFilter *, char ***);
+
+    /// read a directory
+extern int ReadDirectory(const char *, int, const NameFilter *,
+    void (*cb_add) (void *, const char *), void *);
diff --git a/vdr-play-9999-pre1.7.36.ebuild b/vdr-play-9999-pre1.7.36.ebuild
new file mode 100644
index 0000000..cb0a331
--- /dev/null
+++ b/vdr-play-9999-pre1.7.36.ebuild
@@ -0,0 +1,63 @@
+# Copyright 1999-2012 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: $
+
+EAPI="4"
+
+inherit flag-o-matic toolchain-funcs vdr-plugin eutils
+
+if [ ${PV} == "9999" ] ; then
+		inherit git-2
+		EGIT_REPO_URI="git://projects.vdr-developer.org/vdr-plugin-play.git"
+		KEYWORDS=""
+else
+		SRC_URI="mirror://vdr-developerorg/1010/${P}.tgz"
+		KEYWORDS="~x86 ~amd64"
+fi
+
+DESCRIPTION="A mediaplayer plugin for VDR."
+HOMEPAGE="http://projects.vdr-developer.org/projects/show/plg-play"
+
+LICENSE="AGPL-3"
+SLOT="0"
+IUSE="jpeg png avfs swscale"
+
+RDEPEND=">=media-video/vdr-1.7
+		>=x11-libs/libxcb-1.8
+		x11-libs/xcb-util
+		x11-libs/xcb-util-image
+		x11-libs/xcb-util-keysyms
+		x11-libs/xcb-util-wm
+		|| ( media-video/mplayer media-video/mplayer2 )
+		jpeg? ( virtual/jpeg )
+		png? ( media-libs/libpng )
+		avfs? ( sys-fs/avfs )
+		swscale? ( >=virtual/ffmpeg-0.7 )"
+DEPEND="${RDEPEND}
+		x11-proto/xproto
+		sys-devel/gettext
+		dev-util/pkgconfig
+		sys-devel/make"
+
+src_prepare() {
+		vdr-plugin_src_prepare
+}
+
+src_compile() {
+		local myconf
+
+		myconf="-DHAVE_PTHREAD_NAME"
+		use jpeg && myconf="${myconf} -DUSE_JPEG"
+		use png && myconf="${myconf} -DUSE_PNG"
+		use avfs && myconf="${myconf} -DUSE_AVFS"
+		use swscale && myconf="${myconf} -DUSE_SWSCALE"
+
+		emake all CC="$(tc-getCC)" CFLAGS="${CFLAGS}" \
+			LDFLAGS="${LDFLAGS}" CONFIG="${myconf}" LIBDIR="." || die
+}
+
+src_install() {
+		vdr-plugin_src_install
+
+		dodoc ChangeLog README.txt
+}
diff --git a/video.c b/video.c
new file mode 100644
index 0000000..54d1179
--- /dev/null
+++ b/video.c
@@ -0,0 +1,775 @@
+///
+///	@file video.c	@brief Video module
+///
+///	Copyright (c) 2012, 2013 by Johns.  All Rights Reserved.
+///
+///	Contributor(s):
+///
+///	License: AGPLv3
+///
+///	This program is free software: you can redistribute it and/or modify
+///	it under the terms of the GNU Affero General Public License as
+///	published by the Free Software Foundation, either version 3 of the
+///	License.
+///
+///	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 Affero General Public License for more details.
+///
+///	$Id$
+//////////////////////////////////////////////////////////////////////////////
+
+///
+///	@defgroup Video The video module.
+///
+///	This module contains all video rendering functions.
+///
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <libintl.h>
+#define _(str) gettext(str)		///< gettext shortcut
+#define _N(str) str			///< gettext_noop shortcut
+
+#include "misc.h"
+#include "video.h"
+
+//////////////////////////////////////////////////////////////////////////////
+//	X11 / XCB
+//////////////////////////////////////////////////////////////////////////////
+
+#include <xcb/xcb.h>
+#include <xcb/xcb_icccm.h>
+#include <xcb/xcb_image.h>
+#include <xcb/xcb_pixel.h>
+#include <xcb/xcb_event.h>
+#include <xcb/xcb_keysyms.h>
+#include <X11/keysym.h>			// keysym XK_
+#include <X11/XF86keysym.h>		// XF86XK_
+#include <poll.h>
+
+static xcb_connection_t *Connection;	///< xcb connection
+static xcb_colormap_t VideoColormap;	///< video colormap
+static xcb_window_t VideoOsdWindow;	///< video osd window
+static xcb_window_t VideoPlayWindow;	///< video player window
+static xcb_screen_t const *VideoScreen;	///< video screen
+static uint32_t VideoBlankTick;		///< blank cursor timer
+static xcb_pixmap_t VideoPixmap;	///< blank cursor pixmap
+static xcb_cursor_t VideoBlankCursor;	///< empty invisible cursor
+
+static uint32_t VideoColorKey;		///< color key pixel value
+
+static int VideoWindowX;		///< video output window x coordinate
+static int VideoWindowY;		///< video outout window y coordinate
+static unsigned VideoWindowWidth;	///< video output window width
+static unsigned VideoWindowHeight;	///< video output window height
+
+static char Osd3DMode;			///< 3D OSD mode
+
+///
+///	Create X11 window.
+///
+///	@param parent	parent of new window
+///	@param visual	visual of parent
+///	@param depth	depth of parent
+///
+///	@returns created X11 window id
+///
+static xcb_window_t VideoCreateWindow(xcb_window_t parent,
+    xcb_visualid_t visual, uint8_t depth)
+{
+    uint32_t values[5];
+    xcb_window_t window;
+
+    Debug(3, "video: visual %#0x depth %d\n", visual, depth);
+
+    //
+    // create color map
+    //
+    if (VideoColormap == XCB_NONE) {
+	VideoColormap = xcb_generate_id(Connection);
+	xcb_create_colormap(Connection, XCB_COLORMAP_ALLOC_NONE, VideoColormap,
+	    parent, visual);
+    }
+    //
+    //	create blank cursor
+    //
+    if (VideoBlankCursor == XCB_NONE) {
+	VideoPixmap = xcb_generate_id(Connection);
+	xcb_create_pixmap(Connection, 1, VideoPixmap, parent, 1, 1);
+	VideoBlankCursor = xcb_generate_id(Connection);
+	xcb_create_cursor(Connection, VideoBlankCursor, VideoPixmap,
+	    VideoPixmap, 0, 0, 0, 0, 0, 0, 1, 1);
+	VideoBlankTick = 0;
+    }
+
+    values[0] = VideoColorKey;		// ARGB
+    values[1] = VideoColorKey;
+    values[2] =
+	XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE |
+	XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE |
+	XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_EXPOSURE |
+	XCB_EVENT_MASK_STRUCTURE_NOTIFY;
+    values[3] = VideoColormap;
+    values[4] = VideoBlankCursor;
+    window = xcb_generate_id(Connection);
+    xcb_create_window(Connection, depth, window, parent, VideoWindowX,
+	VideoWindowY, VideoWindowWidth, VideoWindowHeight, 0,
+	XCB_WINDOW_CLASS_INPUT_OUTPUT, visual,
+	XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK |
+	XCB_CW_COLORMAP | XCB_CW_CURSOR, values);
+
+    // define only available with xcb-utils-0.3.8
+#ifdef XCB_ICCCM_NUM_WM_SIZE_HINTS_ELEMENTS
+    // FIXME: utf _NET_WM_NAME
+    xcb_icccm_set_wm_name(Connection, window, XCB_ATOM_STRING, 8,
+	sizeof("play control") - 1, "play control");
+    xcb_icccm_set_wm_icon_name(Connection, window, XCB_ATOM_STRING, 8,
+	sizeof("play control") - 1, "play control");
+#endif
+    // define only available with xcb-utils-0.3.6
+#ifdef XCB_NUM_WM_HINTS_ELEMENTS
+    // FIXME: utf _NET_WM_NAME
+    xcb_set_wm_name(Connection, window, XCB_ATOM_STRING,
+	sizeof("play control") - 1, "play control");
+    xcb_set_wm_icon_name(Connection, window, XCB_ATOM_STRING,
+	sizeof("play control") - 1, "play control");
+#endif
+
+    // FIXME: size hints
+
+    // window above parent
+    values[0] = parent;
+    values[1] = XCB_STACK_MODE_ABOVE;
+    xcb_configure_window(Connection, window,
+	XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, values);
+
+    return window;
+}
+
+///
+///	Enable OSD 3d mode.
+///
+///	@param mode	turn 3d mode on/off
+///
+void VideoSetOsd3DMode(int mode)
+{
+    Osd3DMode = mode;
+}
+
+///
+///	Draw a ARGB image.
+///
+///	@param x	x position of image in osd
+///	@param y	y position of image in osd
+///	@param width	width of image
+///	@param height	height of image
+///	@param argb	argb image
+///
+void VideoDrawARGB(int x, int y, int width, int height, const uint8_t * argb)
+{
+    xcb_image_t *xcb_image;
+    xcb_gcontext_t gc;
+    int sx;
+    int sy;
+    int fs;
+
+    if (!Connection) {
+	Debug(3, "play: FIXME: must restore osd provider\n");
+	return;
+    }
+
+    if (x + y < 1 && (unsigned)height == VideoWindowHeight
+	&& (unsigned)width == VideoWindowWidth) {
+	fs = 1;
+    } else {
+	fs = 0;
+    }
+
+    gc = xcb_generate_id(Connection);
+    xcb_create_gc(Connection, gc, VideoOsdWindow, 0, NULL);
+
+    switch (Osd3DMode) {
+	case 1:			// SBS
+	    xcb_image =
+		xcb_image_create_native(Connection, width / 2, height,
+		XCB_IMAGE_FORMAT_Z_PIXMAP, VideoScreen->root_depth, NULL, 0L,
+		NULL);
+	    break;
+	case 2:			// TB
+	    xcb_image =
+		xcb_image_create_native(Connection, width, height / 2,
+		XCB_IMAGE_FORMAT_Z_PIXMAP, VideoScreen->root_depth, NULL, 0L,
+		NULL);
+	    break;
+	default:
+	    xcb_image =
+		xcb_image_create_native(Connection, width, height,
+		XCB_IMAGE_FORMAT_Z_PIXMAP, VideoScreen->root_depth, NULL, 0L,
+		NULL);
+    }
+
+    //	fast 32it versions
+    if (xcb_image->bpp == 32) {
+	if (xcb_image->byte_order == XCB_IMAGE_ORDER_LSB_FIRST) {
+	    for (sy = 0; sy < height; ++sy) {
+		for (sx = 0; sx < width; ++sx) {
+		    uint32_t pixel;
+
+		    if (argb[(width * sy + sx) * 4 + 3] < 200) {
+			pixel = VideoColorKey;
+		    } else {
+			pixel = argb[(width * sy + sx) * 4 + 0] << 0;
+			pixel |= argb[(width * sy + sx) * 4 + 1] << 8;
+			pixel |= argb[(width * sy + sx) * 4 + 2] << 16;
+		    }
+		    switch (Osd3DMode) {
+			case 1:	// SBS
+			    xcb_image_put_pixel_Z32L(xcb_image, sx / 2, sy,
+				pixel);
+			    break;
+			case 2:	// TB
+			    xcb_image_put_pixel_Z32L(xcb_image, sx, sy / 2,
+				pixel);
+			    break;
+			default:
+			    xcb_image_put_pixel_Z32L(xcb_image, sx, sy, pixel);
+		    }
+		}
+	    }
+	} else {
+	    Error(_("play: unsupported put_image\n"));
+	}
+    } else {
+	for (sy = 0; sy < height; ++sy) {
+	    for (sx = 0; sx < width; ++sx) {
+		uint32_t pixel;
+
+		if (argb[(width * sy + sx) * 4 + 3] < 200) {
+		    pixel = argb[(width * sy + sx) * 4 + 3] << 0;
+		    pixel |= argb[(width * sy + sx) * 4 + 3] << 8;
+		    pixel |= argb[(width * sy + sx) * 4 + 3] << 16;
+		} else {
+		    pixel = argb[(width * sy + sx) * 4 + 0] << 0;
+		    pixel |= argb[(width * sy + sx) * 4 + 1] << 8;
+		    pixel |= argb[(width * sy + sx) * 4 + 2] << 16;
+		}
+		switch (Osd3DMode) {
+		    case 1:		// SBS
+			xcb_image_put_pixel(xcb_image, sx / 2, sy, pixel);
+			break;
+		    case 2:		// TB
+			xcb_image_put_pixel(xcb_image, sx, sy / 2, pixel);
+			break;
+		    default:
+			xcb_image_put_pixel(xcb_image, sx, sy, pixel);
+		}
+	    }
+	}
+    }
+
+    // render xcb_image to color data pixmap
+    switch (Osd3DMode) {
+	case 1:			// SBS
+	    if (fs) {
+		xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image, x, y,
+		    0);
+		xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image,
+		    x + VideoWindowWidth / 2, y, 0);
+	    } else {
+		xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image, x / 2,
+		    y, 0);
+		xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image,
+		    x / 2 + VideoWindowWidth / 2, y, 0);
+	    }
+	    break;
+	case 2:			// TB
+	    if (fs) {
+		xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image, x, y,
+		    0);
+		xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image, x,
+		    y + VideoWindowHeight / 2, 0);
+	    } else {
+		xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image, x,
+		    y / 2, 0);
+		xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image, x,
+		    y / 2 + VideoWindowHeight / 2, 0);
+	    }
+	    break;
+	default:
+	    xcb_image_put(Connection, VideoOsdWindow, gc, xcb_image, x, y, 0);
+    }
+    // release xcb_image
+    xcb_image_destroy(xcb_image);
+    xcb_free_gc(Connection, gc);
+    xcb_flush(Connection);
+}
+
+///
+///	Show window.
+///
+void VideoWindowShow(void)
+{
+    if (!Connection) {
+	Debug(3, "play: FIXME: must restore osd provider\n");
+	return;
+    }
+    xcb_map_window(Connection, VideoOsdWindow);
+}
+
+///
+///	Hide window.
+///
+void VideoWindowHide(void)
+{
+    if (!Connection) {
+	Debug(3, "play: FIXME: must restore osd provider\n");
+	return;
+    }
+    xcb_unmap_window(Connection, VideoOsdWindow);
+}
+
+///
+///	Clear window.
+///
+void VideoWindowClear(void)
+{
+    if (!Connection) {
+	Debug(3, "play: FIXME: must restore osd provider\n");
+	return;
+    }
+    xcb_clear_area(Connection, 0, VideoOsdWindow, 0, 0, VideoWindowWidth,
+	VideoWindowHeight);
+    xcb_flush(Connection);
+}
+
+static xcb_key_symbols_t *XcbKeySymbols;	///< Keyboard symbols
+static uint16_t NumLockMask;		///< mod mask for num-lock
+static uint16_t ShiftLockMask;		///< mod mask for shift-lock
+static uint16_t CapsLockMask;		///< mod mask for caps-lock
+static uint16_t ModeSwitchMask;		///< mod mask for mode-switch
+
+///
+///	Handle key press event.
+///
+static void VideoKeyPress(const xcb_key_press_event_t * event)
+{
+    char buf[2];
+    xcb_keysym_t ks0;
+    xcb_keysym_t ks1;
+    xcb_keysym_t keysym;
+    xcb_keycode_t keycode;
+    unsigned modifier;
+
+    if (!XcbKeySymbols) {
+	XcbKeySymbols = xcb_key_symbols_alloc(Connection);
+	if (!XcbKeySymbols) {
+	    Error(_("play/event: can't read key symbols\n"));
+	    return;
+	}
+	NumLockMask = ShiftLockMask = CapsLockMask = ModeSwitchMask = 0;
+
+	// FIXME: lock and mode keys are not prepared!
+    }
+
+    keycode = event->detail;
+    modifier = event->state;
+    // handle mode-switch
+    if (modifier & ModeSwitchMask) {
+	ks0 = xcb_key_symbols_get_keysym(XcbKeySymbols, keycode, 2);
+	ks1 = xcb_key_symbols_get_keysym(XcbKeySymbols, keycode, 3);
+    } else {
+	ks0 = xcb_key_symbols_get_keysym(XcbKeySymbols, keycode, 0);
+	ks1 = xcb_key_symbols_get_keysym(XcbKeySymbols, keycode, 1);
+    }
+    // use first keysym, if second keysym didn't exists
+    if (ks1 == XCB_NO_SYMBOL) {
+	ks1 = ks0;
+    }
+    // see xcb-util-0.3.6/keysyms/keysyms.c:
+    if (!(modifier & XCB_MOD_MASK_SHIFT) && !(modifier & XCB_MOD_MASK_LOCK)) {
+	keysym = ks0;
+    } else {
+	// FIXME: more cases
+
+	keysym = ks0;
+    }
+
+    // FIXME: use xcb_lookup_string
+    switch (keysym) {
+	case XK_space:
+	    FeedKeyPress("XKeySym", "space", 0, 0);
+	    break;
+	case XK_exclam ... XK_slash:
+	case XK_0 ... XK_9:
+	case XK_A ... XK_Z:
+	case XK_a ... XK_z:
+	    buf[0] = keysym;
+	    buf[1] = '\0';
+	    FeedKeyPress("XKeySym", buf, 0, 0);
+	    break;
+
+	case XK_BackSpace:
+	    FeedKeyPress("XKeySym", "BackSpace", 0, 0);
+	    break;
+	case XK_Tab:
+	    FeedKeyPress("XKeySym", "Tab", 0, 0);
+	    break;
+	case XK_Return:
+	    FeedKeyPress("XKeySym", "Return", 0, 0);
+	    break;
+	case XK_Escape:
+	    FeedKeyPress("XKeySym", "Escape", 0, 0);
+	    break;
+	case XK_Delete:
+	    FeedKeyPress("XKeySym", "Delete", 0, 0);
+	    break;
+
+	case XK_Home:
+	    FeedKeyPress("XKeySym", "Home", 0, 0);
+	    break;
+	case XK_Left:
+	    FeedKeyPress("XKeySym", "Left", 0, 0);
+	    break;
+	case XK_Up:
+	    FeedKeyPress("XKeySym", "Up", 0, 0);
+	    break;
+	case XK_Right:
+	    FeedKeyPress("XKeySym", "Right", 0, 0);
+	    break;
+	case XK_Down:
+	    FeedKeyPress("XKeySym", "Down", 0, 0);
+	    break;
+	case XK_Page_Up:
+	    FeedKeyPress("XKeySym", "Page_Up", 0, 0);
+	    break;
+	case XK_Page_Down:
+	    FeedKeyPress("XKeySym", "Page_Down", 0, 0);
+	    break;
+	case XK_End:
+	    FeedKeyPress("XKeySym", "End", 0, 0);
+	    break;
+	case XK_Begin:
+	    FeedKeyPress("XKeySym", "Begin", 0, 0);
+	    break;
+
+	case XK_F1:
+	    FeedKeyPress("XKeySym", "F1", 0, 0);
+	    break;
+	case XK_F2:
+	    FeedKeyPress("XKeySym", "F2", 0, 0);
+	    break;
+	case XK_F3:
+	    FeedKeyPress("XKeySym", "F3", 0, 0);
+	    break;
+	case XK_F4:
+	    FeedKeyPress("XKeySym", "F4", 0, 0);
+	    break;
+	case XK_F5:
+	    FeedKeyPress("XKeySym", "F5", 0, 0);
+	    break;
+	case XK_F6:
+	    FeedKeyPress("XKeySym", "F6", 0, 0);
+	    break;
+	case XK_F7:
+	    FeedKeyPress("XKeySym", "F7", 0, 0);
+	    break;
+	case XK_F8:
+	    FeedKeyPress("XKeySym", "F8", 0, 0);
+	    break;
+	case XK_F9:
+	    FeedKeyPress("XKeySym", "F9", 0, 0);
+	    break;
+	case XK_F10:
+	    FeedKeyPress("XKeySym", "F10", 0, 0);
+	    break;
+	case XK_F11:
+	    FeedKeyPress("XKeySym", "F11", 0, 0);
+	    break;
+	case XK_F12:
+	    FeedKeyPress("XKeySym", "F12", 0, 0);
+	    break;
+
+	case XF86XK_Red:
+	    FeedKeyPress("XKeySym", "Red", 0, 0);
+	    break;
+	case XF86XK_Green:
+	    FeedKeyPress("XKeySym", "Green", 0, 0);
+	    break;
+	case XF86XK_Yellow:
+	    FeedKeyPress("XKeySym", "Yellow", 0, 0);
+	    break;
+	case XF86XK_Blue:
+	    FeedKeyPress("XKeySym", "Blue", 0, 0);
+	    break;
+
+	case XF86XK_HomePage:
+	    FeedKeyPress("XKeySym", "XF86HomePage", 0, 0);
+	    break;
+	case XF86XK_AudioLowerVolume:
+	    FeedKeyPress("XKeySym", "XF86AudioLowerVolume", 0, 0);
+	    break;
+	case XF86XK_AudioMute:
+	    FeedKeyPress("XKeySym", "XF86AudioMute", 0, 0);
+	    break;
+	case XF86XK_AudioRaiseVolume:
+	    FeedKeyPress("XKeySym", "XF86AudioRaiseVolume", 0, 0);
+	    break;
+	case XF86XK_AudioPlay:
+	    FeedKeyPress("XKeySym", "XF86AudioPlay", 0, 0);
+	    break;
+	case XF86XK_AudioStop:
+	    FeedKeyPress("XKeySym", "XF86AudioStop", 0, 0);
+	    break;
+	case XF86XK_AudioPrev:
+	    FeedKeyPress("XKeySym", "XF86AudioPrev", 0, 0);
+	    break;
+	case XF86XK_AudioNext:
+	    FeedKeyPress("XKeySym", "XF86AudioNext", 0, 0);
+	    break;
+
+	default:
+	    Debug(3, "play/event: keycode %d\n", event->detail);
+	    break;
+
+    }
+}
+
+///
+///	Poll video events.
+///
+///	@param timeout	timeout in milliseconds
+///
+void VideoPollEvents(int timeout)
+{
+    struct pollfd fds[1];
+    xcb_generic_event_t *event;
+    int n;
+    int delay;
+
+    if (!Connection) {
+	Debug(3, "play: poll without connection\n");
+	return;
+    }
+
+    fds[0].fd = xcb_get_file_descriptor(Connection);
+    fds[0].events = POLLIN | POLLPRI;
+
+    delay = timeout;
+    for (;;) {
+	xcb_flush(Connection);
+
+	// wait for events or timeout
+	// FIXME: this can poll forever
+	if ((n = poll(fds, 1, delay)) <= 0) {
+	    // error or timeout
+	    if (n) {			// error
+		Error(_("play/event: poll failed: %s\n"), strerror(errno));
+	    }
+	    return;
+	}
+	if (fds[0].revents & (POLLIN | POLLPRI)) {
+	    if ((event = xcb_poll_for_event(Connection))) {
+
+		switch (XCB_EVENT_RESPONSE_TYPE(event)) {
+#if 0
+			// background pixmap no need to redraw
+		    case XCB_EXPOSE:
+			// collapse multi expose
+			if (!((xcb_expose_event_t *) event)->count) {
+			    xcb_clear_area(Connection, 0, Window, 0, 0, 64,
+				64);
+			    // flush the request
+			    xcb_flush(Connection);
+			}
+			break;
+#endif
+		    case XCB_MAP_NOTIFY:
+			Debug(3, "video/event: MapNotify\n");
+			// hide cursor after mapping
+			xcb_change_window_attributes(Connection,
+			    VideoOsdWindow, XCB_CW_CURSOR, &VideoBlankCursor);
+			xcb_change_window_attributes(Connection,
+			    VideoPlayWindow, XCB_CW_CURSOR, &VideoBlankCursor);
+			break;
+		    case XCB_DESTROY_NOTIFY:
+			return;
+		    case XCB_KEY_PRESS:
+			VideoKeyPress((xcb_key_press_event_t *) event);
+			break;
+		    case XCB_KEY_RELEASE:
+		    case XCB_BUTTON_PRESS:
+		    case XCB_BUTTON_RELEASE:
+			break;
+		    case XCB_MOTION_NOTIFY:
+			break;
+
+		    case 0:
+			// error_code
+			Debug(3, "play/event: error %x\n",
+			    event->response_type);
+			break;
+		    default:
+			// unknown event type, ignore it
+			Debug(3, "play/event: unknown %x\n",
+			    event->response_type);
+			break;
+		}
+
+		free(event);
+	    } else {
+		// no event, can happen, but we must check for close
+		if (xcb_connection_has_error(Connection)) {
+		    return;
+		}
+	    }
+	}
+    }
+}
+
+///
+///	Get OSD size.
+///
+///	@param[out] width	OSD width
+///	@param[out] height	OSD height
+///
+void VideoGetOsdSize(int *width, int *height)
+{
+    *width = 1920;
+    *height = 1080;			// unknown default
+    if (VideoWindowWidth && VideoWindowHeight) {
+	*width = VideoWindowWidth;
+	*height = VideoWindowHeight;
+    }
+}
+
+///
+///	Get player video window id.
+///
+int VideoGetPlayWindow(void)
+{
+    return VideoPlayWindow;
+}
+
+///
+///	Set video geometry.
+///
+///	@todo write/search the good version
+///
+void VideoSetGeometry(const char *geometry)
+{
+    sscanf(geometry, "%dx%d%d%d", &VideoWindowWidth, &VideoWindowHeight,
+	&VideoWindowX, &VideoWindowY);
+}
+
+///
+///	Set video color key.
+///
+///	Should be called before VideoInit().
+///
+void VideoSetColorKey(uint32_t color_key)
+{
+    VideoColorKey = color_key;
+}
+
+///
+///	Initialize video.
+///
+int VideoInit(const char *display)
+{
+    const char *display_name;
+    xcb_connection_t *connection;
+    xcb_screen_iterator_t iter;
+    int screen_nr;
+    int i;
+
+    display_name = display ? display : getenv("DISPLAY");
+
+    //	Open the connection to the X server.
+    connection = xcb_connect(display_name, &screen_nr);
+    if (!connection || xcb_connection_has_error(connection)) {
+	fprintf(stderr, "play: can't connect to X11 server on %s\n",
+	    display_name);
+	return -1;
+    }
+    Connection = connection;
+
+    //	Get the requested screen number
+    iter = xcb_setup_roots_iterator(xcb_get_setup(connection));
+    for (i = 0; i < screen_nr; ++i) {
+	xcb_screen_next(&iter);
+    }
+    VideoScreen = iter.data;
+
+    //
+    //	Default window size
+    //
+    if (!VideoWindowHeight) {
+	if (VideoWindowWidth) {
+	    VideoWindowHeight = (VideoWindowWidth * 9) / 16;
+	} else {			// default to fullscreen
+	    VideoWindowHeight = VideoScreen->height_in_pixels;
+	    VideoWindowWidth = VideoScreen->width_in_pixels;
+	}
+    }
+    if (!VideoWindowWidth) {
+	VideoWindowWidth = (VideoWindowHeight * 16) / 9;
+    }
+
+    VideoPlayWindow =
+	VideoCreateWindow(VideoScreen->root, VideoScreen->root_visual,
+	VideoScreen->root_depth);
+    xcb_map_window(Connection, VideoPlayWindow);
+    VideoOsdWindow =
+	VideoCreateWindow(VideoPlayWindow, VideoScreen->root_visual,
+	VideoScreen->root_depth);
+    Debug(3, "play: osd %x, play %x\n", VideoOsdWindow, VideoPlayWindow);
+
+    VideoWindowClear();
+    // done by clear: xcb_flush(Connection);
+
+    return 0;
+}
+
+///
+///	Cleanup video.
+///
+void VideoExit(void)
+{
+    if (VideoOsdWindow != XCB_NONE) {
+	xcb_destroy_window(Connection, VideoOsdWindow);
+	VideoOsdWindow = XCB_NONE;
+    }
+    if (VideoPlayWindow != XCB_NONE) {
+	xcb_destroy_window(Connection, VideoPlayWindow);
+	VideoPlayWindow = XCB_NONE;
+    }
+    if (VideoColormap != XCB_NONE) {
+	xcb_free_colormap(Connection, VideoColormap);
+	VideoColormap = XCB_NONE;
+    }
+    if (VideoBlankCursor != XCB_NONE) {
+	xcb_free_cursor(Connection, VideoBlankCursor);
+	VideoBlankCursor = XCB_NONE;
+    }
+    if (VideoPixmap != XCB_NONE) {
+	xcb_free_pixmap(Connection, VideoPixmap);
+	VideoPixmap = XCB_NONE;
+    }
+    if (XcbKeySymbols != XCB_NONE) {
+	xcb_key_symbols_free(XcbKeySymbols);
+	XcbKeySymbols = XCB_NONE;
+    }
+
+    if (Connection) {
+	xcb_flush(Connection);
+	xcb_disconnect(Connection);
+	Connection = NULL;
+    }
+}
diff --git a/video.h b/video.h
new file mode 100644
index 0000000..bc7bb8f
--- /dev/null
+++ b/video.h
@@ -0,0 +1,62 @@
+///
+///	@file video.h	@brief Video module header file
+///
+///	Copyright (c) 2012 by Johns.  All Rights Reserved.
+///
+///	Contributor(s):
+///
+///	License: AGPLv3
+///
+///	This program is free software: you can redistribute it and/or modify
+///	it under the terms of the GNU Affero General Public License as
+///	published by the Free Software Foundation, either version 3 of the
+///	License.
+///
+///	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 Affero General Public License for more details.
+///
+///	$Id$
+//////////////////////////////////////////////////////////////////////////////
+
+/// @addtogroup Video
+/// @{
+
+    /// C callback feed key press
+extern void FeedKeyPress(const char *, const char *, int, int);
+
+    /// Show window.
+extern void VideoWindowShow(void);
+
+    /// Hide window.
+extern void VideoWindowHide(void);
+
+    /// Clear window.
+extern void VideoWindowClear(void);
+
+    /// Poll video events.
+extern void VideoPollEvents(int);
+
+    /// Get player window id.
+extern int VideoGetPlayWindow(void);
+
+    /// Set Osd 3D Mode
+extern void VideoSetOsd3DMode(int);
+
+    /// Draw an OSD ARGB image.
+extern void VideoDrawARGB(int, int, int, int, const uint8_t *);
+
+    /// Get OSD size.
+extern void VideoGetOsdSize(int *, int *);
+
+    /// Set video geometry.
+extern void VideoSetGeometry(const char *);
+
+    /// Set video color key.
+extern void VideoSetColorKey(uint32_t);
+
+extern int VideoInit(const char *);	///< Setup video module.
+extern void VideoExit(void);		///< Cleanup and exit video module.
+
+/// @}

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



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