[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