diff options
-rw-r--r-- | .gitignore | 10 | ||||
-rw-r--r-- | AGPL-3.0 | 662 | ||||
-rw-r--r-- | HISTORY | 82 | ||||
-rw-r--r-- | Makefile | 142 | ||||
-rw-r--r-- | README | 132 | ||||
-rw-r--r-- | TODO | 11 | ||||
-rw-r--r-- | config.c | 149 | ||||
-rw-r--r-- | config.h | 63 | ||||
-rw-r--r-- | control.c | 377 | ||||
-rw-r--r-- | control.h | 44 | ||||
-rw-r--r-- | filebrowser.c | 161 | ||||
-rw-r--r-- | filebrowser.h | 39 | ||||
-rw-r--r-- | mpv.c | 145 | ||||
-rw-r--r-- | mpv_service.h | 47 | ||||
-rw-r--r-- | osd.c | 182 | ||||
-rw-r--r-- | osd.h | 44 | ||||
-rw-r--r-- | player.c | 570 | ||||
-rw-r--r-- | player.h | 99 | ||||
-rw-r--r-- | playmenu.c | 92 | ||||
-rw-r--r-- | playmenu.h | 25 | ||||
-rw-r--r-- | po/de_DE.po | 63 | ||||
-rw-r--r-- | setup.c | 65 | ||||
-rw-r--r-- | setup.h | 34 | ||||
-rw-r--r-- | status.c | 25 | ||||
-rw-r--r-- | status.h | 29 |
25 files changed, 3292 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..82f4d66 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# gitignore(5) file +*.[oa] +*~ +.*.swp +.gdb_history +# generated files +.dependencies +libvdr-mpv.so* +po/*.pot +po/*.mo diff --git a/AGPL-3.0 b/AGPL-3.0 new file mode 100644 index 0000000..1489b72 --- /dev/null +++ b/AGPL-3.0 @@ -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/>. @@ -0,0 +1,82 @@ +2015-06-29: vdr-mpv-0.0.4 +- start playback even when audio or video fails, this should give a better + user feedback to see what's configured wrong +- use VDR volume control if passthrough is disabled +- add support for playlists, by default the file endings m3u, ini, pls, txt, + playlist are passed as a playlist, this can be adjusted by the new argument x +- stereo downmix support, this can be enabled in the setup menu +- workaround for open osd during shutdown +- navigating through playlist using the remote control keys 7 and 9 + 7 to select the previous playlist item, 9 to select the next +- the osd file in /tmp is removed when osd is closed, this should also fix + unclean osd display while using the audio/subtitle menus +- added a setup option to choose between chapter and playlist navigation on + prev/next keys +- added a setup option to use the next/prev keys for chapter navigation and + if the file has no chapters use them for playlist navigation. this option + is turned on by default +- use pause property of mpv for detecting if player is paused +- use speed property of mpv for detecting current player speed +- if the file contains named chapters they are now displayed on replay info +- added option to display the title from the meta data instead of the filename + you have to enable this in the setup menu +- readd MsgReplaying which was lost somewhere during cleanup +- added shuffle function for playlist, if the playlist ist started with OK + it's played in normal order, if the playback is started with yellow shuffle + is active +- the service function Mpv_PlaylistShuffle can be used by other plugins to start + a playlist in shuffle mode (see filebrowser.c for an example) +- added a first german translation + +2015-06-22: vdr-mpv 0.0.3 +- FastFwd and FastRwd (Rewind only jumps back 10 seconds, since mpv has no rewind) +- Audio passthrough is now configurable in the plugin setup menu +- Primary device isn't switched anymore when playback is started, on playback stop + the device is still switched (to the current one) to recreate the normal osd + provider +- fix osd display when skindesigner is used +- the shared memory is now mapped using mmap +- if mpv isn't able to start the player is closed after a 5 seconds timeout +- vo and hwdec can now be set as an argument, now other vo/hwdec than vdpau are + supported. vdpau is choosen as default +- 3D osd svdrp commands removed, since they didn't worked. If you need this + please give me a feature request and I will try to implement them again +- chapters are now displayed as "recording" marks in the replay info +- the vdr audio menu is now used to select different audio tracks if available +- the vdr subtitle menu is now used to select different subtitles if available +- Back key is now doing the same like stop and blue if disdcnav is not active +- default audio and subtitle language can now be set as an argument (-l) +- the new svdrp command play can be used to start playing a file +- time search is now available using the red remote control key, it works + the same way like the VDR implementation (in fact most of the code is + copied from VDR and adjusted slightly for this function) +- cleaned up service interface and rename it to mpv, this was a left + over from the initial fork +- renamed some of the arguments, the old ones are not longer valid. + Please see the README for the new names + +2015-06-09: vdr-mpv 0.0.2 +- use pkg-config to check if libmpv is available +- set alsa default as audio default value +- use old dts-hd parameter again for backwards compatiblity +- create osd file readable +- generic osd size support + +2015-06-08: vdr-mpv 0.0.1 +- Forked under the new name mpv +- Replaced mplayer by libmpv +- Added OSD support using libmpv API +- Several OSD improvments +- Switch to video framerate (if xrandr extension is available) +- DTS-HD passthrough + +2013-09-05: vdr-play +- Add replay info. + +2013-08-26: vdr-play + +- 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..83c5536 --- /dev/null +++ b/Makefile @@ -0,0 +1,142 @@ +# +# 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 = mpv + +### Configuration (edit this for your needs) +#CONFIG := -DDEBUG # uncomment to build DEBUG + +# support refresh rate switching +XRANDR ?= $(shell pkg-config --exists xrandr && echo 1) +LIBMPV ?= $(shell pkg-config --exists mpv && echo 1) + +ifneq ($(LIBMPV),1) +$(error libmpv missing) +endif + +ifeq ($(XRANDR),1) +CONFIG += -DUSE_XRANDR +LIBS += $(shell pkg-config --libs xrandr) +endif + +LIBS += $(shell pkg-config --libs mpv) + +### The version number of this plugin (taken from the main source file): + +VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g') +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_PATH="$$PKG_CONFIG_PATH:../../.." pkg-config --variable=$(1) vdr)) +LIBDIR = $(call PKGCFG,libdir) +LOCDIR = $(call PKGCFG,locdir) +PLGCFG = $(call PKGCFG,plgcfg) +# +TMPDIR ?= /tmp + +### The compiler options: + +export CFLAGS = $(call PKGCFG,cflags) +export CXXFLAGS = $(call PKGCFG,cxxflags) + +### The version number of VDR's plugin API: + +APIVERSION = $(call PKGCFG,apiversion) + +### Allow user defined options to overwrite defaults: + +-include $(PLGCFG) + +### The name of the distribution archive: + +ARCHIVE = $(PLUGIN)-$(VERSION) +PACKAGE = vdr-$(ARCHIVE) + +### The name of the shared object file: + +SOFILE = libvdr-$(PLUGIN).so + +### Includes and Defines (add further entries here): + +INCLUDES += + +DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -D_GNU_SOURCE $(CONFIG) \ + $(if $(GIT_REV), -DGIT_REV='"$(GIT_REV)"') + +### The object files (add further files here): + +OBJS = $(PLUGIN).o config.o control.o filebrowser.o osd.o player.o playmenu.o setup.o status.o + +### The main target: + +all: $(SOFILE) i18n + +### Implicit rules: + +%.o: %.c + $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) -o $@ $< + +### Dependencies: + +MAKEDEP = $(CXX) -MM -MG +DEPFILE = .dependencies +$(DEPFILE): Makefile + @$(MAKEDEP) $(CXXFLAGS) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@ + +-include $(DEPFILE) + +### Internationalization (I18N): + +PODIR = po +I18Npo = $(wildcard $(PODIR)/*.po) +I18Nmo = $(addsuffix .mo, $(foreach file, $(I18Npo), $(basename $(file)))) +I18Nmsgs = $(addprefix $(DESTDIR)$(LOCDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file)))))) +I18Npot = $(PODIR)/$(PLUGIN).pot + +%.mo: %.po + msgfmt -c -o $@ $< + +$(I18Npot): $(wildcard *.c) + xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --package-name=vdr-$(PLUGIN) --package-version=$(VERSION) --msgid-bugs-address='<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: + +$(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 cjf $(PACKAGE).tar.bz2 -C $(TMPDIR) $(ARCHIVE) + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @echo Distribution package created as $(PACKAGE).tar.bz2 + +clean: + @-rm -f $(PODIR)/*.mo $(PODIR)/*.pot + @-rm -f $(OBJS) $(DEPFILE) *.so *.tar.bz2 core* *~ @@ -0,0 +1,132 @@ +This is a "plugin" for the Video Disk Recorder (VDR). + +Copyright (c) 2012, 2013 by Johns. All Rights Reserved. +Copyright (c) 2013 - 2015 by Maniac. All Rights Reserved. + +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. + +Description +----------- + +vdr-mpv is a fork of the vdr-play plugin from Johns. It uses libmpv for playing +media in VDR and also displays the VDR OSD (On Screen Display) during playback. + +Requirements +------------ + +- VDR version >= 2.2.0 (earlier versions may work, but are untested) +- libmpv >= 0.8.0 (earlier versions may work, but are untested) +- xrandr (only needed for modeline switching) + +Installation +------------ + +- extract the plugin archive and change to the folder +- using "make install" the plugin will be compiled and installed on your system +- adjust the command line arguments to fit your needs + +Command line arguments +---------------------- + +-a audio + mpv --ao (Default: alsa:device=default) + The audio out device which is used by mpv, this is directly passed as ao to mpv. + +-v video + mpv --vo (Default: vdpau) + The video out device which is used by mpv, this is directly passed as vo to mpv. + +-h hwdec + mpv --hwdec-codecs (Default: vdpau) + The hwdec-codecs which are used by mpv, this is directly passed as hwdec-codecs + to mpv. + +-d device + optical disc device (Default: /dev/dvd) + The device which is used for playback of optical media + +-l languages + languages for audio and subtitles (Default: deu,de,ger,eng,en)" + The default languages choosen for audio and subtitles. Please note since different + container formats are using different standards you should pass this at least in + ISO-639-1 (two letters) and 639-2 (three letters). The list is seperated by comma. + This will is passed as alang and slang to mpv. + +-b /dir + file browser root directory + +-r + switch modeline to refresh rate of played file + xrandr is needed for this to work. If no xrandr is detected during compile time + this feature will be deactivated. + +Plugin setup options +-------------------- + +- Hide main menu entry + Don't show the filebrowser in VDR main menu + +- Audio passthrough + Passthrough digital audio (like AC3, DTS) formats without decoding them + If passthrough is disabled the VDR volume control is used + +- DTS-HD passthrough + Passthrough DTS-HD audio, this is only selectable if general passtrough is enabled + +- Enable stereo downmix + Downmix audio to stereo, this is only selectable if passthrough is disabled + +- Use prev/next keys for playlist control + If activated the next and previous keys are used for playlist navigation, + the keys 7 and 9 are then used for chapter navigation + +- Control playlist if no chapters in file + If a file without chapters is played use the prev/next keys for playlist control + +Remote control keys +------------------- + +The following remote keys are supported in normal operation (no disc menu active) + +Play, Up: Continue playback if paused +Down, Pause: Pause playback +Fast rewind: Seek 10 seconds backwards (fast rew is not supported by mpv) +Fast forward: Speed up playback speed up to 32 times +Red: Time search functionality like used in VDR recordings +Green: Seek 60 seconds backwards +Yellow: Seek 60 seconds forward +Blue, Stop, Back: Stop playback +Ok: Display current playback status or close status display + if currently open +5: Open disc nav menu +7: Previous playlist item +9: Next playlist item +Next: Next chapter +Previous: Previous chapter +Audio: Display a menu to switch between the available audio + tracks +Subtitles: Display a menu to switch between the available subtitle + tracks + +If a disc menu is active the following keys loose their default function described +above and gain the following function + +Up, Down, Left, Right: Navigate through the disc menu +Ok: Select the currently highlighted entry in the disc menu +Back: Back in the disc nav menu + +SVDRP commands +-------------- + +PLAY filename play given filename + @@ -0,0 +1,11 @@ +mpv issues: + - subtitle forced flag is ignored with mkv -> https://github.com/mpv-player/mpv/issues/2032 + - ff while passthrough is active does not work because mpv tries to add resample filter which fails + +Known issues: + +Feature requests: + - resume + +Todo: + diff --git a/config.c b/config.c new file mode 100644 index 0000000..9fdc834 --- /dev/null +++ b/config.c @@ -0,0 +1,149 @@ +////////////////////////////////////////////////////////////////////////////// +/// /// +/// This file is part of the VDR mpv plugin and licensed under AGPLv3 /// +/// /// +/// See the README file for copyright information /// +/// /// +////////////////////////////////////////////////////////////////////////////// + +#include <stdlib.h> +#include <unistd.h> +#include <sstream> +#include <vdr/tools.h> + +#include "config.h" + +cMpvPluginConfig::cMpvPluginConfig() +{ + HideMainMenuEntry = 0; + UsePassthrough = 1; + UseDtsHdPassthrough = 1; + StereoDownmix = 0; + PlaylistOnNextKey = 0; + PlaylistIfNoChapters = 1; + ShowMediaTitle = 0; + + BrowserRoot = "/"; + RefreshRate = 0; + VideoOut = "vdpau"; + HwDec = "vdpau"; + AudioOut = "alsa:device=default"; + DiscDevice = "/dev/dvd"; + Languages = "deu,de,ger,eng,en"; + PlayListExtString = "m3u,ini,pls,txt,playlist"; + + X11Display = ":0.0"; +} + +vector<string> cMpvPluginConfig::ExplodeString(string Input) +{ + vector<string> Result; + + std::stringstream Data(Input); + string Item; + while(std::getline(Data, Item, ',')) + { + if (Item != "") + Result.push_back(Item); + } + return Result; +} + +int cMpvPluginConfig::ProcessArgs(int argc, char *const argv[]) +{ + char *s; + if ((s = getenv("DISPLAY"))) + { + X11Display = s; + } + else + setenv("DISPLAY", X11Display.c_str(), 1); + + for (;;) + { + switch (getopt(argc, argv, "a:v:h:d:b:l:x:r")) + { + case 'a': // audio out + AudioOut = optarg; + continue; + case 'v': // video out + VideoOut = optarg; + continue; + case 'h': // hwdec-codecs + HwDec = optarg; + continue; + case 'd': // dvd-device + DiscDevice = optarg; + continue; + case 'b': // browser root + BrowserRoot = optarg; + continue; + case 'l': // languages + Languages = optarg; + continue; + case 'x': // playlist extensions + PlayListExtString = optarg; + continue; + case 'r': // refresh rate + RefreshRate = 1; + continue; + case EOF: + break; + case ':': + esyslog("[mpv]: missing argument for option '%c'\n", optopt); + return 0; + default: + esyslog("[mpv]: unkown option '%c'\n", optopt); + return 0; + } + break; + } + while (optind < argc) + { + esyslog("[mpv]: unhandled argument '%s'\n", argv[optind++]); + } + + //convert the playlist extension string to a vector + PlaylistExtensions = ExplodeString(PlayListExtString); + + return 1; +} + +const char *cMpvPluginConfig::CommandLineHelp(void) +{ + return + " -a audio\tmpv --ao (Default: alsa:device=default)\n" + " -v video\tmpv --vo (Default: vdpau)\n" + " -h hwdec\tmpv --hwdec-codecs (Default: vdpau)\n" + " -d device\tmpv optical disc device (Default: /dev/dvd)\n" + " -l languages\tlanguages for audio and subtitles (Default: deu,de,ger,eng,en)\n" + " -b /dir\tbrowser root directory\n" + " -x extensions\tfiles with this extensions are handled as playlists\n" + "\t\t(Default: m3u,ini,pls,txt,playlist)\n" +#ifdef USE_XRANDR + " -r\t\tswitch modeline to refresh rate of played file\n" +#endif + ; +} + +bool cMpvPluginConfig::SetupParse(const char *name, const char *value) +{ + if (!strcasecmp(name, "HideMainMenuEntry")) + HideMainMenuEntry = atoi(value); + else if (!strcasecmp(name, "UsePassthrough")) + UsePassthrough = atoi(value); + else if (!strcasecmp(name, "StereoDownmix")) + StereoDownmix = atoi(value); + else if (!strcasecmp(name, "UseDtsHdPassthrough")) + UseDtsHdPassthrough = atoi(value); + else if (!strcasecmp(name, "PlaylistOnNextKey")) + PlaylistOnNextKey = atoi(value); + else if (!strcasecmp(name, "PlaylistIfNoChapters")) + PlaylistIfNoChapters = atoi(value); + else if (!strcasecmp(name, "ShowMediaTitle")) + ShowMediaTitle = atoi(value); + else + return false; + return true; +} + diff --git a/config.h b/config.h new file mode 100644 index 0000000..b36f7a3 --- /dev/null +++ b/config.h @@ -0,0 +1,63 @@ +////////////////////////////////////////////////////////////////////////////// +/// /// +/// This file is part of the VDR mpv plugin and licensed under AGPLv3 /// +/// /// +/// See the README file for copyright information /// +/// /// +////////////////////////////////////////////////////////////////////////////// + +#ifndef __MPV_CONFIG_H +#define __MPV_CONFIG_H + +#include <string> +#include <vector> + +#include "player.h" + +using std::string; +using std::vector; + +class cMpvPluginConfig +{ + private: + string PlayListExtString; // list of playlist extensions, we will convert this into a vector + vector<string> ExplodeString(string Input); // creates a vector from the given string + + public: + cMpvPluginConfig(); // define default values + int ProcessArgs(int argc, char *const argv[]); // parse command line arguments + const char *CommandLineHelp(); // return our command line help string + bool SetupParse(const char *name, const char *value); // parse setup.conf values + + // plugin setup variables + int UsePassthrough; // enable passthrough + int UseDtsHdPassthrough; // enable DTS-HD passthrough + int StereoDownmix; // enable stereo downmix + int HideMainMenuEntry; // hide main menu entry + int PlaylistOnNextKey; // skip to next playlist item on next/previous keys + int PlaylistIfNoChapters; // skip to next playlist item if the file has no chapters + int ShowMediaTitle; // show title from media file instead of filename + + // plugin parameter variables + string BrowserRoot; // start dir for filebrowser + int RefreshRate; // enable modeline switching + string VideoOut; // video out device + string HwDec; // hwdec codecs + string AudioOut; // audio out device + string DiscDevice; // optical disc device + string Languages; // language string for audio and subtitle TODO move to Setup menu + vector<string> PlaylistExtensions; // file extensions which are recognized as a playlist + + string X11Display; // X11 display used for mpv + string TitleOverride; // title to display (ovveride used via service interface) +}; + +// only create one instance (done in mpv.c), all other calls will simply get the extern reference +#ifdef CREATE_CONFIG + cMpvPluginConfig *MpvPluginConfig; // create an instance of this class to have the config available if needed +#else + extern cMpvPluginConfig *MpvPluginConfig; +#endif + +#endif + diff --git a/control.c b/control.c new file mode 100644 index 0000000..e6061cb --- /dev/null +++ b/control.c @@ -0,0 +1,377 @@ +////////////////////////////////////////////////////////////////////////////// +/// /// +/// This file is part of the VDR mpv plugin and licensed under AGPLv3 /// +/// /// +/// See the README file for copyright information /// +/// /// +////////////////////////////////////////////////////////////////////////////// + +#include "control.h" +#include "config.h" +#include <vdr/interface.h> + +cMpvControl::cMpvControl(string Filename, bool Shuffle) +:cControl(Player = new cMpvPlayer(Filename.c_str(), Shuffle)) +{ + dsyslog("[mpv] %s\n", __FUNCTION__); + DisplayReplay = NULL; + VolumeStatus = NULL; + if (!MpvPluginConfig->UsePassthrough) + VolumeStatus = new cMpvStatus(Player); + infoVisible = false; + timeSearchActive = false; + cStatus::MsgReplaying(this, Filename.c_str(), Filename.c_str(), true); +} + +cMpvControl::~cMpvControl() +{ + dsyslog("[mpv]%s\n", __FUNCTION__); + if (VolumeStatus) + delete VolumeStatus; + Hide(); + Player->OsdClose(); + // Osd Workaround + // osd is open, first send menu key, this will close the menu, if any other + // osd was open this opens the menu, so send kBack afterwards, to close it again + if (cOsd::IsOpen()) + { + eKeys key = kMenu; + dsyslog ("[mpv] osd is open sending menu key followed by back key\n"); + cRemote::Put(key); + key = kBack; + cRemote::Put(key); +} + Player->Shutdown(); + delete Player; + cDevice::SetPrimaryDevice(cDevice::PrimaryDevice()->DeviceNumber() + 1); + cStatus::MsgReplaying(this, NULL, NULL, false); +} + +void cMpvControl::ShowProgress(void) +{ + if (!Player->IsPaused() && LastPlayerCurrent == Player->CurrentPlayTime()) + return; + LastPlayerCurrent = Player->CurrentPlayTime(); + + if (!DisplayReplay) + DisplayReplay = Skins.Current()->DisplayReplay(false); + + if (!infoVisible) + { + UpdateMarks(); + infoVisible = true; + timeoutShow = Setup.ProgressDisplayTime ? (time(0) + Setup.ProgressDisplayTime) : 0; + } + + string TitleDisplay = Player->CurrentFile(); + if (MpvPluginConfig->TitleOverride != "") + TitleDisplay = MpvPluginConfig->TitleOverride; + else if (MpvPluginConfig->ShowMediaTitle && Player->MediaTitle() != "") + TitleDisplay = Player->MediaTitle(); + + if (Player->NumChapters() > 0) + { + if (Player->ChapterTitle(Player->CurrentChapter()) != "") + TitleDisplay += " - " + Player->ChapterTitle(Player->CurrentChapter()); + + char buffer[10]; + snprintf (buffer, sizeof(buffer), " (%d/%d)", Player->CurrentChapter(), Player->NumChapters()); + TitleDisplay += buffer; + DisplayReplay->SetMarks(Marks()); + } + + DisplayReplay->SetTitle(TitleDisplay.c_str()); + DisplayReplay->SetProgress(Player->CurrentPlayTime(), Player->TotalPlayTime()); + int Speed = Player->CurrentPlaybackSpeed(); + if (Speed == 1) + Speed = -1; + DisplayReplay->SetMode(!Player->IsPaused(), true, Speed); + DisplayReplay->SetCurrent(IndexToHMSF(Player->CurrentPlayTime(), false, 1)); + DisplayReplay->SetTotal(IndexToHMSF(Player->TotalPlayTime(), false, 1)); + + SetNeedsFastResponse(true); + Skins.Flush(); +} + +void cMpvControl::Hide() +{ + if (DisplayReplay) + { + delete DisplayReplay; + DisplayReplay = NULL; + SetNeedsFastResponse(false); + } +} + +eOSState cMpvControl::ProcessKey(eKeys key) +{ + eOSState state; + + if (key != kNone) + { + dsyslog("[mpv]%s: key=%d\n", __FUNCTION__, key); + } + + if (!Player->PlayerIsRunning()) // check if player is still alive + { + cControl::Shutdown(); + return osEnd; + } + + if (timeSearchActive && key != kNone) + { + TimeSearchProcess(key); + return osContinue; + } + + if (timeoutShow && time(0) > timeoutShow) + { + timeoutShow = 0; + infoVisible = false; + Hide(); + } + else if (infoVisible) // if RecordingInfo visible then update + ShowProgress(); + + state = osContinue; + switch ((int)key) // cast to shutup g++ warnings + { + case kUp: + if (Player->DiscNavActive()) + { + Player->DiscNavUp(); + break; + } + case kPlay: + Hide(); + if (Player->CurrentPlaybackSpeed() != 1) + { + Player->SetSpeed(1); + } + if (Player->IsPaused()) + { + Player->TogglePause(); + } + ShowProgress(); + break; + + case kDown: + if (Player->DiscNavActive()) + { + Player->DiscNavDown(); + break; + } + case kPause: + Player->TogglePause(); + ShowProgress(); + break; + + case kLeft: + if (Player->DiscNavActive()) + { + Player->DiscNavLeft(); + break; + } + case kFastRew: + // reset to normal playback speed (if fastfwd is active currently) and then just go back 10 seconds since there is no fastrew in mpv + Player->SetSpeed(1); + Player->Seek(-10); + ShowProgress(); + break; + case kRight: + if (Player->DiscNavActive()) + { + Player->DiscNavRight(); + break; + } + case kFastFwd: + if (Player->CurrentPlaybackSpeed() < 32) + { + Player->SetSpeed(Player->CurrentPlaybackSpeed() * 2); + } + ShowProgress(); + break; + + case kRed: + TimeSearch(); + break; + case kGreen | k_Repeat: + case kGreen: + Player->Seek(-60); + break; + case kYellow | k_Repeat: + case kYellow: + Player->Seek(+60); + break; + + case kBack: + if (Player->DiscNavActive()) + { + Player->DiscNavPrev(); + break; + } + case kStop: + case kBlue: + Hide(); + Player->QuitPlayer(); + return osEnd; + break; + + case kOk: + if (Player->DiscNavActive()) + { + Player->DiscNavSelect(); + break; + } + if (infoVisible) + { + Hide(); + infoVisible = false; + } + else + ShowProgress(); + break; + + case k5: + Player->DiscNavMenu(); + break; + + case kNext: + if (MpvPluginConfig->PlaylistOnNextKey || (MpvPluginConfig->PlaylistIfNoChapters && !Player->NumChapters())) + Player->NextPlaylistItem(); + else + Player->NextChapter(); + break; + + case kPrev: + if (MpvPluginConfig->PlaylistOnNextKey || (MpvPluginConfig->PlaylistIfNoChapters && !Player->NumChapters())) + Player->PreviousPlaylistItem(); + else + Player->PreviousChapter(); + break; + + case k7: + if (MpvPluginConfig->PlaylistOnNextKey) + Player->PreviousChapter(); + else + Player->PreviousPlaylistItem(); + break; + + case k9: + if (MpvPluginConfig->PlaylistOnNextKey) + Player->NextChapter(); + else + Player->NextPlaylistItem(); + break; + + default: + break; + } + return state; +} + +void cMpvControl::UpdateMarks() +{ + ((cList<cMark> *)&ChapterMarks)->Clear(); + + if (Player->NumChapters() > 1) + { + ChapterMarks.Add(0); + for (int i=1; i <= Player->NumChapters(); i++) + { + ChapterMarks.Add(Player->ChapterStartTime(i) - 1); + ChapterMarks.Add(Player->ChapterStartTime(i)); + } + } +} + +// TimeSearch code is copied from vdr menu.c and slightly adjusted to fit with our class +void cMpvControl::TimeSearch() +{ + timeSearchTime = timeSearchPos = 0; + timeSearchHide = false; + if (!infoVisible) { + ShowProgress(); + if (infoVisible) + timeSearchHide = true; + else + return; + } + timeoutShow = 0; + TimeSearchDisplay(); + timeSearchActive = true; +} + +void cMpvControl::TimeSearchDisplay() +{ + char buf[64]; + // TRANSLATORS: note the trailing blank! + strcpy(buf, tr("Jump: ")); + int len = strlen(buf); + char h10 = '0' + (timeSearchTime >> 24); + char h1 = '0' + ((timeSearchTime & 0x00FF0000) >> 16); + char m10 = '0' + ((timeSearchTime & 0x0000FF00) >> 8); + char m1 = '0' + (timeSearchTime & 0x000000FF); + char ch10 = timeSearchPos > 3 ? h10 : '-'; + char ch1 = timeSearchPos > 2 ? h1 : '-'; + char cm10 = timeSearchPos > 1 ? m10 : '-'; + char cm1 = timeSearchPos > 0 ? m1 : '-'; + sprintf(buf + len, "%c%c:%c%c", ch10, ch1, cm10, cm1); + DisplayReplay->SetJump(buf); +} + +void cMpvControl::TimeSearchProcess(eKeys Key) +{ +#define STAY_SECONDS_OFF_END 10 + int Seconds = (timeSearchTime >> 24) * 36000 + ((timeSearchTime & 0x00FF0000) >> 16) * 3600 + ((timeSearchTime & 0x0000FF00) >> 8) * 600 + (timeSearchTime & 0x000000FF) * 60; + int Current = Player->CurrentPlayTime(); + int Total = Player->TotalPlayTime(); + switch (Key) { + case k0 ... k9: + if (timeSearchPos < 4) { + timeSearchTime <<= 8; + timeSearchTime |= Key - k0; + timeSearchPos++; + TimeSearchDisplay(); + } + break; + case kFastRew: + case kLeft: + case kFastFwd: + case kRight: { + int dir = ((Key == kRight || Key == kFastFwd) ? 1 : -1); + if (dir > 0) + Seconds = min(Total - Current - STAY_SECONDS_OFF_END, Seconds); + else + Seconds = 0-Seconds; + Player->Seek(Seconds); + timeSearchActive = false; + } + break; + case kPlayPause: + case kPlay: + case kUp: + case kPause: + case kDown: + case kOk: + if (timeSearchPos > 0) { + Seconds = min(Total - STAY_SECONDS_OFF_END, Seconds); + Player->SetTimePos(Seconds); + } + timeSearchActive = false; + break; + default: + if (!(Key & k_Flags)) // ignore repeat/release keys + timeSearchActive = false; + break; + } + + if (!timeSearchActive) { + if (timeSearchHide) + Hide(); + else + DisplayReplay->SetJump(NULL); +// ShowProgress(); + } +} + diff --git a/control.h b/control.h new file mode 100644 index 0000000..cea4639 --- /dev/null +++ b/control.h @@ -0,0 +1,44 @@ +////////////////////////////////////////////////////////////////////////////// +/// /// +/// This file is part of the VDR mpv plugin and licensed under AGPLv3 /// +/// /// +/// See the README file for copyright information /// +/// /// +////////////////////////////////////////////////////////////////////////////// + +#ifndef __MPV_CONTROL_H +#define __MPV_CONTROL_H + +#include <vdr/player.h> +#include "player.h" +#include "status.h" + +class cMpvControl:public cControl +{ + private: + cMpvPlayer *Player; // our player + cSkinDisplayReplay *DisplayReplay; // our osd display + void ShowProgress(); // display progress bar + void Hide(); // hide replay control + bool infoVisible; // RecordingInfo visible + int LastPlayerCurrent; // the lasz cuurent time the osd was rendered with + cMarks ChapterMarks; // chapter marks + time_t timeoutShow; // timeout shown control + cMpvStatus *VolumeStatus; // observe hte VDR volume and adjust mpv volume + + void TimeSearch(); + void TimeSearchDisplay(); + void TimeSearchProcess(eKeys Key); + bool timeSearchActive, timeSearchHide; + int timeSearchTime, timeSearchPos; + + public: + cMpvControl(string filename, bool Shuffle=false); + virtual ~cMpvControl(); + virtual eOSState ProcessKey(eKeys); // handle keyboard input + cMarks *Marks() { return &ChapterMarks; } + void UpdateMarks(); +}; + +#endif + diff --git a/filebrowser.c b/filebrowser.c new file mode 100644 index 0000000..c187b40 --- /dev/null +++ b/filebrowser.c @@ -0,0 +1,161 @@ +#include <vector> +#include <string> +#include <algorithm> + +#include "filebrowser.h" +#include "mpv_service.h" + +using std::string; +using std::vector; +using std::sort; + +string cMpvFilebrowser::currentDir = ""; +string cMpvFilebrowser::currentItem = ""; + +cMpvFilebrowser::cMpvFilebrowser(string RootDir) +:cOsdMenu("Filebrowser") +{ + rootDir = RootDir; + if (currentDir == "") + currentDir = rootDir; + ShowDirectory(currentDir); +} + +void cMpvFilebrowser::ShowDirectory(string Path) +{ + Clear(); + vector<string> Directories; + vector<string> Files; + + DIR *hDir; + struct dirent *Entry; + + hDir = opendir(Path.c_str()); + while ((Entry = readdir(hDir)) != NULL) + { + if (!Entry || Entry->d_name[0] == '.') + continue; + + struct stat Stat; + string Filename = Path + "/" + Entry->d_name; + stat(Filename.c_str(), &Stat); + + if (S_ISDIR(Stat.st_mode)) + Directories.push_back(Entry->d_name); + + else if (S_ISREG(Stat.st_mode)) + Files.push_back(Entry->d_name); + } + closedir(hDir); + + sort(Directories.begin(), Directories.end()); + sort(Files.begin(), Files.end()); + + for (unsigned int i=0; i<Directories.size(); i++) + AddItem(Path, Directories[i], true); + + for (unsigned int i=0; i<Files.size(); i++) + AddItem(Path, Files[i], false); + + string MenuTitle = "Filebrowser"; + if (rootDir != Path) + MenuTitle += " (" + Path.substr(rootDir.size() + 1, string::npos) + ")"; + SetTitle(MenuTitle.c_str()); + SetHelp(NULL, NULL, "Shuffle", NULL); + Display(); +} + +void cMpvFilebrowser::AddItem(string Path, string Text, bool IsDir) +{ + bool Current = false; + if (currentItem == Text) + Current = true; + Add(new cMpvFilebrowserMenuItem(Path, Text, IsDir), Current); +} + +eOSState cMpvFilebrowser::ProcessKey(eKeys Key) +{ + //TODO since we get more and more Keys we should use switch again + if (Key == kOk) + { + cMpvFilebrowserMenuItem *item = (cMpvFilebrowserMenuItem *) Get(Current()); + string newPath = item->Path() + "/" + item->Text(); + if (item->IsDirectory()) + { + currentDir = newPath; + currentItem = ""; + ShowDirectory(newPath); + return osContinue; + } + else + { + currentItem = item->Text(); + PlayFile(newPath); + return osEnd; + } + } + else if (Key == kBack) + { + // we reached our root, so don't go back further and let VDR handle this (close submenu) + if (currentDir == rootDir) + return cOsdMenu::ProcessKey(Key); + currentItem = currentDir.substr(currentDir.find_last_of("/")+1, string::npos); + currentDir = currentDir.substr(0,currentDir.find_last_of("/")); + ShowDirectory(currentDir); + return osContinue; + } + else if (Key == kLeft || Key == kRight || Key == kUp || Key == kDown) + { + // first let VDR handle those keys or we get the previous item + eOSState State = cOsdMenu::ProcessKey(Key); + cMpvFilebrowserMenuItem *item = (cMpvFilebrowserMenuItem *) Get(Current()); + currentItem = item->Text(); + return State; + } + else if (Key == kYellow) + { + cMpvFilebrowserMenuItem *item = (cMpvFilebrowserMenuItem *) Get(Current()); + string newPath = item->Path() + "/" + item->Text(); + if (!item->IsDirectory()) + { + currentItem = item->Text(); + PlayFile(newPath, true); + return osEnd; + } + return osContinue; + } + + return cOsdMenu::ProcessKey(Key); +} + +bool cMpvFilebrowser::PlayFile(string Filename, bool Shuffle) +{ + cPlugin *p; + p = cPluginManager::GetPlugin("mpv"); + if (!p) + return false; + + if (!Shuffle) + { + Mpv_PlayFile playFile; + playFile.Filename = (char *)Filename.c_str(); + return p->Service("Mpv_PlayFile", &playFile); + } + else + { + Mpv_PlaylistShuffle shuffleFile; + shuffleFile.Filename = (char *)Filename.c_str(); + return p->Service("Mpv_PlaylistShuffle", &shuffleFile); + } + + // should never be reached, but silence a compiler warning + return false; +} + +cMpvFilebrowserMenuItem::cMpvFilebrowserMenuItem(string Path, string Item, bool IsDir) +{ + isDir = IsDir; + path = Path; + SetText(Item.c_str()); +} + diff --git a/filebrowser.h b/filebrowser.h new file mode 100644 index 0000000..4eb3555 --- /dev/null +++ b/filebrowser.h @@ -0,0 +1,39 @@ +#ifndef __MPV_FILEBROWSER +#define __MPV_FILEBROWSER + +#include <string> +#include <vdr/plugin.h> + +using std::string; + +class cMpvFilebrowser:public cOsdMenu +{ + private: + string rootDir; + static string currentDir; + static string currentItem; + + void ShowDirectory(string Path); + void AddItem(string Path, string Text, bool IsDir); + bool PlayFile(string Filename, bool Shuffle=false); + + public: + cMpvFilebrowser(string RootDir); + virtual eOSState ProcessKey(eKeys Key); + static string CurrentDir() { return currentDir; } +}; + +class cMpvFilebrowserMenuItem:public cOsdItem +{ + private: + string path; + bool isDir; + + public: + cMpvFilebrowserMenuItem(string Path, string Item, bool IsDir); + string Path() { return path; } + bool IsDirectory() { return isDir; } +}; + +#endif + @@ -0,0 +1,145 @@ +////////////////////////////////////////////////////////////////////////////// +/// /// +/// This file is part of the VDR mpv plugin and licensed under AGPLv3 /// +/// /// +/// See the README file for copyright information /// +/// /// +////////////////////////////////////////////////////////////////////////////// + +#include <string> +#include <vdr/plugin.h> + +// set this define to create the instance of the config class +#define CREATE_CONFIG 1 +#include "config.h" +#include "control.h" +#include "setup.h" +#include "playmenu.h" +#include "mpv_service.h" + +static const char *VERSION = "0.0.4" + #ifdef GIT_REV + "-GIT" GIT_REV + #endif + ; + +using std::string; + +static const char *DESCRIPTION = trNOOP("mpv player plugin"); +static const char *MAINMENUENTRY = trNOOP("MPV"); + +class cMpvPlugin:public cPlugin +{ + private: + void PlayFile(string Filename, bool Shuffle=false) { cControl::Launch(new cMpvControl(Filename.c_str(), Shuffle)); } + bool IsIsoImage(string Filename); + void PlayFileHandleType(string Filename, bool Shuffle=false); + + public: + cMpvPlugin(void); + virtual ~cMpvPlugin(void); + virtual const char *Version(void) { return VERSION; } + virtual const char *Description(void) { return tr(DESCRIPTION); } + virtual const char *CommandLineHelp(void) { return MpvPluginConfig->CommandLineHelp(); } + virtual bool ProcessArgs(int argc, char *argv[]) { return MpvPluginConfig->ProcessArgs(argc, argv); } + virtual bool Initialize(void) { return true; } + virtual void MainThreadHook(void) {} + virtual const char *MainMenuEntry(void) { return MpvPluginConfig->HideMainMenuEntry ? NULL : tr(MAINMENUENTRY); } + virtual cOsdObject *MainMenuAction(void); + virtual cMenuSetupPage *SetupMenu(void) { return new cMpvPluginSetup; } + virtual bool SetupParse(const char *Name, const char *Value) { return MpvPluginConfig->SetupParse(Name, Value); } + virtual bool Service(const char *Id, void *Data = NULL); + virtual const char **SVDRPHelpPages(void) { return NULL; } + virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplayCode); +}; + +cMpvPlugin::cMpvPlugin(void) +{ + MpvPluginConfig = new cMpvPluginConfig(); +} + +cMpvPlugin::~cMpvPlugin(void) +{ + delete MpvPluginConfig; +} + +cOsdObject *cMpvPlugin::MainMenuAction(void) +{ + return new cPlayMenu("MPV"); +} + +bool cMpvPlugin::Service(const char *id, void *data) +{ + if (strcmp(id, "Mpv_PlayFile") == 0) + { + Mpv_PlayFile *playFile = (Mpv_PlayFile *)data; + PlayFileHandleType(playFile->Filename); + return true; + } + if (strcmp(id, "Mpv_PlaylistShuffle") == 0) + { + Mpv_PlaylistShuffle *shuffleFile = (Mpv_PlaylistShuffle *)data; + PlayFileHandleType(shuffleFile->Filename, true); + return true; + } + if (strcmp(id, "Mpv_SetTitle") == 0) + { + Mpv_SetTitle *setTitle = (Mpv_SetTitle *) data; + MpvPluginConfig->TitleOverride = setTitle->Title; + return true; + } + if (strcmp(id, MPV_START_PLAY_SERVICE) == 0) + { + Mpv_StartPlayService_v1_0_t *r = (Mpv_StartPlayService_v1_0_t *) data; + PlayFileHandleType(r->Filename); + return true; + } + if (strcmp(id, MPV_SET_TITLE_SERVICE) == 0) + { + Mpv_SetTitleService_v1_0_t *r = (Mpv_SetTitleService_v1_0_t *) data; + MpvPluginConfig->TitleOverride = r->Title; + return true; + } + return false; +} + +cString cMpvPlugin::SVDRPCommand(const char *Command, const char *Option, int &ReplayCode) +{ + dsyslog ("Command %s Option %s\n", Command, Option); + if (strcasecmp(Command, "PLAY") == 0) + { + PlayFileHandleType(Option); + cControl::Attach(); + return "starting player"; + } + return NULL; +} + +bool cMpvPlugin::IsIsoImage(string Filename) +{ + vector<string> IsoExtensions; + IsoExtensions.push_back("bin"); + IsoExtensions.push_back("dvd"); + IsoExtensions.push_back("img"); + IsoExtensions.push_back("iso"); + IsoExtensions.push_back("mdf"); + IsoExtensions.push_back("nrg"); + for (unsigned int i=0;i<IsoExtensions.size();i++) + { + if (Filename.substr(Filename.find_last_of(".") + 1) == IsoExtensions[i]) + return true; + } + + return false; +} + +void cMpvPlugin::PlayFileHandleType(string Filename, bool Shuffle) +{ + if (IsIsoImage(Filename.c_str())) // handle dvd iso images + { + Filename = "dvd://menu/" + Filename; + } + PlayFile(Filename, Shuffle); +} + +VDRPLUGINCREATOR(cMpvPlugin); // Don't touch this! diff --git a/mpv_service.h b/mpv_service.h new file mode 100644 index 0000000..f4e5632 --- /dev/null +++ b/mpv_service.h @@ -0,0 +1,47 @@ +////////////////////////////////////////////////////////////////////////////// +/// /// +/// This file is part of the VDR mpv plugin and licenced under AGPLv3 /// +/// /// +/// See the README file for copyright information /// +/// /// +////////////////////////////////////////////////////////////////////////////// + +#ifndef __MPV_SERVICE_H +#define __MPV_SERVICE_H + +#define MPV_START_PLAY_SERVICE "Mpv-StartPlayService_v1_0" +#define MPV_SET_TITLE_SERVICE "Mpv-SetTitleService_v1_0" + +// Deprecated will be removed in a future release, please use Mpv_Playfile instead +typedef struct +{ + char* Filename; + char* Title; +} Mpv_StartPlayService_v1_0_t; + +// Deprecated will be removed in a future release, please use Mpv_SetTitle instead +typedef struct +{ + char* Title; +} Mpv_SetTitleService_v1_0_t; + +// play the given Filename, this can be a media file or a playlist +typedef struct +{ + char *Filename; +} Mpv_PlayFile; + +// start the given playlist in shuffle mode +typedef struct +{ + char *Filename; +} Mpv_PlaylistShuffle; + +// Overrides the displayed title in replay info +typedef struct +{ + char *Title; +} Mpv_SetTitle; + +#endif + @@ -0,0 +1,182 @@ +////////////////////////////////////////////////////////////////////////////// +/// /// +/// This file is part of the VDR mpv plugin and licensed under AGPLv3 /// +/// /// +/// See the README file for copyright information /// +/// /// +////////////////////////////////////////////////////////////////////////////// + +#include <sys/mman.h> + +#include "osd.h" + +cMpvOsdProvider::cMpvOsdProvider(cMpvPlayer *player) +{ + Player = player; +} + +cOsd *cMpvOsdProvider::CreateOsd(int Left, int Top, uint Level) +{ + return new cMpvOsd(Left, Top, Level, Player); +} + +bool cMpvOsdProvider::ProvidesTrueColor() +{ + return true; +} + +cMpvOsd::cMpvOsd(int Left, int Top, uint Level, cMpvPlayer *player) +:cOsd(Left, Top, Level) +{ + Player = player; + + int OsdAreaWidth = OsdWidth() + cOsd::Left(); + int OsdAreaHeight = OsdHeight() + cOsd::Top(); + fdOsd = open ("/tmp/vdr_mpv_osd", O_CREAT | O_RDWR, S_IWUSR | S_IRUSR); + lseek(fdOsd, OsdAreaWidth*OsdAreaHeight*4, SEEK_SET); + write(fdOsd, "", 1); + pOsd = (char*) mmap (NULL, OsdAreaWidth*OsdAreaHeight*4, PROT_WRITE, MAP_SHARED, fdOsd, 0); + + SetActive(true); +} + +cMpvOsd::~cMpvOsd() +{ + dsyslog("[mpv] %s\n", __FUNCTION__); + SetActive(false); + + if (cMpvPlayer::PlayerIsRunning()) + Player->OsdClose(); + + munmap(pOsd, sizeof(pOsd)); + close(fdOsd); + remove("/tmp/vdr_mpv_osd"); +} + +void cMpvOsd::SetActive(bool On) +{ + if (On == Active()) + { + return; + } + cOsd::SetActive(On); + + if (!On) + { + if (cMpvPlayer::PlayerIsRunning()) + Player->OsdClose(); + cOsd::SetActive(false); + } +} + +void cMpvOsd::WriteToMpv(int sw, int sh, int x, int y, int w, int h, const uint8_t * argb) +{ + if (!cMpvPlayer::PlayerIsRunning()) + return; + int sx; + int sy; + int pos; + char cmd[64]; + + for (sy = 0; sy < h; ++sy) { + for (sx = 0; sx < w; ++sx) { + pos=0; + pos = pos + ((sy+y)*sw*4); + pos = pos + ((sx+x)*4); + pOsd[pos + 0] = argb[(w * sy + sx) * 4 + 0]; + pOsd[pos + 1] = argb[(w * sy + sx) * 4 + 1]; + pOsd[pos + 2] = argb[(w * sy + sx) * 4 + 2]; + pOsd[pos + 3] = argb[(w * sy + sx) * 4 + 3]; + } + } + snprintf (cmd, sizeof(cmd), "overlay_add 1 0 0 @%d 0 \"bgra\" %d %d %d\n", fdOsd, sw, sh, sw*4); + Player->SendCommand (cmd); +} + +void cMpvOsd::Flush() +{ + cPixmapMemory *pm; + if (!cMpvPlayer::PlayerIsRunning()) + cOsd::SetActive(false); + if (!Active()) + return; + + int OsdAreaWidth = Width() + Left(); + int OsdAreaHeight = Height()+ Top(); + + if (IsTrueColor()) + { + LOCK_PIXMAPS; + while ((pm = dynamic_cast<cPixmapMemory *>(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(); + + WriteToMpv(OsdAreaWidth, OsdAreaHeight, x, y, w, h, pm->Data()); + + DestroyPixmap(pm); + } + } + else + { + 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; + + 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 + { + if (w > OsdAreaWidth) + { + w = OsdAreaWidth; + x2 = x1 + OsdAreaWidth - 1; + } + if (h > OsdAreaHeight) + { + h = OsdAreaHeight; + y2 = y1 + OsdAreaHeight - 1; + } + } + 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); + } + } + WriteToMpv(OsdAreaWidth, OsdAreaHeight, Left() + bitmap->X0() + x1, Top() + bitmap->Y0() + y1, w, h, argb); + + bitmap->Clean(); + // FIXME: reuse argb + free(argb); + } + } +} + @@ -0,0 +1,44 @@ +////////////////////////////////////////////////////////////////////////////// +/// /// +/// This file is part of the VDR mpv plugin and licensed under AGPLv3 /// +/// /// +/// See the README file for copyright information /// +/// /// +////////////////////////////////////////////////////////////////////////////// + +#ifndef __MPV_OSD_H +#define __MPV_OSD_H + +#include <vdr/osd.h> +#include "player.h" + +class cMpvOsdProvider:public cOsdProvider +{ + private: + cMpvPlayer *Player; + + public: + cMpvOsdProvider(cMpvPlayer *player); + virtual cOsd *CreateOsd(int Left, int Top, uint Level); + virtual bool ProvidesTrueColor(); +}; + +class cMpvOsd:public cOsd +{ + private: + void WriteToMpv(int sw, int sh, int x, int y, int w, int h, const uint8_t * argb); + + cMpvPlayer *Player; + int fdOsd; + char *pOsd; + + protected: + virtual void SetActive(bool On); + + public: + cMpvOsd(int Left, int Top, uint Level, cMpvPlayer *player); + virtual ~cMpvOsd(); + virtual void Flush(void); +}; + +#endif diff --git a/player.c b/player.c new file mode 100644 index 0000000..166cfcc --- /dev/null +++ b/player.c @@ -0,0 +1,570 @@ +////////////////////////////////////////////////////////////////////////////// +/// /// +/// This file is part of the VDR mpv plugin and licensed under AGPLv3 /// +/// /// +/// See the README file for copyright information /// +/// /// +////////////////////////////////////////////////////////////////////////////// + +#include <locale.h> +#include <string> +#include <vector> + +#include "player.h" +#include "config.h" +#include "osd.h" + +#ifdef USE_XRANDR +#include <X11/extensions/Xrandr.h> +#endif + +using std::vector; + +#define MPV_OBSERVE_TIME_POS 1 +#define MPV_OBSERVE_DISC_MENU 2 +#define MPV_OBSERVE_FPS 3 +#define MPV_OBSERVE_FILENAME 4 +#define MPV_OBSERVE_LENGTH 5 +#define MPV_OBSERVE_CHAPTERS 6 +#define MPV_OBSERVE_CHAPTER 7 +#define MPV_OBSERVE_PAUSE 8 +#define MPV_OBSERVE_SPEED 9 +#define MPV_OBSERVE_MEDIA_TITLE 10 + +volatile int cMpvPlayer::running = 0; + +// check mpv errors and send them to log +static inline void check_error(int status) +{ + if (status < 0) + { + esyslog("[mpv] API error: %s\n", mpv_error_string(status)); + } +} + +void *cMpvPlayer::ObserverThread(void *handle) +{ + cMpvPlayer *Player = (cMpvPlayer*) handle; + struct mpv_event_log_message *msg; + + // set properties which should be observed + mpv_observe_property(Player->hMpv, MPV_OBSERVE_TIME_POS, "time-pos", MPV_FORMAT_DOUBLE); + mpv_observe_property(Player->hMpv, MPV_OBSERVE_DISC_MENU, "disc-menu-active", MPV_FORMAT_FLAG); + mpv_observe_property(Player->hMpv, MPV_OBSERVE_FPS, "fps", MPV_FORMAT_DOUBLE); + mpv_observe_property(Player->hMpv, MPV_OBSERVE_FILENAME, "filename", MPV_FORMAT_STRING); + mpv_observe_property(Player->hMpv, MPV_OBSERVE_LENGTH, "length", MPV_FORMAT_DOUBLE); + mpv_observe_property(Player->hMpv, MPV_OBSERVE_CHAPTERS, "chapters", MPV_FORMAT_INT64); + mpv_observe_property(Player->hMpv, MPV_OBSERVE_CHAPTER, "chapter", MPV_FORMAT_INT64); + mpv_observe_property(Player->hMpv, MPV_OBSERVE_PAUSE, "pause", MPV_FORMAT_FLAG); + mpv_observe_property(Player->hMpv, MPV_OBSERVE_SPEED, "speed", MPV_FORMAT_DOUBLE); + mpv_observe_property(Player->hMpv, MPV_OBSERVE_MEDIA_TITLE, "media-title", MPV_FORMAT_STRING); + + while (Player->PlayerIsRunning()) + { + mpv_event *event = mpv_wait_event(Player->hMpv, 5); + switch (event->event_id) + { + case MPV_EVENT_SHUTDOWN : + Player->running = 0; + break; + + case MPV_EVENT_PROPERTY_CHANGE : + Player->HandlePropertyChange(event); + break; + + case MPV_EVENT_PLAYBACK_RESTART : + Player->ChangeFrameRate(Player->CurrentFps()); // switching directly after the fps event causes black screen + break; + + case MPV_EVENT_LOG_MESSAGE : + msg = (struct mpv_event_log_message *)event->data; + // without DEBUG log to error since we only request error messages from mpv in this case + #ifdef DEBUG + dsyslog("[mpv]: %s\n", msg->text); + #else + esyslog("[mpv]: %s\n", msg->text); + #endif + break; + + case MPV_EVENT_NONE : + if (!Player->IsPaused()) + { + // no event since 5 secons and not paused -> player died + Player->running = 0; + } + break; + + case MPV_EVENT_TRACKS_CHANGED : + Player->HandleTracksChange(); + break; + + case MPV_EVENT_END_FILE : + case MPV_EVENT_PAUSE : + case MPV_EVENT_UNPAUSE : + case MPV_EVENT_FILE_LOADED : + case MPV_EVENT_VIDEO_RECONFIG : + case MPV_EVENT_GET_PROPERTY_REPLY : + case MPV_EVENT_SET_PROPERTY_REPLY : + case MPV_EVENT_COMMAND_REPLY : + case MPV_EVENT_START_FILE : + case MPV_EVENT_TRACK_SWITCHED : + case MPV_EVENT_IDLE : + case MPV_EVENT_TICK : + case MPV_EVENT_SCRIPT_INPUT_DISPATCH : + case MPV_EVENT_CLIENT_MESSAGE : + case MPV_EVENT_AUDIO_RECONFIG : + case MPV_EVENT_METADATA_UPDATE : + case MPV_EVENT_SEEK : + case MPV_EVENT_CHAPTER_CHANGE : + default : + dsyslog("[mpv]: event: %d %s\n", event->event_id, mpv_event_name(event->event_id)); + break; + } + } + dsyslog("[mpv] Observer thread ended\n"); + return handle; +} + +cMpvPlayer::cMpvPlayer(string Filename, bool Shuffle) +:cPlayer(pmExtern_THIS_SHOULD_BE_AVOIDED) +{ + PlayFilename = Filename; + PlayShuffle = Shuffle; + running = 0; + OriginalFps = -1; +} + +cMpvPlayer::~cMpvPlayer() +{ + dsyslog("[mpv]%s: end\n", __FUNCTION__); + Detach(); +} + +void cMpvPlayer::Activate(bool on) +{ + if (on) + PlayerStart(); +} + +void cMpvPlayer::SetAudioTrack(eTrackType Type, const tTrackId *TrackId) +{ + SetAudio(TrackId->id); +} + +void cMpvPlayer::SetSubtitleTrack(eTrackType Type, const tTrackId *TrackId) +{ + if (Type == ttNone) + { + check_error(mpv_set_option_string(hMpv, "sub-forced-only", "yes")); + return; + } + check_error(mpv_set_option_string(hMpv, "sub-forced-only", "no")); + SetSubtitle(TrackId->id); +} + +void cMpvPlayer::PlayerStart() +{ + PlayerPaused = 0; + PlayerSpeed = 1; + PlayerDiscNav = 0; + + SwitchOsdToMpv(); + + //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! WARNING !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // we are cheating here with mpv since it checks for LC_NUMERIC=C at startup + // this can cause unforseen issues with mpv + //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! WARNING !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + std::string LocaleSave = setlocale(LC_NUMERIC, NULL); + dsyslog ("get locale %s\n", LocaleSave.c_str()); + setlocale(LC_NUMERIC, "C"); + hMpv = mpv_create(); + if (!hMpv) + { + esyslog("[mpv] failed to create context\n"); + cControl::Shutdown(); + } + + int64_t osdlevel = 0; + + check_error(mpv_set_option_string(hMpv, "vo", MpvPluginConfig->VideoOut.c_str())); + check_error(mpv_set_option_string(hMpv, "hwdec", MpvPluginConfig->HwDec.c_str())); + check_error(mpv_set_option_string(hMpv, "ao", MpvPluginConfig->AudioOut.c_str())); + check_error(mpv_set_option_string(hMpv, "hwdec-codecs", MpvPluginConfig->HwDec.c_str())); + check_error(mpv_set_option_string(hMpv, "slang", MpvPluginConfig->Languages.c_str())); // this can break br menu display + check_error(mpv_set_option_string(hMpv, "alang", MpvPluginConfig->Languages.c_str())); + check_error(mpv_set_option_string(hMpv, "cache", "no")); // video stutters if enabled + check_error(mpv_set_option_string(hMpv, "fullscreen", "yes")); + check_error(mpv_set_option_string(hMpv, "sub-forced-only", "yes")); + check_error(mpv_set_option_string(hMpv, "ontop", "yes")); + check_error(mpv_set_option_string(hMpv, "cursor-autohide", "always")); + check_error(mpv_set_option_string(hMpv, "stop-playback-on-init-failure", "no")); + check_error(mpv_set_option(hMpv, "osd-level", MPV_FORMAT_INT64, &osdlevel)); + + if (MpvPluginConfig->UsePassthrough) + { + check_error(mpv_set_option_string(hMpv, "ad", "-spdif:mp3,-spdif:aac,spdif:*")); + if (MpvPluginConfig->UseDtsHdPassthrough) + check_error(mpv_set_option_string(hMpv, "ad-spdif-dtshd", "yes")); + } + else + { + int64_t StartVolume = cDevice::CurrentVolume() / 2.55; + check_error(mpv_set_option(hMpv, "volume", MPV_FORMAT_INT64, &StartVolume)); + if (MpvPluginConfig->StereoDownmix) + { + check_error(mpv_set_option_string(hMpv, "ad-lavc-downmix", "yes")); + check_error(mpv_set_option_string(hMpv, "audio-channels", "2")); + } + } + + if (PlayShuffle && IsPlaylist(PlayFilename)) + { + dsyslog("use shuffle\n"); + check_error(mpv_set_option_string(hMpv, "shuffle", "yes")); + } + +#ifdef DEBUG + mpv_request_log_messages(hMpv, "info"); +#else + mpv_request_log_messages(hMpv, "error"); +#endif + + if (mpv_initialize(hMpv) < 0) + { + esyslog("[mpv] failed to initialize\n"); + cControl::Shutdown(); + } + + running = 1; + + isyslog("[mpv] playing %s\n", PlayFilename.c_str()); + if (!IsPlaylist(PlayFilename)) + { + const char *cmd[] = {"loadfile", PlayFilename.c_str(), NULL}; + mpv_command(hMpv, cmd); + } + else + { + const char *cmd[] = {"loadlist", PlayFilename.c_str(), NULL}; + mpv_command(hMpv, cmd); + } + + // start thread to observe and react on mpv events + pthread_create(&ObserverThreadHandle, NULL, ObserverThread, this); + + // set back locale + setlocale(LC_NUMERIC, LocaleSave.c_str()); +} + +void cMpvPlayer::HandlePropertyChange(mpv_event *event) +{ + mpv_event_property *property = (mpv_event_property *) event->data; + + if (!property->data) + return; + + // don't log on time-pos change since this floods the log + if (event->reply_userdata != MPV_OBSERVE_TIME_POS) + { + dsyslog("[mpv]: property %s \n", property->name); + } + + switch (event->reply_userdata) + { + case MPV_OBSERVE_TIME_POS : + PlayerCurrent = (int)*(double*)property->data; + break; + + case MPV_OBSERVE_DISC_MENU : + PlayerDiscNav = (int)*(int64_t*)property->data; + break; + + case MPV_OBSERVE_FPS : + PlayerFps = (int)*(double*)property->data; + break; + + case MPV_OBSERVE_FILENAME : + PlayerFilename = *(char**)property->data; + break; + + case MPV_OBSERVE_LENGTH : + PlayerTotal = (int)*(double*)property->data; + break; + + case MPV_OBSERVE_CHAPTERS : + PlayerNumChapters = (int)*(int64_t*)property->data; + mpv_node Node; + mpv_get_property(hMpv, "chapter-list", MPV_FORMAT_NODE, &Node); + ChapterTitles.clear(); + PlayerChapters.clear(); + if (Node.format == MPV_FORMAT_NODE_ARRAY) + { + for (int i=0; i<Node.u.list->num; i++) + { + ChapterTitles.push_back (Node.u.list->values[i].u.list->values[0].u.string); + PlayerChapters.push_back (Node.u.list->values[i].u.list->values[1].u.double_); + } + } + break; + + case MPV_OBSERVE_CHAPTER : + PlayerChapter = (int)*(int64_t*)property->data; + break; + + case MPV_OBSERVE_PAUSE : + PlayerPaused = (int)*(int64_t*)property->data; + break; + + case MPV_OBSERVE_SPEED : + PlayerSpeed = (int)*(double*)property->data; + break; + + case MPV_OBSERVE_MEDIA_TITLE : + mediaTitle = *(char**)property->data; + break; + } +} + +void cMpvPlayer::HandleTracksChange() +{ + mpv_node Node; + mpv_get_property(hMpv, "track-list", MPV_FORMAT_NODE, &Node); + if (!Node.format == MPV_FORMAT_NODE_ARRAY) + return; + + // loop though available tracks + for (int i=0; i<Node.u.list->num; i++) + { + int TrackId; + string TrackType; + string TrackLanguage = "undefined"; + string TrackTitle = ""; + for (int j=0; j<Node.u.list->values[i].u.list->num; j++) + { + if (strcmp(Node.u.list->values[i].u.list->keys[j], "id") == 0) + TrackId = Node.u.list->values[i].u.list->values[j].u.int64; + if (strcmp(Node.u.list->values[i].u.list->keys[j], "type") == 0) + TrackType = Node.u.list->values[i].u.list->values[j].u.string; + if (strcmp(Node.u.list->values[i].u.list->keys[j], "lang") == 0) + TrackLanguage = Node.u.list->values[i].u.list->values[j].u.string; + if (strcmp(Node.u.list->values[i].u.list->keys[j], "title") == 0) + TrackTitle = Node.u.list->values[i].u.list->values[j].u.string; + } + if (TrackType == "audio") + { + eTrackType type = ttAudio; + DeviceSetAvailableTrack(type, i, TrackId, TrackLanguage.c_str(), TrackTitle.c_str()); + } + else if (TrackType == "sub") + { + eTrackType type = ttSubtitle; + DeviceSetAvailableTrack(type, 0, 0, "Off"); + DeviceSetAvailableTrack(type, i, TrackId, TrackLanguage.c_str(), TrackTitle.c_str()); + } + } +} + +void cMpvPlayer::OsdClose() +{ + dsyslog("[mpv] %s\n", __FUNCTION__); + + SendCommand ("overlay_remove 1"); +} + +void cMpvPlayer::Shutdown() +{ + mediaTitle = ""; + running = 0; + MpvPluginConfig->TitleOverride = ""; + ChapterTitles.clear(); + PlayerChapters.clear(); + + if (ObserverThreadHandle) + pthread_cancel(ObserverThreadHandle); + + mpv_terminate_destroy(hMpv); + hMpv = NULL; + cOsdProvider::Shutdown(); + if (MpvPluginConfig->RefreshRate) + { + ChangeFrameRate(OriginalFps); + OriginalFps = -1; + } +} + +void cMpvPlayer::SwitchOsdToMpv() +{ + dsyslog("[mpv] %s\n", __FUNCTION__); + cOsdProvider::Shutdown(); + new cMpvOsdProvider(this); +} + +bool cMpvPlayer::IsPlaylist(string File) +{ + for (unsigned int i=0;i<MpvPluginConfig->PlaylistExtensions.size();i++) + { + if (File.substr(File.find_last_of(".") + 1) == MpvPluginConfig->PlaylistExtensions[i]) + return true; + } + + return false; +} + +void cMpvPlayer::ChangeFrameRate(int TargetRate) +{ + #ifdef USE_XRANDR + if (!MpvPluginConfig->RefreshRate) + return; + + Display *Dpy; + int RefreshRate; + XRRScreenConfiguration *CurrInfo; + + if (TargetRate == 25) + TargetRate = 50; // fix DVD audio and since this is doubled it's ok + + if (TargetRate == 23) + TargetRate = 24; + + Dpy = XOpenDisplay(MpvPluginConfig->X11Display.c_str()); + + if (Dpy) + { + short *Rates; + int NumberOfRates; + SizeID CurrentSizeId; + Rotation CurrentRotation; + int RateFound = 0; + + CurrInfo = XRRGetScreenInfo(Dpy, DefaultRootWindow(Dpy)); + RefreshRate = XRRConfigCurrentRate(CurrInfo); + CurrentSizeId = + XRRConfigCurrentConfiguration(CurrInfo, &CurrentRotation); + Rates = XRRConfigRates(CurrInfo, CurrentSizeId, &NumberOfRates); + + while (NumberOfRates-- > 0) + { + if (TargetRate == *Rates++) + RateFound = 1; + } + + if ((RefreshRate != TargetRate) && (RateFound == 1)) + { + OriginalFps = RefreshRate; + XRRSetScreenConfigAndRate(Dpy, CurrInfo, DefaultRootWindow(Dpy), + CurrentSizeId, CurrentRotation, TargetRate, CurrentTime); + } + + XRRFreeScreenConfigInfo(CurrInfo); + XCloseDisplay(Dpy); + } +#endif +} + +void cMpvPlayer::SendCommand(const char *cmd, ...) +{ + if (!PlayerIsRunning()) + return; + va_list va; + char buf[256]; + va_start(va, cmd); + vsnprintf(buf, sizeof(buf), cmd, va); + va_end(va); + mpv_command_string(hMpv, buf); +} + +void cMpvPlayer::Seek(int Seconds) +{ + SendCommand("seek %d\n", Seconds); +} + +void cMpvPlayer::SetTimePos(int Seconds) +{ + SendCommand("set time-pos %d\n", Seconds); +} + +void cMpvPlayer::SetSpeed(int Speed) +{ + SendCommand("set speed %d\n", Speed); +} + +void cMpvPlayer::SetAudio(int Audio) +{ + SendCommand("set audio %d\n", Audio); +} + +void cMpvPlayer::SetSubtitle(int Subtitle) +{ + SendCommand("set sub %d\n", Subtitle); +} + +void cMpvPlayer::TogglePause() +{ + SendCommand("cycle pause\n"); +} + +void cMpvPlayer::QuitPlayer() +{ + SendCommand("quit\n"); +} + +void cMpvPlayer::DiscNavUp() +{ + SendCommand("discnav up\n"); +} + +void cMpvPlayer::DiscNavDown() +{ + SendCommand("discnav down\n"); +} + +void cMpvPlayer::DiscNavLeft() +{ + SendCommand("discnav left\n"); +} + +void cMpvPlayer::DiscNavRight() +{ + SendCommand("discnav right\n"); +} + +void cMpvPlayer::DiscNavMenu() +{ + SendCommand("discnav menu\n"); +} + +void cMpvPlayer::DiscNavSelect() +{ + SendCommand("discnav select\n"); +} + +void cMpvPlayer::DiscNavPrev() +{ + SendCommand("discnav prev\n"); +} + +void cMpvPlayer::PreviousChapter() +{ + SendCommand("cycle chapter -1\n"); +} + +void cMpvPlayer::NextChapter() +{ + SendCommand("cycle chapter 1\n"); +} + +void cMpvPlayer::NextPlaylistItem() +{ + SendCommand("pt_step 1\n"); +} + +void cMpvPlayer::PreviousPlaylistItem() +{ + SendCommand("pt_step -1\n"); +} + +void cMpvPlayer::SetVolume(int Volume) +{ + SendCommand("set volume %d\n", Volume); +} + diff --git a/player.h b/player.h new file mode 100644 index 0000000..82fe5cc --- /dev/null +++ b/player.h @@ -0,0 +1,99 @@ +////////////////////////////////////////////////////////////////////////////// +/// /// +/// This file is part of the VDR mpv plugin and licensed under AGPLv3 /// +/// /// +/// See the README file for copyright information /// +/// /// +////////////////////////////////////////////////////////////////////////////// + +#ifndef __MPV_PLAYER_H +#define __MPV_PLAYER_H + +#include <string> +#include <vector> +#include <vdr/player.h> +#include <mpv/client.h> + +using std::string; +using std::vector; + +class cMpvPlayer:public cPlayer +{ + private: + void PlayerStart(); + void SwitchOsdToMpv(); + + string PlayFilename; // file to play + bool PlayShuffle; // shuffle playlist + pthread_t ObserverThreadHandle; // player property observer thread + mpv_handle *hMpv; // handle to mpv + int OriginalFps; // fps before modeswitch + bool IsPlaylist(string File); // returns true if the given file is a playlist + void ChangeFrameRate(int TargetRate); + void HandlePropertyChange(mpv_event *event); // handle change mpv property + void HandleTracksChange(); // handle mpv track information + static void *ObserverThread(void *handle); + + // Player status variables + int PlayerPaused; // player paused + int PlayerDiscNav; // discnav active + int PlayerNumChapters; // number of chapters + int PlayerChapter; // current chapter + int PlayerFps; // frames per second + int PlayerCurrent; // current postion in seconds + int PlayerTotal; // total length in seconds + string PlayerFilename; // filename currently playing + int PlayerSpeed; // player playback speed + vector<int> PlayerChapters; // chapter start times + vector<string> ChapterTitles; // chapter titles + string mediaTitle; // title from meta data + static volatile int running; + + public: + cMpvPlayer(string Filename, bool Shuffle=false); + virtual ~cMpvPlayer(); + void Activate(bool); // player attached/detached + virtual void SetAudioTrack(eTrackType Type, const tTrackId *TrackId); + virtual void SetSubtitleTrack(eTrackType Type, const tTrackId *TrackId); + void OsdClose(); // clear or close current OSD + void Shutdown(); + static volatile int PlayerIsRunning() { return running; } + + // functions to send commands to mpv + void SendCommand(const char *cmd, ...); + void Seek(int Seconds); // seek n seconds + void SetTimePos(int Seconds); + void SetSpeed(int Speed); + void SetAudio(int Audio); + void SetSubtitle(int Subtitle); + void TogglePause(); + void QuitPlayer(); + void DiscNavUp(); + void DiscNavDown(); + void DiscNavLeft(); + void DiscNavRight(); + void DiscNavMenu(); + void DiscNavSelect(); + void DiscNavPrev(); + void PreviousChapter(); + void NextChapter(); + void NextPlaylistItem(); + void PreviousPlaylistItem(); + void SetVolume(int Volume); + + // functions to get different status information about the current playback + int IsPaused() { return PlayerPaused; } + int DiscNavActive() { return PlayerDiscNav; } + int NumChapters() { return PlayerNumChapters; } + int CurrentChapter() { return PlayerChapter + 1; } // return the current value +1 since mpv begins at 0 + int CurrentPlayTime() { return PlayerCurrent; } + int TotalPlayTime() { return PlayerTotal; } + int CurrentFps() { return PlayerFps; } + string CurrentFile() { return PlayerFilename; } + int CurrentPlaybackSpeed() { return PlayerSpeed; } + int ChapterStartTime(unsigned int Chapter) { if (Chapter <= PlayerChapters.size()) return PlayerChapters[Chapter-1]; else return 0; } + string ChapterTitle(unsigned int Chapter) { if (Chapter <= ChapterTitles.size()) return ChapterTitles[Chapter-1]; else return ""; } + string MediaTitle() { return mediaTitle; } +}; + +#endif diff --git a/playmenu.c b/playmenu.c new file mode 100644 index 0000000..2943e94 --- /dev/null +++ b/playmenu.c @@ -0,0 +1,92 @@ +////////////////////////////////////////////////////////////////////////////// +/// /// +/// This file is part of the VDR mpv plugin and licensed under AGPLv3 /// +/// /// +/// See the README file for copyright information /// +/// /// +////////////////////////////////////////////////////////////////////////////// + +#include "playmenu.h" +#include "config.h" +#include "mpv_service.h" +#include "filebrowser.h" + +////////////////////////////////////////////////////////////////////////////// +// cOsdMenu +////////////////////////////////////////////////////////////////////////////// + +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)); + + if (cMpvFilebrowser::CurrentDir() != "") + { + AddSubMenu(new cMpvFilebrowser(MpvPluginConfig->BrowserRoot.c_str())); + } +} + +cPlayMenu::~cPlayMenu() +{ +} + +/** +** Handle play plugin menu key event. +** +** @param key key event +*/ +eOSState cPlayMenu::ProcessKey(eKeys key) +{ + eOSState state; + + // call standard function + state = cOsdMenu::ProcessKey(key); + + switch (state) + { + case osUser1: + AddSubMenu(new cMpvFilebrowser(MpvPluginConfig->BrowserRoot.c_str())); + return osContinue; // 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://menu"); + return osEnd; + + default: + break; + } + return state; +} + +int cPlayMenu::PlayFile(const char *Filename) +{ + cPlugin *p; + p = cPluginManager::GetPlugin("mpv"); + if (p) + { + Mpv_StartPlayService_v1_0_t r; + r.Filename = (char *)Filename; + + return p->Service("Mpv-StartPlayService_v1_0", &r); + } + + return false; +} + + diff --git a/playmenu.h b/playmenu.h new file mode 100644 index 0000000..4844585 --- /dev/null +++ b/playmenu.h @@ -0,0 +1,25 @@ +////////////////////////////////////////////////////////////////////////////// +/// /// +/// This file is part of the VDR mpv plugin and licensed under AGPLv3 /// +/// /// +/// See the README file for copyright information /// +/// /// +////////////////////////////////////////////////////////////////////////////// + +#ifndef __MPV_BROWSER_H +#define __MPV_BROWSER_H + +#include <vdr/plugin.h> + +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); + int PlayFile(const char *Filename); +}; + +#endif + diff --git a/po/de_DE.po b/po/de_DE.po new file mode 100644 index 0000000..47837f5 --- /dev/null +++ b/po/de_DE.po @@ -0,0 +1,63 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: vdr-mpv 0.0.3\n" +"Report-Msgid-Bugs-To: <see README>\n" +"POT-Creation-Date: 2015-06-29 22:14+0200\n" +"PO-Revision-Date: 2015-06-29 22:14+0200\n" +"Last-Translator: Maniac\n" +"Language-Team: Maniac\n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#. TRANSLATORS: note the trailing blank! +msgid "Jump: " +msgstr "" + +msgid "mpv player plugin" +msgstr "" + +msgid "MPV" +msgstr "" + +msgid "Browse" +msgstr "" + +msgid "Play optical disc" +msgstr "" + +msgid "Play audio CD" +msgstr "Audio CD" + +msgid "Play video DVD" +msgstr "DVD" + +msgid "Function not working yet, use 3 or 4" +msgstr "" + +msgid "Hide main menu entry" +msgstr "Hauptmenüeintrag verstecken" + +msgid "Audio Passthrough" +msgstr "" + +msgid "DTS-HD Passthrough" +msgstr "" + +msgid "Enable stereo downmix" +msgstr "Aktiviere Stereo downmix" + +msgid "Use prev/next keys for playlist control" +msgstr "Weiter/Zurüc zum steuern der Playlist" + +msgid "Control playlist if no chapters in file" +msgstr "Steuere die Playlist wenn keine Kapitel vorhanden" + +msgid "Show media title instead of filename" +msgstr "Media Titel anstatt des Dateinamen anzeigen" @@ -0,0 +1,65 @@ +////////////////////////////////////////////////////////////////////////////// +/// /// +/// This file is part of the VDR mpv plugin and licensed under AGPLv3 /// +/// /// +/// See the README file for copyright information /// +/// /// +////////////////////////////////////////////////////////////////////////////// + +#include "setup.h" +#include "config.h" + +cMpvPluginSetup::cMpvPluginSetup() +{ + SetupHideMainMenuEntry = MpvPluginConfig->HideMainMenuEntry; + SetupUsePassthrough = MpvPluginConfig->UsePassthrough; + SetupUseDtsHdPassthrough = MpvPluginConfig->UseDtsHdPassthrough; + SetupPlaylistOnNextKey = MpvPluginConfig->PlaylistOnNextKey; + SetupPlaylistIfNoChapters = MpvPluginConfig->PlaylistIfNoChapters; + SetupStereoDownmix = MpvPluginConfig->StereoDownmix; + SetupShowMediaTitle = MpvPluginConfig->ShowMediaTitle; + Setup(); +} + +eOSState cMpvPluginSetup::ProcessKey(eKeys key) +{ + int oldUsePassthrough = SetupUsePassthrough; + int oldPlaylistOnNextKey = SetupPlaylistOnNextKey; + eOSState state = cMenuSetupPage::ProcessKey(key); + + if (key != kNone && (SetupUsePassthrough != oldUsePassthrough || SetupPlaylistOnNextKey != oldPlaylistOnNextKey)) + Setup(); + + return state; +} + +void cMpvPluginSetup::Setup() +{ + int current = Current(); + Clear(); + + Add(new cMenuEditBoolItem(tr("Hide main menu entry"), &SetupHideMainMenuEntry, trVDR("no"), trVDR("yes"))); + Add(new cMenuEditBoolItem(tr("Audio Passthrough"), &SetupUsePassthrough)); + if (SetupUsePassthrough) + Add(new cMenuEditBoolItem(tr("DTS-HD Passthrough"), &SetupUseDtsHdPassthrough)); + else + Add(new cMenuEditBoolItem(tr("Enable stereo downmix"), &SetupStereoDownmix)); + Add(new cMenuEditBoolItem(tr("Use prev/next keys for playlist control"), &SetupPlaylistOnNextKey)); + if (!SetupPlaylistOnNextKey) + Add(new cMenuEditBoolItem(tr("Control playlist if no chapters in file"), &SetupPlaylistIfNoChapters)); + Add(new cMenuEditBoolItem(tr("Show media title instead of filename"), &SetupShowMediaTitle)); + SetCurrent(Get(current)); + Display(); +} + +void cMpvPluginSetup::Store() +{ + SetupStore("HideMainMenuEntry", MpvPluginConfig->HideMainMenuEntry = SetupHideMainMenuEntry); + SetupStore("UsePassthrough", MpvPluginConfig->UsePassthrough = SetupUsePassthrough); + SetupStore("UseDtsHdPassthrough", MpvPluginConfig->UseDtsHdPassthrough = SetupUseDtsHdPassthrough); + SetupStore("StereoDownmix", MpvPluginConfig->StereoDownmix = SetupStereoDownmix); + SetupStore("PlaylistOnNextKey", MpvPluginConfig->PlaylistOnNextKey = SetupPlaylistOnNextKey); + SetupStore("PlaylistIfNoChapters", MpvPluginConfig->PlaylistIfNoChapters = SetupPlaylistIfNoChapters); + SetupStore("ShowMediaTitle", MpvPluginConfig->ShowMediaTitle = SetupShowMediaTitle); +} + @@ -0,0 +1,34 @@ +////////////////////////////////////////////////////////////////////////////// +/// /// +/// This file is part of the VDR mpv plugin and licensed under AGPLv3 /// +/// /// +/// See the README file for copyright information /// +/// /// +////////////////////////////////////////////////////////////////////////////// + +#ifndef __MPV_SETUP_H +#define __MPV_SETUP_H + +#include <vdr/plugin.h> + +class cMpvPluginSetup:public cMenuSetupPage +{ + private: + void Setup(); + virtual void Store(); + + int SetupHideMainMenuEntry; + int SetupUsePassthrough; + int SetupUseDtsHdPassthrough; + int SetupStereoDownmix; + int SetupPlaylistOnNextKey; + int SetupPlaylistIfNoChapters; + int SetupShowMediaTitle; + + public: + cMpvPluginSetup(); + virtual eOSState ProcessKey(eKeys); +}; + +#endif + diff --git a/status.c b/status.c new file mode 100644 index 0000000..5dc6f59 --- /dev/null +++ b/status.c @@ -0,0 +1,25 @@ +////////////////////////////////////////////////////////////////////////////// +/// /// +/// This file is part of the VDR mpv plugin and licensed under AGPLv3 /// +/// /// +/// See the README file for copyright information /// +/// /// +////////////////////////////////////////////////////////////////////////////// + +#include "status.h" + +cMpvStatus::cMpvStatus(cMpvPlayer *player) +{ + Volume = cDevice::CurrentVolume(); + Player = player; +} + +void cMpvStatus::SetVolume(int volume, bool absolute) +{ + if (absolute) + Volume = volume; + else + Volume += volume; + + Player->SetVolume(Volume/2.55); +} diff --git a/status.h b/status.h new file mode 100644 index 0000000..b78e291 --- /dev/null +++ b/status.h @@ -0,0 +1,29 @@ +////////////////////////////////////////////////////////////////////////////// +/// /// +/// This file is part of the VDR mpv plugin and licensed under AGPLv3 /// +/// /// +/// See the README file for copyright information /// +/// /// +////////////////////////////////////////////////////////////////////////////// + +#ifndef __MPV_STATUS_H +#define __MPV_STATUS_H + +#include <vdr/status.h> +#include "player.h" + +class cMpvStatus:public cStatus +{ + private: + int Volume; // current volume + cMpvPlayer *Player; + + public: + cMpvStatus(cMpvPlayer *player); + + protected: + virtual void SetVolume(int volume, bool absolute); // volume changed +}; + +#endif + |