From 36f66f4b7c1086ee416f96cb36fe2a529ebeb322 Mon Sep 17 00:00:00 2001 From: rtlhq Date: Sat, 24 Sep 2022 23:29:46 +0800 Subject: [PATCH] Import Upstream version 0.11.3 --- .gitignore | 12 + .krazy | 2 + AUTHORS | 34 ++ CMakeLists.txt | 56 +++ COPYING.LIB | 462 +++++++++++++++++ Messages.sh | 2 + MimeTypes.cmake | 165 +++++++ cmake/COPYING-CMAKE-SCRIPTS | 22 + cmake/FindKDEWin.cmake | 89 ++++ cmake/FindLIBVLC.cmake | 118 +++++ po/ca/phonon_vlc_qt.po | 46 ++ po/ca@valencia/phonon_vlc_qt.po | 46 ++ po/cs/phonon_vlc_qt.po | 44 ++ po/da/phonon_vlc_qt.po | 37 ++ po/de/phonon_vlc_qt.po | 42 ++ po/en_GB/phonon_vlc_qt.po | 42 ++ po/es/phonon_vlc_qt.po | 44 ++ po/et/phonon_vlc_qt.po | 42 ++ po/eu/phonon_vlc_qt.po | 41 ++ po/fi/phonon_vlc_qt.po | 47 ++ po/fr/phonon_vlc_qt.po | 48 ++ po/gl/phonon_vlc_qt.po | 42 ++ po/hu/phonon_vlc_qt.po | 37 ++ po/id/phonon_vlc_qt.po | 41 ++ po/it/phonon_vlc_qt.po | 41 ++ po/ko/phonon_vlc_qt.po | 41 ++ po/nl/phonon_vlc_qt.po | 42 ++ po/nn/phonon_vlc_qt.po | 44 ++ po/pl/phonon_vlc_qt.po | 38 ++ po/pt/phonon_vlc_qt.po | 43 ++ po/pt_BR/phonon_vlc_qt.po | 37 ++ po/ro/phonon_vlc_qt.po | 42 ++ po/ru/phonon_vlc_qt.po | 38 ++ po/sk/phonon_vlc_qt.po | 42 ++ po/sl/phonon_vlc_qt.po | 48 ++ po/sv/phonon_vlc_qt.po | 42 ++ po/uk/phonon_vlc_qt.po | 45 ++ po/zh_CN/phonon_vlc_qt.po | 38 ++ po/zh_TW/phonon_vlc_qt.po | 36 ++ src/.gitignore | 4 + src/CMakeLists.txt | 95 ++++ src/audio/audiodataoutput.cpp | 164 +++++++ src/audio/audiodataoutput.h | 162 ++++++ src/audio/audiooutput.cpp | 250 ++++++++++ src/audio/audiooutput.h | 136 +++++ src/audio/volumefadereffect.cpp | 127 +++++ src/audio/volumefadereffect.h | 71 +++ src/backend.cpp | 365 ++++++++++++++ src/backend.h | 158 ++++++ src/config.h.cmake | 27 + src/devicemanager.cpp | 340 +++++++++++++ src/devicemanager.h | 146 ++++++ src/effect.cpp | 225 +++++++++ src/effect.h | 80 +++ src/effectmanager.cpp | 135 +++++ src/effectmanager.h | 119 +++++ src/equalizereffect.cpp | 80 +++ src/equalizereffect.h | 55 +++ src/media.cpp | 109 +++++ src/media.h | 77 +++ src/mediacontroller.cpp | 497 +++++++++++++++++++ src/mediacontroller.h | 156 ++++++ src/mediaobject.cpp | 844 ++++++++++++++++++++++++++++++++ src/mediaobject.h | 347 +++++++++++++ src/mediaplayer.cpp | 413 ++++++++++++++++ src/mediaplayer.h | 277 +++++++++++ src/phonon-vlc.json.in | 9 + src/sinknode.cpp | 81 +++ src/sinknode.h | 102 ++++ src/streamreader.cpp | 242 +++++++++ src/streamreader.h | 121 +++++ src/utils/debug.cpp | 232 +++++++++ src/utils/debug.h | 200 ++++++++ src/utils/debug_p.h | 57 +++ src/utils/libvlc.cpp | 128 +++++ src/utils/libvlc.h | 140 ++++++ src/utils/mime.h.cmake | 41 ++ src/utils/vstring.h | 59 +++ src/video/mac/nsvideoview.h | 29 ++ src/video/mac/nsvideoview.mm | 37 ++ src/video/mac/vlcmacwidget.h | 32 ++ src/video/mac/vlcmacwidget.mm | 39 ++ src/video/videodataoutput.cpp | 200 ++++++++ src/video/videodataoutput.h | 74 +++ src/video/videomemorystream.cpp | 147 ++++++ src/video/videomemorystream.h | 83 ++++ src/video/videowidget.cpp | 545 +++++++++++++++++++++ src/video/videowidget.h | 253 ++++++++++ warning-check.rb | 40 ++ 89 files changed, 10528 insertions(+) create mode 100644 .gitignore create mode 100644 .krazy create mode 100644 AUTHORS create mode 100644 CMakeLists.txt create mode 100644 COPYING.LIB create mode 100644 Messages.sh create mode 100644 MimeTypes.cmake create mode 100644 cmake/COPYING-CMAKE-SCRIPTS create mode 100644 cmake/FindKDEWin.cmake create mode 100644 cmake/FindLIBVLC.cmake create mode 100644 po/ca/phonon_vlc_qt.po create mode 100644 po/ca@valencia/phonon_vlc_qt.po create mode 100644 po/cs/phonon_vlc_qt.po create mode 100644 po/da/phonon_vlc_qt.po create mode 100644 po/de/phonon_vlc_qt.po create mode 100644 po/en_GB/phonon_vlc_qt.po create mode 100644 po/es/phonon_vlc_qt.po create mode 100644 po/et/phonon_vlc_qt.po create mode 100644 po/eu/phonon_vlc_qt.po create mode 100644 po/fi/phonon_vlc_qt.po create mode 100644 po/fr/phonon_vlc_qt.po create mode 100644 po/gl/phonon_vlc_qt.po create mode 100644 po/hu/phonon_vlc_qt.po create mode 100644 po/id/phonon_vlc_qt.po create mode 100644 po/it/phonon_vlc_qt.po create mode 100644 po/ko/phonon_vlc_qt.po create mode 100644 po/nl/phonon_vlc_qt.po create mode 100644 po/nn/phonon_vlc_qt.po create mode 100644 po/pl/phonon_vlc_qt.po create mode 100644 po/pt/phonon_vlc_qt.po create mode 100644 po/pt_BR/phonon_vlc_qt.po create mode 100644 po/ro/phonon_vlc_qt.po create mode 100644 po/ru/phonon_vlc_qt.po create mode 100644 po/sk/phonon_vlc_qt.po create mode 100644 po/sl/phonon_vlc_qt.po create mode 100644 po/sv/phonon_vlc_qt.po create mode 100644 po/uk/phonon_vlc_qt.po create mode 100644 po/zh_CN/phonon_vlc_qt.po create mode 100644 po/zh_TW/phonon_vlc_qt.po create mode 100644 src/.gitignore create mode 100644 src/CMakeLists.txt create mode 100644 src/audio/audiodataoutput.cpp create mode 100644 src/audio/audiodataoutput.h create mode 100644 src/audio/audiooutput.cpp create mode 100644 src/audio/audiooutput.h create mode 100644 src/audio/volumefadereffect.cpp create mode 100644 src/audio/volumefadereffect.h create mode 100644 src/backend.cpp create mode 100644 src/backend.h create mode 100644 src/config.h.cmake create mode 100644 src/devicemanager.cpp create mode 100644 src/devicemanager.h create mode 100644 src/effect.cpp create mode 100644 src/effect.h create mode 100644 src/effectmanager.cpp create mode 100644 src/effectmanager.h create mode 100644 src/equalizereffect.cpp create mode 100644 src/equalizereffect.h create mode 100644 src/media.cpp create mode 100644 src/media.h create mode 100644 src/mediacontroller.cpp create mode 100644 src/mediacontroller.h create mode 100644 src/mediaobject.cpp create mode 100644 src/mediaobject.h create mode 100644 src/mediaplayer.cpp create mode 100644 src/mediaplayer.h create mode 100644 src/phonon-vlc.json.in create mode 100644 src/sinknode.cpp create mode 100644 src/sinknode.h create mode 100644 src/streamreader.cpp create mode 100644 src/streamreader.h create mode 100644 src/utils/debug.cpp create mode 100644 src/utils/debug.h create mode 100644 src/utils/debug_p.h create mode 100644 src/utils/libvlc.cpp create mode 100644 src/utils/libvlc.h create mode 100644 src/utils/mime.h.cmake create mode 100644 src/utils/vstring.h create mode 100644 src/video/mac/nsvideoview.h create mode 100644 src/video/mac/nsvideoview.mm create mode 100644 src/video/mac/vlcmacwidget.h create mode 100644 src/video/mac/vlcmacwidget.mm create mode 100644 src/video/videodataoutput.cpp create mode 100644 src/video/videodataoutput.h create mode 100644 src/video/videomemorystream.cpp create mode 100644 src/video/videomemorystream.h create mode 100644 src/video/videowidget.cpp create mode 100644 src/video/videowidget.h create mode 100755 warning-check.rb diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..14c9ef4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +.svn +Makefile +CMakeTmp +CMakeCache.txt +CMakeFiles/ +cmake_install.cmake +cmake_uninstall.cmake +lib/ +*.o +*.so +*.user +build diff --git a/.krazy b/.krazy new file mode 100644 index 0000000..5e8f60f --- /dev/null +++ b/.krazy @@ -0,0 +1,2 @@ +SKIP /build +SKIP /src/vlc.desktop.cmake diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..a83f12b --- /dev/null +++ b/AUTHORS @@ -0,0 +1,34 @@ +The phonon-VLC authors are: +--------------------------- + +Harald Sitter +Casian Andrei +Ben Cooksley +Jean-Baptiste Kempf +Mark Kretschmann +Martin T. H. Sandsmark +Patrick von Reth +Andreas Hartmetz +Rémi Duraffort +Colin Guthrie +Fathi Boudra +Patrick Spendrin +Rémi Denis-Courmont +Arno Rehn +Kamil Klimek +Michael Forney +Raphael Kubo da Costa +Alex Neundorf +Benoit Calvez +Myriam Schweingruber +Romain Perier +Valentin Rusu +Albert Astals Cid +Alejandro Wainzinger +Alexander Potashev +Andrius da Costa Ribas +Arnaud Le Roy +Locke Shinseiko +Pino Toscano +Ralf Habacker +Thiago Macieira diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5f63889 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,56 @@ +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) +project(PhononVLC VERSION 0.11.3) + +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +include(FeatureSummary) + +# ECM +find_package(ECM 5.60 NO_MODULE) +set_package_properties(ECM PROPERTIES + TYPE REQUIRED + DESCRIPTION "Extra CMake Modules" + URL "https://api.kde.org/frameworks/extra-cmake-modules/html/index.html") +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) + +include(KDEInstallDirs) +include(ECMPoQmTools) +include(KDECMakeSettings) +include(KDECompilerSettings) +include(ECMSetupVersion) + +# Phonon +find_package(Phonon4Qt5 4.10.60 NO_MODULE) +set_package_properties(Phonon4Qt5 PROPERTIES + TYPE REQUIRED + DESCRIPTION "Phonon core library" + URL "https://api.kde.org/phonon/html/index.html") + +find_package(Phonon4Qt5Experimental 4.10.60 NO_MODULE) +set_package_properties(Phonon4Qt5Experimental PROPERTIES + TYPE OPTIONAL + DESCRIPTION "Phonon experimental library" + URL "https://api.kde.org/phonon/html/index.html") +if(Phonon4Qt5Experimental_FOUND) + set(PHONON_EXPERIMENTAL TRUE) +endif() + +# LibVLC +find_package(LIBVLC 2.1.0) +set_package_properties(LIBVLC PROPERTIES + TYPE REQUIRED + DESCRIPTION "VLC C library" + URL "http://git.videolan.org") + +ecm_setup_version(PROJECT VARIABLE_PREFIX PHONON_VLC) + +# PVLC only uses static mimetype lists, they are created by cmake, hooray. +include(MimeTypes.cmake) + +add_subdirectory(src) + +if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po") + ecm_install_po_files_as_qm(po) +endif() + +feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/COPYING.LIB b/COPYING.LIB new file mode 100644 index 0000000..bcbd562 --- /dev/null +++ b/COPYING.LIB @@ -0,0 +1,462 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + diff --git a/Messages.sh b/Messages.sh new file mode 100644 index 0000000..61535ee --- /dev/null +++ b/Messages.sh @@ -0,0 +1,2 @@ +#! /usr/bin/env bash +$EXTRACT_TR_STRINGS $(find . -name "*.cpp" -o -name "*.h") -o $podir/phonon_vlc_qt.pot diff --git a/MimeTypes.cmake b/MimeTypes.cmake new file mode 100644 index 0000000..8f828d8 --- /dev/null +++ b/MimeTypes.cmake @@ -0,0 +1,165 @@ +# Copyright (C) 2012, Harald Sitter +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +set(PHONON_VLC_MIME_TYPES + application/mpeg4-iod + application/mpeg4-muxcodetable + application/mxf + application/ogg + application/ram + application/sdp + application/vnd.apple.mpegurl + application/vnd.ms-asf + application/vnd.ms-wpl + application/vnd.rn-realmedia + application/vnd.rn-realmedia-vbr + application/x-cd-image + application/x-extension-m4a + application/x-extension-mp4 + application/x-flac + application/x-flash-video + application/x-matroska + application/x-ogg + application/x-quicktime-media-link + application/x-quicktimeplayer + application/x-shockwave-flash + application/xspf+xml + audio/3gpp + audio/3gpp2 + audio/AMR + audio/AMR-WB + audio/aac + audio/ac3 + audio/basic + audio/dv + audio/eac3 + audio/flac + audio/m4a + audio/midi + audio/mp1 + audio/mp2 + audio/mp3 + audio/mp4 + audio/mpeg + audio/mpegurl + audio/mpg + audio/ogg + audio/opus + audio/scpls + audio/vnd.dolby.heaac.1 + audio/vnd.dolby.heaac.2 + audio/vnd.dolby.mlp + audio/vnd.dts + audio/vnd.dts.hd + audio/vnd.rn-realaudio + audio/vorbis + audio/wav + audio/webm + audio/x-aac + audio/x-adpcm + audio/x-aiff + audio/x-ape + audio/x-flac + audio/x-gsm + audio/x-it + audio/x-m4a + audio/x-matroska + audio/x-mod + audio/x-mp1 + audio/x-mp2 + audio/x-mp3 + audio/x-mpeg + audio/x-mpegurl + audio/x-mpg + audio/x-ms-asf + audio/x-ms-asx + audio/x-ms-wax + audio/x-ms-wma + audio/x-musepack + audio/x-pn-aiff + audio/x-pn-au + audio/x-pn-realaudio + audio/x-pn-realaudio-plugin + audio/x-pn-wav + audio/x-pn-windows-acm + audio/x-real-audio + audio/x-realaudio + audio/x-s3m + audio/x-scpls + audio/x-shorten + audio/x-speex + audio/x-tta + audio/x-vorbis + audio/x-vorbis+ogg + audio/x-wav + audio/x-wavpack + audio/x-xm + image/vnd.rn-realpix + misc/ultravox + text/google-video-pointer + text/x-google-video-pointer + video/3gp + video/3gpp + video/3gpp2 + video/avi + video/divx + video/dv + video/fli + video/flv + video/mp2t + video/mp4 + video/mp4v-es + video/mpeg + video/mpeg-system + video/msvideo + video/ogg + video/quicktime + video/vnd.divx + video/vnd.mpegurl + video/vnd.rn-realvideo + video/webm + video/x-anim + video/x-avi + video/x-flc + video/x-fli + video/x-flv + video/x-m4v + video/x-matroska + video/x-mpeg + video/x-mpeg-system + video/x-mpeg2 + video/x-ms-asf + video/x-ms-asf-plugin + video/x-ms-asx + video/x-ms-wm + video/x-ms-wmv + video/x-ms-wmx + video/x-ms-wvx + video/x-msvideo + video/x-nsv + video/x-ogm + video/x-ogm+ogg + video/x-theora + video/x-theora+ogg + x-content/audio-cdda + x-content/audio-player + x-content/video-dvd + x-content/video-svcd + x-content/video-vcd +) + +macro(CREATE_C_ARRAY var list) + set(ret "") + foreach(str ${PHONON_VLC_MIME_TYPES}) + if(NOT ret) + set(ret "\"${str}\"") + else(NOT ret) + set(ret "${ret}, \"${str}\"") + endif(NOT ret) + endforeach(str) + set(${var} "{${ret}, 0}") +endmacro(CREATE_C_ARRAY var list) + +CREATE_C_ARRAY(PHONON_VLC_MIME_TYPES_C_ARRAY ${PHONON_VLC_MIME_TYPES}) diff --git a/cmake/COPYING-CMAKE-SCRIPTS b/cmake/COPYING-CMAKE-SCRIPTS new file mode 100644 index 0000000..4b41776 --- /dev/null +++ b/cmake/COPYING-CMAKE-SCRIPTS @@ -0,0 +1,22 @@ +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/cmake/FindKDEWin.cmake b/cmake/FindKDEWin.cmake new file mode 100644 index 0000000..f9b7bf0 --- /dev/null +++ b/cmake/FindKDEWin.cmake @@ -0,0 +1,89 @@ +# - Try to find the KDEWIN library +# +# Once done this will define +# +# KDEWIN_FOUND - system has KDEWIN +# KDEWIN_INCLUDES - the KDEWIN include directories +# KDEWIN_LIBRARIES - The libraries needed to use KDEWIN + +# Copyright (c) 2006, Alexander Neundorf, +# Copyright (c) 2007-2009, Ralf Habacker, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + + +if (WIN32) + + if(WINCE) + FIND_PACKAGE(WCECOMPAT REQUIRED) + endif(WINCE) + + if (NOT KDEWIN_LIBRARY) + + + find_path(KDEWIN_INCLUDE_DIR kdewin_export.h + ${KDE4_INCLUDE_DIR} + ${CMAKE_INCLUDE_PATH} + ${CMAKE_INSTALL_PREFIX}/include + ) + + # search for kdewin in the default install directory for applications (default of (n)make install) + FILE(TO_CMAKE_PATH "${CMAKE_LIBRARY_PATH}" _cmakeLibraryPathCmakeStyle) + + string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_TOLOWER) + if (CMAKE_BUILD_TYPE_TOLOWER MATCHES debug) + set (LIBRARY_NAME kdewind) + else(CMAKE_BUILD_TYPE_TOLOWER MATCHES debug) + set (LIBRARY_NAME kdewin) + endif (CMAKE_BUILD_TYPE_TOLOWER MATCHES debug) + + find_library(KDEWIN_LIBRARY + NAMES ${LIBRARY_NAME} + PATHS + ${KDE4_LIB_DIR} + ${_cmakeLibraryPathCmakeStyle} + ${CMAKE_INSTALL_PREFIX}/lib + NO_SYSTEM_ENVIRONMENT_PATH + ) + endif (NOT KDEWIN_LIBRARY) + + if (KDEWIN_LIBRARY AND KDEWIN_INCLUDE_DIR) + set(KDEWIN_FOUND TRUE) + # add needed system libs + if(NOT WINCE) + set(KDEWIN_LIBRARIES ${KDEWIN_LIBRARY} user32 shell32 ws2_32 netapi32 userenv) + else(NOT WINCE) + set(KDEWIN_LIBRARIES ${KDEWIN_LIBRARY} ws2 ${WCECOMPAT_LIBRARIES}) + endif(NOT WINCE) + + if (MINGW) + #mingw compiler + set(KDEWIN_INCLUDES ${KDEWIN_INCLUDE_DIR} ${KDEWIN_INCLUDE_DIR}/mingw ${QT_INCLUDES}) + else (MINGW) + # msvc compiler + # add the MS SDK include directory if available + file(TO_CMAKE_PATH "$ENV{MSSDK}" MSSDK_DIR) + if (WINCE) + set(KDEWIN_INCLUDES ${KDEWIN_INCLUDE_DIR} ${KDEWIN_INCLUDE_DIR}/msvc ${WCECOMPAT_INCLUDE_DIR} ${QT_INCLUDES} ${MSSDK_DIR}) + else(WINCE) + set(KDEWIN_INCLUDES ${KDEWIN_INCLUDE_DIR} ${KDEWIN_INCLUDE_DIR}/msvc ${QT_INCLUDES} ${MSSDK_DIR}) + endif(WINCE) + endif (MINGW) + + endif (KDEWIN_LIBRARY AND KDEWIN_INCLUDE_DIR) + # required for configure + set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${KDEWIN_INCLUDES}) + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${KDEWIN_LIBRARIES}) + + if (KDEWIN_FOUND) + if (NOT KDEWin_FIND_QUIETLY) + message(STATUS "Found KDEWin library: ${KDEWIN_LIBRARY}") + endif (NOT KDEWin_FIND_QUIETLY) + + else (KDEWIN_FOUND) + if (KDEWin_FIND_REQUIRED) + message(FATAL_ERROR "Could NOT find KDEWin library\nPlease install it first") + endif (KDEWin_FIND_REQUIRED) + endif (KDEWIN_FOUND) +endif (WIN32) diff --git a/cmake/FindLIBVLC.cmake b/cmake/FindLIBVLC.cmake new file mode 100644 index 0000000..24b2480 --- /dev/null +++ b/cmake/FindLIBVLC.cmake @@ -0,0 +1,118 @@ +# CMake module to search for LIBVLC (VLC library) +# +# Copyright (C) 2011-2018, Harald Sitter +# Copyright (C) 2010, Rohit Yadav +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# +# If it's found it sets LIBVLC_FOUND to TRUE +# and following variables are set: +# LIBVLC_INCLUDE_DIR +# LIBVLC_LIBRARY +# LIBVLC_VERSION + +if(NOT LIBVLC_MIN_VERSION) + set(LIBVLC_MIN_VERSION "0.0") +endif(NOT LIBVLC_MIN_VERSION) + +# find_path and find_library normally search standard locations +# before the specified paths. To search non-standard paths first, +# FIND_* is invoked first with specified paths and NO_DEFAULT_PATH +# and then again with no specified paths to search the default +# locations. When an earlier FIND_* succeeds, subsequent FIND_*s +# searching for the same item do nothing. + +if (NOT WIN32) + find_package(PkgConfig) + pkg_check_modules(PC_LIBVLC libvlc) + set(LIBVLC_DEFINITIONS ${PC_LIBVLC_CFLAGS_OTHER}) +endif (NOT WIN32) + +#Put here path to custom location +#example: /home/user/vlc/include etc.. +find_path(LIBVLC_INCLUDE_DIR vlc/vlc.h +HINTS "$ENV{LIBVLC_INCLUDE_PATH}" ${PC_LIBVLC_INCLUDEDIR} ${PC_LIBVLC_INCLUDE_DIRS} +PATHS + "$ENV{LIB_DIR}/include" + "$ENV{LIB_DIR}/include/vlc" + "/usr/include" + "/usr/include/vlc" + "/usr/local/include" + "/usr/local/include/vlc" + #mingw + c:/msys/local/include +) +find_path(LIBVLC_INCLUDE_DIR PATHS "${CMAKE_INCLUDE_PATH}/vlc" NAMES vlc.h + HINTS ${PC_LIBVLC_INCLUDEDIR} ${PC_LIBVLC_INCLUDE_DIRS}) + +#Put here path to custom location +#example: /home/user/vlc/lib etc.. +find_library(LIBVLC_LIBRARY NAMES vlc libvlc +HINTS "$ENV{LIBVLC_LIBRARY_PATH}" ${PC_LIBVLC_LIBDIR} ${PC_LIBVLC_LIBRARY_DIRS} +PATHS + "$ENV{LIB_DIR}/lib" + #mingw + c:/msys/local/lib +) +find_library(LIBVLC_LIBRARY NAMES vlc libvlc) +find_library(LIBVLCCORE_LIBRARY NAMES vlccore libvlccore +HINTS "$ENV{LIBVLC_LIBRARY_PATH}" ${PC_LIBVLC_LIBDIR} ${PC_LIBVLC_LIBRARY_DIRS} +PATHS + "$ENV{LIB_DIR}/lib" + #mingw + c:/msys/local/lib +) +find_library(LIBVLCCORE_LIBRARY NAMES vlccore libvlccore) + +set(LIBVLC_VERSION ${PC_LIBVLC_VERSION}) +if (NOT LIBVLC_VERSION) + file(READ "${LIBVLC_INCLUDE_DIR}/vlc/libvlc_version.h" _libvlc_version_h) + + string(REGEX MATCH "# define LIBVLC_VERSION_MAJOR +\\(([0-9])\\)" _dummy "${_libvlc_version_h}") + set(_version_major "${CMAKE_MATCH_1}") + + string(REGEX MATCH "# define LIBVLC_VERSION_MINOR +\\(([0-9])\\)" _dummy "${_libvlc_version_h}") + set(_version_minor "${CMAKE_MATCH_1}") + + string(REGEX MATCH "# define LIBVLC_VERSION_REVISION +\\(([0-9])\\)" _dummy "${_libvlc_version_h}") + set(_version_revision "${CMAKE_MATCH_1}") + + # Optionally, one could also parse LIBVLC_VERSION_EXTRA, but it does not + # seem to be used by libvlc.pc. + + set(LIBVLC_VERSION "${_version_major}.${_version_minor}.${_version_revision}") +endif (NOT LIBVLC_VERSION) + +if (LIBVLC_INCLUDE_DIR AND LIBVLC_LIBRARY AND LIBVLCCORE_LIBRARY) +set(LIBVLC_FOUND TRUE) +endif (LIBVLC_INCLUDE_DIR AND LIBVLC_LIBRARY AND LIBVLCCORE_LIBRARY) + +if (LIBVLC_VERSION STRLESS "${LIBVLC_MIN_VERSION}") + message(WARNING "LibVLC version not found: version searched: ${LIBVLC_MIN_VERSION}, found ${LIBVLC_VERSION}\nUnless you are on Windows this is bound to fail.") +# TODO: only activate once version detection can be garunteed (which is currently not the case on windows) +# set(LIBVLC_FOUND FALSE) +endif (LIBVLC_VERSION STRLESS "${LIBVLC_MIN_VERSION}") + +if (NOT LIBVLC_FIND_QUIETLY) + message(STATUS "Found LibVLC include-dir path: ${LIBVLC_INCLUDE_DIR}") + message(STATUS "Found LibVLC library path:${LIBVLC_LIBRARY}") + message(STATUS "Found LibVLCcore library path:${LIBVLCCORE_LIBRARY}") + message(STATUS "Found LibVLC version: ${LIBVLC_VERSION} (searched for: ${LIBVLC_MIN_VERSION})") +endif (NOT LIBVLC_FIND_QUIETLY) + +if(LIBVLC_FOUND AND NOT TARGET LibVLC::LibVLC) + add_library(LibVLC::LibVLC UNKNOWN IMPORTED) + set_target_properties(LibVLC::LibVLC PROPERTIES + IMPORTED_LOCATION "${LIBVLC_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${LIBVLC_INCLUDE_DIR}" + ) +endif() + +if(LIBVLC_FOUND AND NOT TARGET LibVLC::Core) + add_library(LibVLC::Core UNKNOWN IMPORTED) + set_target_properties(LibVLC::Core PROPERTIES + IMPORTED_LOCATION "${LIBVLCCORE_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${LIBVLC_INCLUDE_DIR}" + ) +endif() diff --git a/po/ca/phonon_vlc_qt.po b/po/ca/phonon_vlc_qt.po new file mode 100644 index 0000000..6801c6e --- /dev/null +++ b/po/ca/phonon_vlc_qt.po @@ -0,0 +1,46 @@ +# Translation of phonon_vlc_qt.po to Catalan +# Copyright (C) 2011-2019 This_file_is_part_of_KDE +# This file is distributed under the license LGPL version 2.1 or +# version 3 or later versions approved by the membership of KDE e.V. +# +# Josep Ma. Ferrer , 2011, 2012, 2019. +# Antoni Bella Pérez , 2019. +msgid "" +msgstr "" +"Project-Id-Version: phonon-vlc\n" +"Report-Msgid-Bugs-To: https://bugs.kde.org\n" +"POT-Creation-Date: 2019-07-01 01:43+0200\n" +"PO-Revision-Date: 2019-08-07 10:31+0200\n" +"Last-Translator: Antoni Bella Pérez \n" +"Language-Team: Catalan \n" +"Language: ca\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Lokalize 19.07.70\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Accelerator-Marker: &\n" +"X-Qt-Contexts: true\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "Ha fallat en inicialitzar la «libVLC»" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Ha fallat en iniciar el dorsal VLC de Phonon.\n" +"\n" +"Això normalment vol dir que hi ha un problema amb la instal·lació del VLC. " +"Si us plau, informeu de l'error al distribuïdor." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Predeterminat" \ No newline at end of file diff --git a/po/ca@valencia/phonon_vlc_qt.po b/po/ca@valencia/phonon_vlc_qt.po new file mode 100644 index 0000000..95d9f7e --- /dev/null +++ b/po/ca@valencia/phonon_vlc_qt.po @@ -0,0 +1,46 @@ +# Translation of phonon_vlc_qt.po to Catalan (Valencian) +# Copyright (C) 2011-2019 This_file_is_part_of_KDE +# This file is distributed under the license LGPL version 2.1 or +# version 3 or later versions approved by the membership of KDE e.V. +# +# Josep Ma. Ferrer , 2011, 2012, 2019. +# Antoni Bella Pérez , 2019. +msgid "" +msgstr "" +"Project-Id-Version: phonon-vlc\n" +"Report-Msgid-Bugs-To: https://bugs.kde.org\n" +"POT-Creation-Date: 2019-07-01 01:43+0200\n" +"PO-Revision-Date: 2019-08-07 10:31+0200\n" +"Last-Translator: Antoni Bella Pérez \n" +"Language-Team: Catalan \n" +"Language: ca@valencia\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Lokalize 19.07.70\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Accelerator-Marker: &\n" +"X-Qt-Contexts: true\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "Ha fallat en inicialitzar la «libVLC»" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Ha fallat en iniciar el dorsal VLC de Phonon.\n" +"\n" +"Això normalment vol dir que hi ha un problema amb la instal·lació del VLC. " +"Per favor, informeu de l'error al distribuïdor." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Predeterminat" \ No newline at end of file diff --git a/po/cs/phonon_vlc_qt.po b/po/cs/phonon_vlc_qt.po new file mode 100644 index 0000000..1bca62e --- /dev/null +++ b/po/cs/phonon_vlc_qt.po @@ -0,0 +1,44 @@ +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# Vít Pelčák , 2011, 2012, 2019. +# Lukáš Tinkl , 2012. +# Tomáš Chvátal , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: https://bugs.kde.org\n" +"POT-Creation-Date: 2019-07-01 01:50+0200\n" +"PO-Revision-Date: 2019-07-18 15:19+0200\n" +"Last-Translator: Vit Pelcak \n" +"Language-Team: Czech \n" +"Language: cs\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Lokalize 19.04.2\n" +"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" +"X-Qt-Contexts: true\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "Inicializace libVLC selhala" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Podpůrná vrstva Phononu VLC nemohla být spuštěna.\n" +"\n" +"To obvykle znamená problém s instalací VLC. Prosím, nahlaste chybu tvůrci " +"vaší distribuce." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Výchozí" \ No newline at end of file diff --git a/po/da/phonon_vlc_qt.po b/po/da/phonon_vlc_qt.po new file mode 100644 index 0000000..3c0e66f --- /dev/null +++ b/po/da/phonon_vlc_qt.po @@ -0,0 +1,37 @@ +# Martin Schlander , 2020. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"PO-Revision-Date: 2020-04-08 21:14+0200\n" +"Last-Translator: Martin Schlander \n" +"Language-Team: Danish \n" +"Language: da\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Qt-Contexts: true\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Lokalize 18.12.3\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "LibVLC kunne ikke initialisere" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Phonons VLC-motor kunne ikke starte.\n" +"\n" +"Dette skyldes normalt et problem med din VLC-installation. Rapportér " +"venligst fejlen til din distributør." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Standard" \ No newline at end of file diff --git a/po/de/phonon_vlc_qt.po b/po/de/phonon_vlc_qt.po new file mode 100644 index 0000000..b3851a8 --- /dev/null +++ b/po/de/phonon_vlc_qt.po @@ -0,0 +1,42 @@ +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# +# Burkhard Lück , 2018, 2019. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: https://bugs.kde.org\n" +"POT-Creation-Date: 2019-07-01 01:50+0200\n" +"PO-Revision-Date: 2019-07-05 15:28+0200\n" +"Last-Translator: Burkhard Lück \n" +"Language-Team: German \n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Lokalize 18.12.3\n" +"X-Qt-Contexts: true\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "LibVLC kann nicht initialisiert werden" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Das VLC-Backend von Phonon kann nicht gestartet werden.\n" +"\n" +"Das weist meisten auf ein Problem mit Ihrer VLC-Installation hin. Berichten " +"Sie diesen Fehler bitte bei Ihrem Distributor." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Standard" \ No newline at end of file diff --git a/po/en_GB/phonon_vlc_qt.po b/po/en_GB/phonon_vlc_qt.po new file mode 100644 index 0000000..33ca9d5 --- /dev/null +++ b/po/en_GB/phonon_vlc_qt.po @@ -0,0 +1,42 @@ +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# +# Steve Allewell , 2017, 2019. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: https://bugs.kde.org\n" +"POT-Creation-Date: 2019-07-01 01:50+0200\n" +"PO-Revision-Date: 2019-08-03 17:00+0100\n" +"Last-Translator: Steve Allewell \n" +"Language-Team: British English \n" +"Language: en_GB\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Lokalize 19.07.70\n" +"X-Qt-Contexts: true\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "LibVLC Failed to Initialise" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Default" \ No newline at end of file diff --git a/po/es/phonon_vlc_qt.po b/po/es/phonon_vlc_qt.po new file mode 100644 index 0000000..e9f6cf3 --- /dev/null +++ b/po/es/phonon_vlc_qt.po @@ -0,0 +1,44 @@ +# Spanish translations for phonon_vlc.po package. +# Copyright (C) 2017 This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# +# Automatically generated, 2017. +# Eloy Cuadra , 2017, 2019. +msgid "" +msgstr "" +"Project-Id-Version: phonon_vlc\n" +"Report-Msgid-Bugs-To: https://bugs.kde.org\n" +"POT-Creation-Date: 2019-07-01 01:50+0200\n" +"PO-Revision-Date: 2019-07-09 19:06+0200\n" +"Last-Translator: Eloy Cuadra \n" +"Language-Team: Spanish \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Lokalize 19.04.2\n" +"X-Qt-Contexts: true\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "Ha fallado la inicialización de LibVLC" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Ha fallado el inicio del motor VLC de Phonon.\n" +"\n" +"Esto suele significar que hay un problema en su instalación de VLC. Por " +"favor, informe del error a su distribuidor." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Por omisión" \ No newline at end of file diff --git a/po/et/phonon_vlc_qt.po b/po/et/phonon_vlc_qt.po new file mode 100644 index 0000000..863934b --- /dev/null +++ b/po/et/phonon_vlc_qt.po @@ -0,0 +1,42 @@ +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# +# Marek Laane , 2011, 2012, 2019. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: https://bugs.kde.org\n" +"POT-Creation-Date: 2019-07-01 01:43+0200\n" +"PO-Revision-Date: 2019-11-08 00:18+0200\n" +"Last-Translator: Marek Laane \n" +"Language-Team: Estonian \n" +"Language: et\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Lokalize 19.08.1\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Qt-Contexts: true\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "LibVLC initsialiseerimine nurjus" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Phononi VLC taustaprogrammi käivitamine nurjus.\n" +"\n" +"Tavaliselt tähendab see probleemi sinu VLC paigaldusega, palun anna veast " +"teada oma distributsiooni pakkujale." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Vaikimisi" \ No newline at end of file diff --git a/po/eu/phonon_vlc_qt.po b/po/eu/phonon_vlc_qt.po new file mode 100644 index 0000000..9dd7f58 --- /dev/null +++ b/po/eu/phonon_vlc_qt.po @@ -0,0 +1,41 @@ +# Translation of phonon_vlc_qt.po to Euskara/Basque (eu). +# KDE euskaratzeko proiektuaren arduraduna . +# +# Translators: +# Iñigo Salvador Azurmendi , 2019. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"PO-Revision-Date: 2019-07-07 22:18+0200\n" +"Last-Translator: Iñigo Salvador Azurmendi \n" +"Language-Team: Basque \n" +"Language: eu\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Qt-Contexts: true\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Lokalize 19.04.2\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "LibVLC hasieratzea huts egin du" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Phonon-en VLC bizkarraldekoa abiatzea huts egin du.\n" +"\n" +"Honek adierazi ohi du zure instalaziorekin arazo bat dagoela, bidali akats " +"txosten bat zure banatzaileari." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Lehenetsia" \ No newline at end of file diff --git a/po/fi/phonon_vlc_qt.po b/po/fi/phonon_vlc_qt.po new file mode 100644 index 0000000..4234234 --- /dev/null +++ b/po/fi/phonon_vlc_qt.po @@ -0,0 +1,47 @@ +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# Lasse Liehu , 2011, 2013. +# Tommi Nieminen , 2019. +# +# KDE Finnish translation sprint participants: +# Author: Artnay +# Author: Rytilahti +msgid "" +msgstr "" +"Project-Id-Version: phonon_vlc\n" +"Report-Msgid-Bugs-To: https://bugs.kde.org\n" +"POT-Creation-Date: 2019-07-01 01:50+0200\n" +"PO-Revision-Date: 2019-07-30 23:06+0200\n" +"Last-Translator: Tommi Nieminen \n" +"Language-Team: Finnish \n" +"Language: fi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-POT-Import-Date: 2012-12-01 22:27:40+0000\n" +"X-Generator: Lokalize 2.0\n" +"X-Qt-Contexts: true\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "LibVLC:n alustus epäonnistui" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Phononin VLC-taustajärjestelmän käynnistys epäonnistui.\n" +"\n" +"Tämä tarkoittaa yleensä, että VLC-asennus on virheellinen. Ilmoita viasta " +"jakelullesi." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Oletus" \ No newline at end of file diff --git a/po/fr/phonon_vlc_qt.po b/po/fr/phonon_vlc_qt.po new file mode 100644 index 0000000..e4a6462 --- /dev/null +++ b/po/fr/phonon_vlc_qt.po @@ -0,0 +1,48 @@ +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# Geoffray Levasseur , 2011, 2012, 2013. +# xavier , 2013. +# Simon Depiets , 2019. +# Xavier Besnard , 2021. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: https://bugs.kde.org\n" +"POT-Creation-Date: 2019-07-01 01:50+0200\n" +"PO-Revision-Date: 2021-01-13 17:49+0100\n" +"Last-Translator: Xavier Besnard \n" +"Language-Team: French \n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Generator: Lokalize 20.12.0\n" +"X-Environment: kde\n" +"X-Accelerator-Marker: &\n" +"X-Text-Markup: qtrich\n" +"X-Qt-Contexts: true\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "L'initialisation de « LibVLC » a échoué." + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Le démarrage du moteur « VLC » de Phonon a échoué.\n" +"\n" +"Ceci signifie généralement qu'il y a un problème avec votre installation de " +"VLC. Veuillez donc rapporter un bogue à votre distributeur." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Par défaut" \ No newline at end of file diff --git a/po/gl/phonon_vlc_qt.po b/po/gl/phonon_vlc_qt.po new file mode 100644 index 0000000..4db8b62 --- /dev/null +++ b/po/gl/phonon_vlc_qt.po @@ -0,0 +1,42 @@ +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# Marce Villarino , 2011, 2012, 2014. +# Adrián Chaves (Gallaecio) , 2017, 2019. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: https://bugs.kde.org\n" +"POT-Creation-Date: 2019-07-01 01:50+0200\n" +"PO-Revision-Date: 2019-07-06 20:23+0200\n" +"Last-Translator: Adrián Chaves (Gallaecio) \n" +"Language-Team: Galician \n" +"Language: gl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Qt-Contexts: true\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "Non se puido inicializar LibVLC" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Non se puido iniciar a infraestrutura de VLC para Phonon.\n" +"\n" +"Xeralmente isto indica que hai algún problema coa instalación do VLC. Envíe " +"un informe do fallo ao distribuidor." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Predeterminado" \ No newline at end of file diff --git a/po/hu/phonon_vlc_qt.po b/po/hu/phonon_vlc_qt.po new file mode 100644 index 0000000..3160259 --- /dev/null +++ b/po/hu/phonon_vlc_qt.po @@ -0,0 +1,37 @@ +# Kristóf Kiszel , 2021. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"PO-Revision-Date: 2021-01-02 10:00+0100\n" +"Last-Translator: Kristóf Kiszel \n" +"Language-Team: Hungarian \n" +"Language: hu\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Qt-Contexts: true\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Lokalize 21.03.70\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "A LibVLC inicializálása meghiúsult" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Nem sikerült elindítani a Phono VLC modulját.\n" +"\n" +"Ez általában azt jelenti, hogy hiba van a VLC telepítésével. Jelentse a " +"hibát a disztribúció fejlesztőinek." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Alapértelmezett" \ No newline at end of file diff --git a/po/id/phonon_vlc_qt.po b/po/id/phonon_vlc_qt.po new file mode 100644 index 0000000..d6603ff --- /dev/null +++ b/po/id/phonon_vlc_qt.po @@ -0,0 +1,41 @@ +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# Wantoyo , 2018, 2019. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: https://bugs.kde.org\n" +"POT-Creation-Date: 2019-07-01 01:50+0200\n" +"PO-Revision-Date: 2019-07-04 21:10+0700\n" +"Last-Translator: Wantoyo \n" +"Language-Team: Indonesian \n" +"Language: id\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Qt-Contexts: true\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "LibVLC Gagal Mengnisialisasi" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Backend phonon VLC gagal dimulaikan.\n" +"\n" +"Ini mungkin biasanya bermasalah dengan penginstalan VLC-mu, silakan laporkan " +"bug kepada distributormu." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Baku" \ No newline at end of file diff --git a/po/it/phonon_vlc_qt.po b/po/it/phonon_vlc_qt.po new file mode 100644 index 0000000..4742806 --- /dev/null +++ b/po/it/phonon_vlc_qt.po @@ -0,0 +1,41 @@ +# translation of phonon_vlc.po to Italian +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the phonon_vlc package. +# Luigi Toscano , 2011, 2012, 2019. +# +msgid "" +msgstr "" +"Project-Id-Version: phonon_vlc\n" +"PO-Revision-Date: 2019-07-04 12:51+0200\n" +"Last-Translator: Luigi Toscano \n" +"Language-Team: Italian \n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Lokalize 18.12.2\n" +"X-Qt-Contexts: true\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "Inizializzazione di LibVLC non riuscita" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Si è verificato un errore durante l'esecuzione del motore VLC di Phonon.\n" +"\n" +"Questo di solito indica un problema nell'installazione di VLC, segnala il " +"bug presso il tuo distributore." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Predefinito" \ No newline at end of file diff --git a/po/ko/phonon_vlc_qt.po b/po/ko/phonon_vlc_qt.po new file mode 100644 index 0000000..e5fc38c --- /dev/null +++ b/po/ko/phonon_vlc_qt.po @@ -0,0 +1,41 @@ +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# Shinjo Park , 2011, 2013, 2019. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: https://bugs.kde.org\n" +"POT-Creation-Date: 2019-07-01 01:50+0200\n" +"PO-Revision-Date: 2019-07-18 23:25+0200\n" +"Last-Translator: Shinjo Park \n" +"Language-Team: Korean \n" +"Language: ko\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Qt-Contexts: true\n" +"X-Generator: Lokalize 18.12.3\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "LibVLC를 초기화할 수 없음" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Phonon VLC 백엔드를 시작할 수 없습니다.\n" +"\n" +"VLC 설치에 문제가 있는 것 같으므로 배포자에게 문의하십시오." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "기본값" \ No newline at end of file diff --git a/po/nl/phonon_vlc_qt.po b/po/nl/phonon_vlc_qt.po new file mode 100644 index 0000000..ad93cb2 --- /dev/null +++ b/po/nl/phonon_vlc_qt.po @@ -0,0 +1,42 @@ +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# +# Freek de Kruijf , 2011, 2012, 2019. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: https://bugs.kde.org\n" +"POT-Creation-Date: 2019-07-01 01:50+0200\n" +"PO-Revision-Date: 2019-07-04 21:28+0200\n" +"Last-Translator: Freek de Kruijf \n" +"Language-Team: Dutch \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Lokalize 19.04.2\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Qt-Contexts: true\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "Initialisatie van LibVLC is mislukt" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Het opstarten van de VLC-backend van Phonon is mislukt.\n" +"\n" +"Dit betekent meestal een probleem met uw VLC-installatie, aub. een " +"bugrapport indienen bij uw leverancier." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Standaard" \ No newline at end of file diff --git a/po/nn/phonon_vlc_qt.po b/po/nn/phonon_vlc_qt.po new file mode 100644 index 0000000..cf985b7 --- /dev/null +++ b/po/nn/phonon_vlc_qt.po @@ -0,0 +1,44 @@ +# Translation of phonon_vlc_qt to Norwegian Nynorsk +# +# Karl Ove Hufthammer , 2015, 2019. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: https://bugs.kde.org\n" +"POT-Creation-Date: 2019-07-01 01:50+0200\n" +"PO-Revision-Date: 2019-07-17 20:51+0200\n" +"Last-Translator: Karl Ove Hufthammer \n" +"Language-Team: Norwegian Nynorsk \n" +"Language: nn\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Lokalize 19.04.2\n" +"X-Environment: kde\n" +"X-Accelerator-Marker: &\n" +"X-Text-Markup: qtrich\n" +"X-Qt-Contexts: true\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "Klarte ikkje starta LibVLC" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Klarte ikkje starta VLC-motoren til Phonon.\n" +"\n" +"Ein vanleg grunn er at VLC ikkje er rett installert. Meld eventuelt frå som " +"ein feil i distroen din." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Standard" \ No newline at end of file diff --git a/po/pl/phonon_vlc_qt.po b/po/pl/phonon_vlc_qt.po new file mode 100644 index 0000000..f6f18b3 --- /dev/null +++ b/po/pl/phonon_vlc_qt.po @@ -0,0 +1,38 @@ +# Łukasz Wojniłowicz , 2019. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"PO-Revision-Date: 2019-07-13 06:38+0200\n" +"Last-Translator: Łukasz Wojniłowicz \n" +"Language-Team: Polish \n" +"Language: pl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Qt-Contexts: true\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " +"|| n%100>=20) ? 1 : 2);\n" +"X-Generator: Lokalize 19.07.70\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "Nie udało się zainicjować LibVLC" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Nie udało się uruchomić silnika VLC Phonona.\n" +"\n" +"Zazwyczaj oznacza to kłopot z wgranym VLC. Należy zgłosić to do dystrybutora " +"tego pakietu." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Domyślne" \ No newline at end of file diff --git a/po/pt/phonon_vlc_qt.po b/po/pt/phonon_vlc_qt.po new file mode 100644 index 0000000..fd2e277 --- /dev/null +++ b/po/pt/phonon_vlc_qt.po @@ -0,0 +1,43 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: phonon_vlc\n" +"Report-Msgid-Bugs-To: https://bugs.kde.org\n" +"POT-Creation-Date: 2019-07-01 01:43+0200\n" +"PO-Revision-Date: 2019-07-04 11:08+0100\n" +"Last-Translator: José Nuno Coelho Pires \n" +"Language-Team: Portuguese \n" +"Language: pt\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-POFile-SpellExtra: LibVLC VLC\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Qt-Contexts: true\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "Não Foi Possível Inicializar a LibVLC" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Não foi possível iniciar a infra-estrutura de VLC para o Phonon.\n" +"\n" +"Isto normalmente significa que há um problema na sua instalação do VLC; " +"comunique por favor um erro à sua distribuição." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Predefinição" \ No newline at end of file diff --git a/po/pt_BR/phonon_vlc_qt.po b/po/pt_BR/phonon_vlc_qt.po new file mode 100644 index 0000000..288b100 --- /dev/null +++ b/po/pt_BR/phonon_vlc_qt.po @@ -0,0 +1,37 @@ +# Luiz Fernando Ranghetti , 2019. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"PO-Revision-Date: 2019-07-04 12:19-0300\n" +"Last-Translator: Luiz Fernando Ranghetti \n" +"Language-Team: Portuguese \n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Qt-Contexts: true\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Generator: Lokalize 19.04.2\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "Falha ao inicializar a LibVLC" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Não foi possível iniciar a infraestrutura VLC do Phonon.\n" +"\n" +"Isto normalmente significa que há um problema na sua instalação do VLC. " +"Comunique este erro à sua distribuição." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Padrão" \ No newline at end of file diff --git a/po/ro/phonon_vlc_qt.po b/po/ro/phonon_vlc_qt.po new file mode 100644 index 0000000..2592684 --- /dev/null +++ b/po/ro/phonon_vlc_qt.po @@ -0,0 +1,42 @@ +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# Sergiu Bivol , 2011, 2020. +# Cristian Oneț , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"PO-Revision-Date: 2020-09-13 13:16+0100\n" +"Last-Translator: Sergiu Bivol \n" +"Language-Team: Romanian\n" +"Language: ro\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < " +"20)) ? 1 : 2;\n" +"X-Generator: Lokalize 19.12.3\n" +"X-Qt-Contexts: true\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "Inițializarea LibVLC a eșuat" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Pornirea platformei VLC pentru Phonon a eșuat.\n" +"\n" +"De obicei, aceasta indică o problemă cu instalarea VLC. Raportați o eroare " +"la distribuitorul dumneavoastră." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Implicit" \ No newline at end of file diff --git a/po/ru/phonon_vlc_qt.po b/po/ru/phonon_vlc_qt.po new file mode 100644 index 0000000..ea365ac --- /dev/null +++ b/po/ru/phonon_vlc_qt.po @@ -0,0 +1,38 @@ +# Alexander Yavorsky , 2020. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"PO-Revision-Date: 2020-05-05 18:06+0300\n" +"Last-Translator: Alexander Yavorsky \n" +"Language-Team: Russian \n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Qt-Contexts: true\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"X-Generator: Lokalize 20.04.0\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "Не удалось инициализировать LibVLC" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Не удалось запустить модуль поддержки VLC для Phonon.\n" +"\n" +"Обычно это связано с неправильной установкой VLC. Пожалуйста, сообщите об " +"ошибке разработчикам вашего дистрибутива." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "По умолчанию" \ No newline at end of file diff --git a/po/sk/phonon_vlc_qt.po b/po/sk/phonon_vlc_qt.po new file mode 100644 index 0000000..bbaae11 --- /dev/null +++ b/po/sk/phonon_vlc_qt.po @@ -0,0 +1,42 @@ +# translation of phonon_vlc.po to Slovak +# Roman Paholik , 2012. +# Roman Paholík , 2012. +# Matej Mrenica , 2019. +msgid "" +msgstr "" +"Project-Id-Version: phonon_vlc\n" +"Report-Msgid-Bugs-To: https://bugs.kde.org\n" +"POT-Creation-Date: 2019-07-01 01:50+0200\n" +"PO-Revision-Date: 2019-08-08 12:07+0200\n" +"Last-Translator: Matej Mrenica \n" +"Language-Team: Slovak \n" +"Language: sk\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Lokalize 19.07.90\n" +"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" +"X-Qt-Contexts: true\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "Zlyhala inicializácia LibVLC" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Zlyhalo spustenie Phonon VLC backendu.\n" +"\n" +"Toto obyčajne znamená problém s vašou inštaláciou VLC, prosím zadajte chybu " +"s vašim poskytovateľom." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Predvolené" \ No newline at end of file diff --git a/po/sl/phonon_vlc_qt.po b/po/sl/phonon_vlc_qt.po new file mode 100644 index 0000000..1c25f0e --- /dev/null +++ b/po/sl/phonon_vlc_qt.po @@ -0,0 +1,48 @@ +# Slovenian translation of phonon_vlc. +# Copyright (C) 2011 Free Software Foundation, Inc. +# +# +# Klemen Košir , 2011. +# Andrej Vernekar , 2012. +# Andrej Mernik , 2013. +# Matjaž Jeran , 2019. +msgid "" +msgstr "" +"Project-Id-Version: phonon_vlc\n" +"Report-Msgid-Bugs-To: https://bugs.kde.org\n" +"POT-Creation-Date: \n" +"PO-Revision-Date: 2021-03-19 15:54+0100\n" +"Last-Translator: Matjaž Jeran \n" +"Language-Team: Slovenian \n" +"Language: sl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Translator: Andrej Mernik \n" +"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100>=3 && n" +"%100<=4 ? 2 : 3);\n" +"X-Generator: Poedit 2.4.2\n" +"X-Qt-Contexts: true\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "Začetnih vrednosti knjižnice libVLC ni bilo mogoče nastaviti" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Zaledja VLC za Phonon ni bilo mogoče zagnati.\n" +"\n" +"Razlog za to napako je ponavadi težava z namestitvijo programa VLC. Prosimo " +"sporočite napako vašemu distributerju." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Privzeto" \ No newline at end of file diff --git a/po/sv/phonon_vlc_qt.po b/po/sv/phonon_vlc_qt.po new file mode 100644 index 0000000..a24d948 --- /dev/null +++ b/po/sv/phonon_vlc_qt.po @@ -0,0 +1,42 @@ +# Copyright (C) YEAR This_file_is_part_of_KDE +# This file is distributed under the same license as the PACKAGE package. +# +# Stefan Asserhäll , 2017, 2019. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: https://bugs.kde.org\n" +"POT-Creation-Date: 2019-07-01 01:50+0200\n" +"PO-Revision-Date: 2019-07-04 17:31+0100\n" +"Last-Translator: Stefan Asserhäll \n" +"Language-Team: Swedish \n" +"Language: sv\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Lokalize 2.0\n" +"X-Qt-Contexts: true\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "Initiering av libVLC misslyckades" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Phonons VLC-gränssnitt misslyckades starta.\n" +"\n" +"Det betyder oftast ett problem med installationen av VLC. Rapportera gärna " +"felet till din distributör." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Förval" \ No newline at end of file diff --git a/po/uk/phonon_vlc_qt.po b/po/uk/phonon_vlc_qt.po new file mode 100644 index 0000000..0613e4b --- /dev/null +++ b/po/uk/phonon_vlc_qt.po @@ -0,0 +1,45 @@ +# Translation of phonon_vlc_qt.po to Ukrainian +# Copyright (C) 2011-2019 This_file_is_part_of_KDE +# This file is distributed under the license LGPL version 2.1 or +# version 3 or later versions approved by the membership of KDE e.V. +# +# Yuri Chornoivan , 2011, 2012, 2013, 2019. +msgid "" +msgstr "" +"Project-Id-Version: phonon_vlc_qt\n" +"Report-Msgid-Bugs-To: https://bugs.kde.org\n" +"POT-Creation-Date: 2019-07-01 01:50+0200\n" +"PO-Revision-Date: 2019-07-04 19:57+0300\n" +"Last-Translator: Yuri Chornoivan \n" +"Language-Team: Ukrainian \n" +"Language: uk\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=n==1 ? 3 : n%10==1 && n%100!=11 ? 0 : n" +"%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Generator: Lokalize 19.07.70\n" +"X-Qt-Contexts: true\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "Не вдалося ініціалізувати LibVLC" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Не вдалося запустити модуль VLC Phonon.\n" +"\n" +"Зазвичай, такі проблеми пов’язано з працездатністю встановленого вами VLC, " +"будь ласка, повідомте про цю ваду розробникам вашого дистрибутива." + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "Типовий" \ No newline at end of file diff --git a/po/zh_CN/phonon_vlc_qt.po b/po/zh_CN/phonon_vlc_qt.po new file mode 100644 index 0000000..93b988d --- /dev/null +++ b/po/zh_CN/phonon_vlc_qt.po @@ -0,0 +1,38 @@ +msgid "" +msgstr "" +"Project-Id-Version: kdeorg\n" +"PO-Revision-Date: 2021-03-14 15:44\n" +"Language-Team: Chinese Simplified\n" +"Language: zh_CN\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Qt-Contexts: true\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Crowdin-Project: kdeorg\n" +"X-Crowdin-Project-ID: 269464\n" +"X-Crowdin-Language: zh-CN\n" +"X-Crowdin-File: /kf5-stable/messages/phonon-vlc/phonon_vlc_qt.pot\n" +"X-Crowdin-File-ID: 24358\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "LibVLC 初始化失败" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"无法启动 Phonon 的 VLC 后端。\n" +"\n" +"这通常表示 VLC 安装有问题,请向软件包发行者报告问题。" + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "默认" \ No newline at end of file diff --git a/po/zh_TW/phonon_vlc_qt.po b/po/zh_TW/phonon_vlc_qt.po new file mode 100644 index 0000000..b3b951e --- /dev/null +++ b/po/zh_TW/phonon_vlc_qt.po @@ -0,0 +1,36 @@ +# pan93412 , 2019. +msgid "" +msgstr "" +"Project-Id-Version: pan 93412\n" +"PO-Revision-Date: 2019-10-19 15:18+0800\n" +"Last-Translator: pan93412 \n" +"Language-Team: Chinese \n" +"Language: zh_TW\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Qt-Contexts: true\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Lokalize 19.08.2\n" + +#: src/backend.cpp:132 +msgctxt "Phonon::VLC::Backend|" +msgid "LibVLC Failed to Initialize" +msgstr "LibVLC 初始化失敗" + +#: src/backend.cpp:133 +msgctxt "Phonon::VLC::Backend|" +msgid "" +"Phonon's VLC backend failed to start.\n" +"\n" +"This usually means a problem with your VLC installation, please report a bug " +"with your distributor." +msgstr "" +"Phonon 的 VLC 後端介面啟動失敗。\n" +"\n" +"通常這表示您的 VLC 安裝有問題。請向您的套件維護者回報。" + +#: src/devicemanager.cpp:214 +msgctxt "Phonon::VLC::DeviceManager|" +msgid "Default" +msgstr "預設" \ No newline at end of file diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..773218f --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,4 @@ +.svn +Makefile +moc_* +phonon_vlc_* diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..f0b292d --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,95 @@ +if (MSVC OR (WIN32 AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")) + set (CMAKE_MODULE_PATH ${CMAKE_INSTALL_PREFIX}/share/apps/cmake/modules) + find_package(KDEWin) + if (KDEWIN_FOUND) + include_directories(${KDEWIN_INCLUDES}/msvc) + link_libraries(${KDEWIN_LIBRARIES}) + else (KDEWIN_FOUND) + include(CheckIncludeFileCXX) + check_include_file_cxx(inttypes.h HAVE_INTTYPES) + check_include_file_cxx(stdint.h HAVE_STDINT) + if ( NOT HAVE_STDINT AND NOT HAVE_INTTYPES ) + message (FATAL_ERROR "You don't have stdint.h and inttypes.h\n\t get them from http://code.google.com/p/baseutils/source/browse/#svn/trunk/msvc,\n\t or get kdewin http://websvn.kde.org/trunk/kdesupport/kdewin/") + endif ( NOT HAVE_STDINT AND NOT HAVE_INTTYPES ) + endif (KDEWIN_FOUND) +endif (MSVC OR (WIN32 AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")) + +# NB: This is evaluated in the build system because the phonon headers +# change behavior based on the defines, meaning the defines must already be set +# by the time the headers are pre-processed! +if(${PHONON_VERSION} VERSION_GREATER "4.9.50") + message(STATUS "Building against Phonon 4.10 API") + set(BACKEND_VERSION_DEFINE -DPHONON_BACKEND_VERSION_4_10) +# Older not supported because backend doesn't build with older cmake rigging anwyay. +endif() +add_definitions(${BACKEND_VERSION_DEFINE}) # also automatically used for moc + +set(phonon_vlc_SRCS + audio/audiooutput.cpp + audio/audiodataoutput.cpp + audio/volumefadereffect.cpp + backend.cpp + devicemanager.cpp + effect.cpp + effectmanager.cpp + media.cpp + mediacontroller.cpp + mediaobject.cpp + mediaplayer.cpp + sinknode.cpp + streamreader.cpp +# video/videodataoutput.cpp + video/videowidget.cpp + video/videomemorystream.cpp + utils/debug.cpp + utils/libvlc.cpp +) + +if(NOT ${LIBVLC_VERSION} VERSION_LESS "2.2.0") + list(APPEND phonon_vlc_SRCS equalizereffect.cpp) +endif() + +if(PHONON_EXPERIMENTAL) + list(APPEND phonon_vlc_SRCS video/videodataoutput.cpp) +endif() + +if(APPLE) + list(APPEND phonon_vlc_SRCS + video/mac/nsvideoview.mm + video/mac/vlcmacwidget.mm) +endif() + +ecm_create_qm_loader(phonon_vlc_SRCS phonon_vlc_qt) +add_library(phonon_vlc MODULE ${phonon_vlc_SRCS}) + +target_include_directories(phonon_vlc + PRIVATE + # Extra include. We use some internal stuff for easy chroma conversion. + ${LIBVLC_INCLUDE_DIR}/vlc/plugins +) + +target_link_libraries(phonon_vlc + Phonon::phonon4qt5 + Qt5::Core + Qt5::Widgets + LibVLC::Core + LibVLC::LibVLC +) +if(PHONON_EXPERIMENTAL) + target_link_libraries(phonon_vlc Phonon::phonon4qt5experimental) +endif() + +install(TARGETS phonon_vlc DESTINATION ${PHONON_BACKEND_DIR}) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/config.h @ONLY) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/utils/mime.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/utils/mime.h @ONLY) + +# Instead of desktop files we are embedding the information into the plugin itself. +# We have no KDE technology to help with finding the actual libraries anyway, so +# we need to have the library path anyway. +# Also see qtplugin/Q_PLUGIN_METADATA documentation. +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/phonon-vlc.json.in + ${CMAKE_CURRENT_BINARY_DIR}/phonon-vlc.json @ONLY) diff --git a/src/audio/audiodataoutput.cpp b/src/audio/audiodataoutput.cpp new file mode 100644 index 0000000..39ba7ac --- /dev/null +++ b/src/audio/audiodataoutput.cpp @@ -0,0 +1,164 @@ +/* + Copyright (C) 2006 Matthias Kretz + Copyright (C) 2009 Martin Sandsmark + Copyright (C) 2010 Ben Cooksley + Copyright (C) 2011 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) version 3, or any + later version accepted by the membership of KDE e.V. (or its + successor approved by the membership of KDE e.V.), Nokia Corporation + (or its successors, if any) and the KDE Free Qt Foundation, which shall + act as a proxy defined in Section 6 of version 3 of the license. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "audiodataoutput.h" + +#include "media.h" + +namespace Phonon { +namespace VLC { + +AudioDataOutput::AudioDataOutput(QObject *parent) + : QObject(parent) +{ + m_sampleRate = 44100; + connect(this, SIGNAL(sampleReadDone()), this, SLOT(sendData())); + + // Register channels + m_channels.append(Phonon::AudioDataOutput::LeftChannel); + m_channels.append(Phonon::AudioDataOutput::RightChannel); + m_channels.append(Phonon::AudioDataOutput::CenterChannel); + m_channels.append(Phonon::AudioDataOutput::LeftSurroundChannel); + m_channels.append(Phonon::AudioDataOutput::RightSurroundChannel); + m_channels.append(Phonon::AudioDataOutput::SubwooferChannel); +} + +AudioDataOutput::~AudioDataOutput() +{ +} + +int AudioDataOutput::dataSize() const +{ + return m_dataSize; +} + +int AudioDataOutput::sampleRate() const +{ + return m_sampleRate; +} + +void AudioDataOutput::setDataSize(int size) +{ + m_dataSize = size; +} + +void AudioDataOutput::handleAddToMedia(Media *media) +{ + media->addOption(QString(":sout=#duplicate{dst=display,dst='transcode{vcodec=none,acodec=s16l,samplerate=%1}" + ":smem{audio-prerender-callback=%2," + "audio-postrender-callback=%3," + "audio-data=%4," + "time-sync=true}'}" + ).arg(QString::number(m_sampleRate), + QString::number((long long int) INTPTR_FUNC(AudioDataOutput::lock)), + QString::number((long long int) INTPTR_FUNC(AudioDataOutput::unlock)), + QString::number((long long int) INTPTR_PTR(this)))); +} + +void AudioDataOutput::lock(AudioDataOutput *cw, quint8 **pcm_buffer , quint32 size) +{ + cw->m_locker.lock(); + *pcm_buffer = new quint8[size]; +} + +void AudioDataOutput::unlock(AudioDataOutput *cw, quint8 *pcm_buffer, + quint32 channelCount, quint32 rate, + quint32 sampleCount, quint32 bits_per_sample, + quint32 size, qint64 pts) +{ + Q_UNUSED(size); + Q_UNUSED(pts); + + // (bytesPerChannelPerSample * channels * read_samples) + (bytesPerChannelPerSample * read_channels) + int bytesPerChannelPerSample = bits_per_sample / 8; + cw->m_sampleRate = rate; + cw->m_channelCount = channelCount; + + for (quint32 readSamples = 0; readSamples < sampleCount; ++readSamples) { + // Prepare a sample buffer, and initialise it + quint16 sampleBuffer[6]; + for (int initialised = 0; initialised < 6; ++initialised) { + sampleBuffer[initialised] = 0; + } + + int bufferPosition = (bytesPerChannelPerSample * channelCount * readSamples); + + for (quint32 readChannels = 0; readChannels < channelCount; ++readChannels) { + quint32 complet = 0; + for (int readBytes = 0; readBytes < bytesPerChannelPerSample; ++readBytes) { + // Read from the pcm_buffer into the per channel internal buffer + + quint32 complet_temp = 0; + complet_temp = pcm_buffer[bufferPosition]; + complet_temp <<= (8 * readBytes); + + complet += complet_temp; + ++bufferPosition; + } + + sampleBuffer[readChannels] = complet; + } + + if (channelCount == 1) { + cw->m_channelSamples[1].append(qint16(sampleBuffer[0])); + } + + for (quint32 readChannels = 0; readChannels < channelCount; ++readChannels) { + cw->m_channelSamples[readChannels].append(qint16(sampleBuffer[readChannels])); + } + // Finished reading one sample + } + + delete pcm_buffer; + + cw->m_locker.unlock(); + emit cw->sampleReadDone(); +} + +void AudioDataOutput::sendData() +{ + m_locker.lock(); + + int chan_count = m_channelCount; + if (m_channelCount == 1) { + chan_count = 2; + } + + while (m_channelSamples[0].count() > m_dataSize) { + QMap > m_data; + for (int position = 0; position < chan_count; position++) { + Phonon::AudioDataOutput::Channel chan = m_channels.value(position); + QVector data = m_channelSamples[position].mid(0, m_dataSize); + m_channelSamples[position].remove(0, data.count()); + m_data.insert(chan, data); + } + emit dataReady(m_data); + } + m_locker.unlock(); +} + +} // namespace VLC +} // namespace Phonon + +#include "moc_audiodataoutput.cpp" diff --git a/src/audio/audiodataoutput.h b/src/audio/audiodataoutput.h new file mode 100644 index 0000000..9c414e0 --- /dev/null +++ b/src/audio/audiodataoutput.h @@ -0,0 +1,162 @@ +/* + Copyright (C) 2006 Matthias Kretz + Copyright (C) 2009 Martin Sandsmark + Copyright (C) 2010 Ben Cooksley + Copyright (C) 2011 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) version 3, or any + later version accepted by the membership of KDE e.V. (or its + successor approved by the membership of KDE e.V.), Nokia Corporation + (or its successors, if any) and the KDE Free Qt Foundation, which shall + act as a proxy defined in Section 6 of version 3 of the license. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef Phonon_VLC_AUDIODATAOUTPUT_H +#define Phonon_VLC_AUDIODATAOUTPUT_H + +#include +#include + +#include +#include + +#include "sinknode.h" + +namespace Phonon { +namespace VLC { + +/** \brief Implementation for AudioDataOutput using libVLC + * + * This class makes the capture of raw audio data possible. It sets special options + * for the libVLC Media Object when connecting to it, and then captures libVLC events + * to get the audio data and send it further with the dataReady() signal. + * + * As a sink node, it can be connected to media objects. + * + * The frontend Phonon::AudioDataOutput object is unused. + * + * See the Phonon documentation for details. + * + * \see AudioOutput + * \see SinkNode + * + * \author Martin Sandsmark + */ +class AudioDataOutput : public QObject, public SinkNode, public AudioDataOutputInterface +{ + Q_OBJECT + Q_INTERFACES(Phonon::AudioDataOutputInterface) +public: + /** + * Creates an audio data output. The sample rate is set to 44100 Hz. + * The available audio channels are registered. These are: + * \li Left \li Right \li Center \li LeftSurround \li RightSurround \li Subwoofer + */ + explicit AudioDataOutput(QObject *parent); + ~AudioDataOutput(); + + Phonon::AudioDataOutput *frontendObject() const + { + return m_frontend; + } + + void setFrontendObject(Phonon::AudioDataOutput *frontend) + { + m_frontend = frontend; + } + +public Q_SLOTS: + /** + * \return The currently used number of samples passed through the signal. + */ + int dataSize() const; + + /** + * \return The current sample rate in Hz. + */ + int sampleRate() const; + + /** + * Sets the number of samples to be passed in one signal emission. + */ + void setDataSize(int size); + + /** + * Adds special options to the libVLC Media Object to adapt it to give audio data + * directly. There are two callbacks used: lock(), unlock(). + * + * \see lock() + * \see unlock() + * \reimp + */ + void handleAddToMedia(Media *media); + +signals: + void dataReady(const QMap > &data); + void dataReady(const QMap > &data); + void endOfMedia(int remainingSamples); + void sampleReadDone(); + +private Q_SLOTS: + /** + * Looks at the channel samples generated in lock() and creates the QMap required for + * the dataReady() signal. Then the signal is emitted. This repeats as long as there is + * data remaining. + * + * \see lock() + */ + void sendData(); + +private: + /** + * This is a VLC prerender callback. The m_locker mutex is locked, and a new buffer is prepared + * for the incoming audio data. + * + * \param cw The AudioDataOutput for this callback + * \param pcm_buffer The new data buffer + * \param size Size for the incoming data + * + * \see unlock() + */ + static void lock(AudioDataOutput *cw, quint8 **pcm_buffer , quint32 size); + + /** + * This is a VLC postrender callback. Interprets the data received in m_buffer, + * separating the samples and channels. Finally, the buffer is freed and m_locker + * is unlocked. Now the audio data output is ready for sending data. + * + * \param cw The AudioDataOutput for this callback + * + * \see lock() + * \see sendData() + */ + static void unlock(AudioDataOutput *cw, quint8 *pcm_buffer, + quint32 channelCount, quint32 rate, + quint32 sampleCount, quint32 bits_per_sample, + quint32 size, qint64 pts); + + int m_dataSize; + int m_sampleRate; + Phonon::AudioDataOutput *m_frontend; + + QMutex m_locker; + int m_channelCount; + QVector m_channelSamples[6]; + QList m_channels; +}; + +} // namespace VLC +} // namespace Phonon + +#endif // Phonon_VLC_AUDIODATAOUTPUT_H diff --git a/src/audio/audiooutput.cpp b/src/audio/audiooutput.cpp new file mode 100644 index 0000000..e81cd87 --- /dev/null +++ b/src/audio/audiooutput.cpp @@ -0,0 +1,250 @@ +/* + Copyright (C) 2007-2008 Tanguy Krotoff + Copyright (C) 2008 Lukas Durfina + Copyright (C) 2009 Fathi Boudra + Copyright (C) 2013-2018 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "audiooutput.h" + +#include + +#include + +#include "backend.h" +#include "utils/debug.h" +#include "devicemanager.h" +#include "mediaobject.h" +#include "media.h" + +namespace Phonon { +namespace VLC { + +AudioOutput::AudioOutput(QObject *parent) + : QObject(parent) + , m_volume(0.75) + , m_explicitVolume(false) + , m_muted(false) + , m_category(Phonon::NoCategory) +{ +} + +AudioOutput::~AudioOutput() +{ +} + +#if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(3, 0, 0, 0)) +static libvlc_media_player_role categoryToRole(Category category) +{ + switch(category) { + case NoCategory: + return libvlc_role_None; + case NotificationCategory: + return libvlc_role_Notification; + case MusicCategory: + return libvlc_role_Music; + case VideoCategory: + return libvlc_role_Video; + case CommunicationCategory: + return libvlc_role_Communication; + case GameCategory: + return libvlc_role_Game; + case AccessibilityCategory: + return libvlc_role_Accessibility; + } + return libvlc_role_None; +} +#endif + +void AudioOutput::handleConnectToMediaObject(MediaObject *mediaObject) +{ + Q_UNUSED(mediaObject); + setOutputDeviceImplementation(); + if (!PulseSupport::getInstance()->isActive()) { + // Rely on libvlc for updates if PASupport is not active + connect(m_player, SIGNAL(mutedChanged(bool)), + this, SLOT(onMutedChanged(bool))); + connect(m_player, SIGNAL(volumeChanged(float)), + this, SLOT(onVolumeChanged(float))); + applyVolume(); + } +#if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(3, 0, 0, 0)) + libvlc_media_player_set_role(*m_player, categoryToRole(m_category)); +#endif +} + +void AudioOutput::handleAddToMedia(Media *media) +{ + media->addOption(":audio"); + PulseSupport *pulse = PulseSupport::getInstance(); + if (pulse && pulse->isActive()) { + pulse->setupStreamEnvironment(m_streamUuid); + } +} + +qreal AudioOutput::volume() const +{ + return m_volume; +} + +void AudioOutput::setVolume(qreal volume) +{ + if (m_player) { + debug() << "async setting of volume to" << volume; + m_volume = volume; + m_explicitVolume = true; + applyVolume(); + +#if (LIBVLC_VERSION_INT < LIBVLC_VERSION(2, 2, 2, 0)) + emit volumeChanged(m_volume); +#endif + } +} + +void AudioOutput::setMuted(bool mute) +{ + if (mute == m_player->mute()) { + // Make sure we actually have propagated the mutness into the frontend. + onMutedChanged(mute); + return; + } + m_player->setMute(mute); +} + +void AudioOutput::setCategory(Category category) +{ + m_category = category; +} + +int AudioOutput::outputDevice() const +{ + return m_device.index(); +} + +bool AudioOutput::setOutputDevice(int deviceIndex) +{ + const AudioOutputDevice device = AudioOutputDevice::fromIndex(deviceIndex); + if (!device.isValid()) { + error() << Q_FUNC_INFO << "Unable to find the output device with index" << deviceIndex; + return false; + } + return setOutputDevice(device); +} + +bool AudioOutput::setOutputDevice(const AudioOutputDevice &newDevice) +{ + debug() << Q_FUNC_INFO; + + if (!newDevice.isValid()) { + error() << "Invalid audio output device"; + return false; + } + + if (newDevice == m_device) + return true; + + m_device = newDevice; + if (m_player) { + setOutputDeviceImplementation(); + } + + return true; +} + +void AudioOutput::setStreamUuid(QString uuid) +{ + DEBUG_BLOCK; + debug() << uuid; + m_streamUuid = uuid; +} + +void AudioOutput::setOutputDeviceImplementation() +{ + Q_ASSERT(m_player); +#if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(2, 2, 0, 0)) + // VLC 2.2 has the PulseSupport overrides always disabled because of + // incompatibility. Also see backend.cpp for more detals. + // To get access to the correct activity state we need to temporarily + // enable pulse and then disable it again. This is necessary because isActive + // is in fact isActive&isEnabled............. + PulseSupport::getInstance()->enable(true); + const bool pulseActive = PulseSupport::getInstance()->isActive(); + PulseSupport::getInstance()->enable(false); +#else + const bool pulseActive = PulseSupport::getInstance()->isActive(); +#endif + if (pulseActive) { + m_player->setAudioOutput("pulse"); + debug() << "Setting aout to pulse"; + return; + } + + const QVariant dalProperty = m_device.property("deviceAccessList"); + if (!dalProperty.isValid()) { + error() << "Device" << m_device.property("name") << "has no access list"; + return; + } + const DeviceAccessList deviceAccessList = dalProperty.value(); + if (deviceAccessList.isEmpty()) { + error() << "Device" << m_device.property("name") << "has an empty access list"; + return; + } + + // ### we're not trying the whole access list (could mean same device on different soundsystems) + const DeviceAccess &firstDeviceAccess = deviceAccessList.first(); + + QByteArray soundSystem = firstDeviceAccess.first; + debug() << "Setting output soundsystem to" << soundSystem; + m_player->setAudioOutput(soundSystem); + + QByteArray deviceName = firstDeviceAccess.second.toLatin1(); + if (!deviceName.isEmpty()) { + // print the name as possibly messed up by toLatin1() to see conversion problems + debug() << "Setting output device to" << deviceName << '(' << m_device.property("name") << ')'; + m_player->setAudioOutputDevice(soundSystem, deviceName); + } +} + +void AudioOutput::applyVolume() +{ + if (m_player && m_explicitVolume) { + const int preVolume = m_player->audioVolume(); + const int newVolume = m_volume * 100; + m_player->setAudioVolume(newVolume); + +#if (LIBVLC_VERSION_INT < LIBVLC_VERSION(2, 2, 2, 0)) + onMutedChanged(m_volume == 0.0); + onVolumeChanged(newVolume); +#endif + + debug() << "Volume changed from" << preVolume << "to" << newVolume; + } +} + +void AudioOutput::onMutedChanged(bool mute) +{ + m_muted = mute; + emit mutedChanged(mute); +} + +void AudioOutput::onVolumeChanged(float volume) +{ + m_volume = volume; + emit volumeChanged(volume); +} + +} +} // Namespace Phonon::VLC diff --git a/src/audio/audiooutput.h b/src/audio/audiooutput.h new file mode 100644 index 0000000..7165495 --- /dev/null +++ b/src/audio/audiooutput.h @@ -0,0 +1,136 @@ +/* + Copyright (C) 2007-2008 Tanguy Krotoff + Copyright (C) 2008 Lukas Durfina + Copyright (C) 2009 Fathi Boudra + Copyright (C) 2013 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef PHONON_VLC_AUDIOOUTPUT_H +#define PHONON_VLC_AUDIOOUTPUT_H + +#include + +#include + +#include "sinknode.h" + +namespace Phonon { +namespace VLC { + +/** \brief AudioOutput implementation for Phonon-VLC + * + * This class is a SinkNode that implements the AudioOutputInterface from Phonon. It + * supports setting the volume and the audio output device. + * + * There are signals for the change of the volume or for when an audio device failed. + * + * See the Phonon::AudioOutputInterface documentation for details. + * + * \see AudioDataOutput + */ +class AudioOutput : public QObject, public SinkNode, public AudioOutputInterface +{ + Q_OBJECT + Q_INTERFACES(Phonon::AudioOutputInterface) + +public: + /** + * Creates an AudioOutput with the given backend object. The volume is set to 1.0 + * + * \param p_back Parent backend + * \param p_parent A parent object + */ + explicit AudioOutput(QObject *parent); + ~AudioOutput(); + + /** \reimp */ + void handleConnectToMediaObject(MediaObject *mediaObject); + /** \reimp */ + void handleAddToMedia(Media *media); + + /** + * \return The current volume for this audio output. + */ + qreal volume() const; + + /** + * Sets the volume of the audio output. See the Phonon::AudioOutputInterface::setVolume() documentation + * for details. + */ + void setVolume(qreal volume); + + /** + * \return The index of the current audio output device from the list obtained from the backend object. + */ + int outputDevice() const; + + /** + * Sets the current output device for this audio output. The validity of the device index + * is verified before attempting to change the device. + * + * \param device The index of the device, obtained from the backend's audio device list + * \return \c true if succeeded, or no change was made + * \return \c false if failed + */ + bool setOutputDevice(int); + + /** + * Sets the current output device for this audio output. + * + * \param device The device to set; it should be valid and contain an usable deviceAccessList property + * \return \c true if succeeded, or no change was made + * \return \c false if failed + */ + bool setOutputDevice(const AudioOutputDevice &newDevice); + void setStreamUuid(QString uuid); + void setMuted(bool mute); + + virtual void setCategory(Phonon::Category category); + +signals: + void volumeChanged(qreal volume); + void audioDeviceFailed(); + void mutedChanged(bool mute); + +private slots: + /** + * Sets the volume to m_volume. + */ + void applyVolume(); + + void onMutedChanged(bool mute); + void onVolumeChanged(float volume); + +private: + /** + * We can only really set the output device once we have a libvlc_media_player, which comes + * from our SinkNode. + */ + void setOutputDeviceImplementation(); + + qreal m_volume; + // Set after first setVolume to indicate volume was set manually. + bool m_explicitVolume; + bool m_muted; + AudioOutputDevice m_device; + QString m_streamUuid; + Category m_category; +}; + +} // namespace VLC +} // namespace Phonon + +#endif // PHONON_VLC_AUDIOOUTPUT_H diff --git a/src/audio/volumefadereffect.cpp b/src/audio/volumefadereffect.cpp new file mode 100644 index 0000000..9eafd49 --- /dev/null +++ b/src/audio/volumefadereffect.cpp @@ -0,0 +1,127 @@ +/* This file is part of the KDE project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2013 Martin Sandsmark + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 or 3 of the License. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#include "volumefadereffect.h" +#include + +#include "utils/debug.h" + +#include + +#ifndef QT_NO_PHONON_VOLUMEFADEREFFECT +namespace Phonon +{ +namespace VLC +{ +VolumeFaderEffect::VolumeFaderEffect(QObject *parent) + : QObject(parent) + , SinkNode() + , m_fadeCurve(Phonon::VolumeFaderEffect::Fade3Decibel) + , m_fadeFromVolume(0) + , m_fadeToVolume(0) +{ + m_fadeTimeline = new QTimeLine(1000, this); + connect(m_fadeTimeline, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetVolume(qreal))); +} + +VolumeFaderEffect::~VolumeFaderEffect() +{ +} + +float VolumeFaderEffect::volume() const +{ + Q_ASSERT(m_player); + return m_player->audioVolume() / 100.0f; +} + +void VolumeFaderEffect::slotSetVolume(qreal volume) +{ + float gstVolume = m_fadeFromVolume + (volume * (m_fadeToVolume - m_fadeFromVolume)); + setVolumeInternal(gstVolume); +} + +Phonon::VolumeFaderEffect::FadeCurve VolumeFaderEffect::fadeCurve() const +{ + return m_fadeCurve; +} + +void VolumeFaderEffect::setFadeCurve(Phonon::VolumeFaderEffect::FadeCurve pFadeCurve) +{ + m_fadeCurve = pFadeCurve; + QEasingCurve fadeCurve; + switch(pFadeCurve) { + case Phonon::VolumeFaderEffect::Fade3Decibel: + fadeCurve = QEasingCurve::InQuad; + break; + case Phonon::VolumeFaderEffect::Fade6Decibel: + fadeCurve = QEasingCurve::Linear; + break; + case Phonon::VolumeFaderEffect::Fade9Decibel: + fadeCurve = QEasingCurve::OutCubic; + break; + case Phonon::VolumeFaderEffect::Fade12Decibel: + fadeCurve = QEasingCurve::OutQuart; + break; + } + m_fadeTimeline->setEasingCurve(fadeCurve); +} + +void VolumeFaderEffect::fadeTo(float targetVolume, int fadeTime) +{ + Q_ASSERT(m_player); + abortFade(); + m_fadeToVolume = targetVolume; + m_fadeFromVolume = m_player->audioVolume() / 100.0f; + + // Don't call QTimeLine::setDuration() with zero. + // It is not supported and breaks fading. + if (fadeTime <= 0) { + debug() << "Called with retarded fade time " << fadeTime; + setVolumeInternal(targetVolume); + return; + } + + m_fadeTimeline->setDuration(fadeTime); + m_fadeTimeline->start(); +} + +void VolumeFaderEffect::setVolume(float v) +{ + abortFade(); + setVolumeInternal(v); +} + +void VolumeFaderEffect::abortFade() +{ + m_fadeTimeline->stop(); +} + +void VolumeFaderEffect::setVolumeInternal(float v) +{ + if (m_player) + m_player->setAudioFade(v); + else + warning() << Q_FUNC_INFO << this << "no m_player set"; +} + +} +} //namespace Phonon::VLC + +#endif //QT_NO_PHONON_VOLUMEFADEREFFECT +#include "moc_volumefadereffect.cpp" + diff --git a/src/audio/volumefadereffect.h b/src/audio/volumefadereffect.h new file mode 100644 index 0000000..fdf16ed --- /dev/null +++ b/src/audio/volumefadereffect.h @@ -0,0 +1,71 @@ +/* This file is part of the KDE project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 2013 Martin Sandsmark + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 or 3 of the License. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#ifndef PHONON_VLC_VOLUMEFADEREFFECT_H +#define PHONON_VLC_VOLUMEFADEREFFECT_H + +#include + +#include +#include + +#include "sinknode.h" + +class QTimeLine; + +namespace Phonon { + +class MediaObject; + +namespace VLC { + +class VolumeFaderEffect : public QObject, public SinkNode, public VolumeFaderInterface +{ + Q_OBJECT + Q_INTERFACES(Phonon::VolumeFaderInterface) + +public: + explicit VolumeFaderEffect(QObject *parent = 0); + ~VolumeFaderEffect(); + + // VolumeFaderInterface: + float volume() const; + Phonon::VolumeFaderEffect::FadeCurve fadeCurve() const; + void setFadeCurve(Phonon::VolumeFaderEffect::FadeCurve fadeCurve); + void fadeTo(float volume, int fadeTime); + void setVolume(float v); + QPointer mediaObject() { return m_mediaObject; } + +private slots: + void slotSetVolume(qreal v); + +private: + void abortFade(); + inline void setVolumeInternal(float v); + + Phonon::VolumeFaderEffect::FadeCurve m_fadeCurve; + float m_fadeFromVolume; + float m_fadeToVolume; + QTimeLine *m_fadeTimeline; + +}; + +} // namespace VLC +} // namespace Phonon + +#endif // PHONON_VLC_VOLUMEFADEREFFECT_H diff --git a/src/backend.cpp b/src/backend.cpp new file mode 100644 index 0000000..4a82438 --- /dev/null +++ b/src/backend.cpp @@ -0,0 +1,365 @@ +/* + Copyright (C) 2007-2008 Tanguy Krotoff + Copyright (C) 2008 Lukas Durfina + Copyright (C) 2009 Fathi Boudra + Copyright (C) 2009-2011 vlc-phonon AUTHORS + Copyright (C) 2011-2013 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "backend.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "audio/audiooutput.h" +#include "audio/audiodataoutput.h" +#include "audio/volumefadereffect.h" +#include "config.h" +#include "devicemanager.h" +#include "effect.h" +#include "effectmanager.h" +#include "mediaobject.h" +#include "sinknode.h" +#include "utils/debug.h" +#include "utils/libvlc.h" +#include "utils/mime.h" +#ifdef PHONON_EXPERIMENTAL +#include "video/videodataoutput.h" +#endif +#include "video/videowidget.h" + +namespace Phonon +{ +namespace VLC +{ + +Backend *Backend::self; + +Backend::Backend(QObject *parent, const QVariantList &) + : QObject(parent) + , m_deviceManager(0) + , m_effectManager(0) +{ + self = this; + + // Backend information properties + setProperty("identifier", QLatin1String("phonon_vlc")); + setProperty("backendName", QLatin1String("VLC")); + setProperty("backendComment", QLatin1String("VLC backend for Phonon")); + setProperty("backendVersion", QLatin1String(PHONON_VLC_VERSION)); + setProperty("backendIcon", QLatin1String("vlc")); + setProperty("backendWebsite", QLatin1String("https://commits.kde.org/phonon-vlc")); + + // Check if we should enable debug output + int debugLevel = qgetenv("PHONON_BACKEND_DEBUG").toInt(); + if (debugLevel > 3) // 3 is maximum + debugLevel = 3; + Debug::setMinimumDebugLevel((Debug::DebugLevel)((int) Debug::DEBUG_NONE - 1 - debugLevel)); + + debug() << "Constructing Phonon-VLC Version" << PHONON_VLC_VERSION; + + // Actual libVLC initialisation + if (LibVLC::init()) { + debug() << "Using VLC version" << libvlc_get_version(); + if (!qApp->applicationName().isEmpty()) { + QString userAgent = + QString("%0/%1 (Phonon/%2; Phonon-VLC/%3)").arg( + qApp->applicationName(), + qApp->applicationVersion(), + PHONON_VERSION_STR, + PHONON_VLC_VERSION); + libvlc_set_user_agent(pvlc_libvlc, + qApp->applicationName().toUtf8().constData(), + userAgent.toUtf8().constData()); + } else { + qWarning("WARNING: Setting the user agent for streaming and" + " PulseAudio requires you to set QCoreApplication::applicationName()"); + } + + PulseSupport::getInstance()->enable(true); + const bool pulseActive = PulseSupport::getInstance()->isActive(); + PulseSupport::getInstance()->enable(false); + if (!qApp->applicationName().isEmpty()) { + const QString id = QString("org.kde.phonon.%1").arg(qApp->applicationName()); + const QString version = qApp->applicationVersion(); + QString icon; + if (!qApp->windowIcon().isNull()){ + // Try to get the fromTheme() name of the QIcon. + icon = qApp->windowIcon().name(); + } + if (icon.isEmpty()) { + // If we failed to get a proper icon name, use the appname instead. + icon = qApp->applicationName().toLower(); + } + libvlc_set_app_id(pvlc_libvlc, + id.toUtf8().constData(), + version.toUtf8().constData(), + icon.toUtf8().constData()); + } else if (pulseActive) { + qWarning("WARNING: Setting PulseAudio context information requires you" + " to set QCoreApplication::applicationName()," + " QCoreApplication::applicationVersion() and" + " QGuiApplication::windowIcon()."); + } + } else { +#ifdef __GNUC__ + #warning TODO - this error message is as useful as a knife at a gun fight +#endif + QMessageBox msg; + msg.setIcon(QMessageBox::Critical); + msg.setWindowTitle(tr("LibVLC Failed to Initialize")); + msg.setText(tr("Phonon's VLC backend failed to start." + "\n\n" + "This usually means a problem with your VLC installation," + " please report a bug with your distributor.")); + msg.setDetailedText(LibVLC::errorMessage()); + msg.exec(); + fatal() << "Phonon::VLC::vlcInit: Failed to initialize VLC"; + } + +#if (LIBVLC_VERSION_INT < LIBVLC_VERSION(2, 2, 2, 0)) + // VLC 2.2 changed the stream creation order around internally which breaks + // the Pulseaudio hijacking. Since VLC upstream doesn't feel like giving us + // any more property control we now consider this feature unsupported. As + // such whatever properties VLC sets will be what pulse knows about us. + + // Initialise PulseAudio support + PulseSupport *pulse = PulseSupport::getInstance(); + pulse->enable(true); + connect(pulse, SIGNAL(objectDescriptionChanged(ObjectDescriptionType)), + SIGNAL(objectDescriptionChanged(ObjectDescriptionType))); +#endif + + m_deviceManager = new DeviceManager(this); + m_effectManager = new EffectManager(this); +} + +Backend::~Backend() +{ + if (LibVLC::self) + delete LibVLC::self; + if (GlobalAudioChannels::self) + delete GlobalAudioChannels::self; + if (GlobalSubtitles::self) + delete GlobalSubtitles::self; + PulseSupport::shutdown(); +} + +QObject *Backend::createObject(BackendInterface::Class c, QObject *parent, const QList &args) +{ + if (!LibVLC::self || !pvlc_libvlc) + return 0; + + switch (c) { + case MediaObjectClass: + return new MediaObject(parent); + case AudioOutputClass: + return new AudioOutput(parent); +#if (LIBVLC_VERSION_INT < LIBVLC_VERSION(2, 0, 0, 0)) + // Broken >= 2.0 + // https://trac.videolan.org/vlc/ticket/6992 + case AudioDataOutputClass: + return new AudioDataOutput(parent); +#endif +#ifdef PHONON_EXPERIMENTAL + case VideoDataOutputClass: + return new VideoDataOutput(parent); +#endif + case VideoGraphicsObjectClass: + return nullptr; // No longer supported + case EffectClass: + return effectManager()->createEffect(args[0].toInt(), parent); + case VideoWidgetClass: + return new VideoWidget(qobject_cast(parent)); +// case VolumeFaderEffectClass: +#ifdef __GNUC__ +#warning VFE crashes and has volume bugs ... deactivated +// return new VolumeFaderEffect(parent); +#endif + } + + warning() << "Backend class" << c << "is not supported by Phonon VLC :("; + return 0; +} + +QStringList Backend::availableMimeTypes() const +{ + if (m_supportedMimeTypes.isEmpty()) + const_cast(this)->m_supportedMimeTypes = mimeTypeList(); + return m_supportedMimeTypes; +} + +QList Backend::objectDescriptionIndexes(ObjectDescriptionType type) const +{ + QList list; + + switch (type) { + case Phonon::AudioChannelType: { + list << GlobalAudioChannels::instance()->globalIndexes(); + } + break; + case Phonon::AudioOutputDeviceType: + case Phonon::AudioCaptureDeviceType: + case Phonon::VideoCaptureDeviceType: { + return deviceManager()->deviceIds(type); + } + break; + case Phonon::EffectType: { + QList effectList = effectManager()->effects(); + for (int eff = 0; eff < effectList.size(); ++eff) { + list.append(eff); + } + } + break; + case Phonon::SubtitleType: { + list << GlobalSubtitles::instance()->globalIndexes(); + } + break; + } + + return list; +} + +QHash Backend::objectDescriptionProperties(ObjectDescriptionType type, int index) const +{ + QHash ret; + + switch (type) { + case Phonon::AudioChannelType: { + const AudioChannelDescription description = GlobalAudioChannels::instance()->fromIndex(index); + ret.insert("name", description.name()); + ret.insert("description", description.description()); + } + break; + case Phonon::AudioOutputDeviceType: + case Phonon::AudioCaptureDeviceType: + case Phonon::VideoCaptureDeviceType: { + // Index should be unique, even for different categories + return deviceManager()->deviceProperties(index); + } + break; + case Phonon::EffectType: { + const QList effectList = effectManager()->effects(); + if (index >= 0 && index <= effectList.size()) { + const EffectInfo &effect = effectList.at(index); + ret.insert("name", effect.name()); + ret.insert("description", effect.description()); + ret.insert("author", effect.author()); + } else { + Q_ASSERT(1); // Since we use list position as ID, this should not happen + } + } + break; + case Phonon::SubtitleType: { + const SubtitleDescription description = GlobalSubtitles::instance()->fromIndex(index); + ret.insert("name", description.name()); + ret.insert("description", description.description()); + ret.insert("type", description.property("type")); + } + break; + } + + return ret; +} + +bool Backend::startConnectionChange(QSet objects) +{ + //FIXME + foreach(QObject * object, objects) { + debug() << "Object:" << object->metaObject()->className(); + } + + // There is nothing we can do but hope the connection changes will not take too long + // so that buffers would underrun + // But we should be pretty safe the way xine works by not doing anything here. + return true; +} + +bool Backend::connectNodes(QObject *source, QObject *sink) +{ + debug() << "Backend connected" << source->metaObject()->className() << "to" << sink->metaObject()->className(); + + SinkNode *sinkNode = dynamic_cast(sink); + if (sinkNode) { + MediaObject *mediaObject = qobject_cast(source); + if (mediaObject) { + // Connect the SinkNode to a MediaObject + sinkNode->connectToMediaObject(mediaObject); + return true; + } + + VolumeFaderEffect *effect = qobject_cast(source); + if (effect) { + sinkNode->connectToMediaObject(effect->mediaObject()); + return true; + } + } + + warning() << "Linking" << source->metaObject()->className() << "to" << sink->metaObject()->className() << "failed"; + + return false; +} + +bool Backend::disconnectNodes(QObject *source, QObject *sink) +{ + SinkNode *sinkNode = dynamic_cast(sink); + if (sinkNode) { + MediaObject *const mediaObject = qobject_cast(source); + if (mediaObject) { + // Disconnect the SinkNode from a MediaObject + sinkNode->disconnectFromMediaObject(mediaObject); + return true; + } + + VolumeFaderEffect *const effect = qobject_cast(source); + if (effect) { + sinkNode->disconnectFromMediaObject(effect->mediaObject()); + return true; + } + } + + return false; +} + +bool Backend::endConnectionChange(QSet objects) +{ + foreach(QObject *object, objects) { + debug() << "Object:" << object->metaObject()->className(); + } + return true; +} + +DeviceManager *Backend::deviceManager() const +{ + return m_deviceManager; +} + +EffectManager *Backend::effectManager() const +{ + return m_effectManager; +} + +} // namespace VLC +} // namespace Phonon diff --git a/src/backend.h b/src/backend.h new file mode 100644 index 0000000..640b70a --- /dev/null +++ b/src/backend.h @@ -0,0 +1,158 @@ +/* + Copyright (C) 2007-2008 Tanguy Krotoff + Copyright (C) 2008 Lukas Durfina + Copyright (C) 2009 Fathi Boudra + Copyright (C) 2009-2011 vlc-phonon AUTHORS + Copyright (C) 2011 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef Phonon_VLC_BACKEND_H +#define Phonon_VLC_BACKEND_H + +#include + +#include +#include + +class LibVLC; + +namespace Phonon +{ +namespace VLC +{ +class DeviceManager; +class EffectManager; + +/** \brief Backend class for Phonon-VLC. + * + * This class provides the special objects created by the backend and information about + * various things that the backend supports. An object of this class is the root for + * the backend plugin. + * + * Phonon will request the backend to create objects of various classes, like MediaObject, + * AudioOutput, VideoWidget, Effect. There are also methods to handle the connections between + * these objects. + * + * This class also provides information about the devices and effects that the backend supports. + * These are audio output devices, audio capture devices, video capture devices, effects. + */ + +class Backend : public QObject, public BackendInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.kde.phonon.vlc" FILE "phonon-vlc.json") + Q_INTERFACES(Phonon::BackendInterface) + +public: + /** + * Instance. Since there is no backend instance without actual Backend object + * this class behaves likes a singleton. + */ + static Backend *self; + + /** + * Constructs the backend. Sets the backend properties, fetches the debug level from the + * environment, initializes libVLC, constructs the device and effect managers, initializes + * PulseAudio support. + * + * \param parent A parent object for the backend (passed to the QObject constructor) + */ + explicit Backend(QObject *parent = 0, const QVariantList & = QVariantList()); + virtual ~Backend(); + + /// \return The device manager that is associated with this backend object + DeviceManager *deviceManager() const; + + /// \return The effect manager that is associated with this backend object. + EffectManager *effectManager() const; + + /** + * Creates a backend object of the desired class and with the desired parent. Extra arguments can be provided. + * + * \param c The class of object that is to be created + * \param parent The object that will be the parent of the new object + * \param args Optional arguments for the object creation + * \return The desired object or NULL if the class is not implemented. + */ + QObject *createObject(BackendInterface::Class, QObject *parent, const QList &args); + + /// \returns a list of all available mimetypes (hardcoded) + QStringList availableMimeTypes() const; + + /** + * Returns a list of indexes for the desired object types. It specifies a list of objects + * of a particular category that the backend knows about. These indexes can be used with + * objectDescriptionProperties() to get the properties of a particular object. + * + * \param type The type of objects for the list + */ + QList objectDescriptionIndexes(ObjectDescriptionType type) const; + + /** + * Returns a list of properties for a particular object of the desired category. + * + * \param type The type of object for the index + * \param index The index for the object of the desired type + * \return The property list. If the object is inexistent, an empty list is returned. + */ + QHash objectDescriptionProperties(ObjectDescriptionType type, int index) const; + + /** + * Called when a connection between nodes is about to be changed + * + * \param objects A set of objects that will be involved in the change + */ + bool startConnectionChange(QSet); + + /** + * Connects two media nodes. The sink is informed that it should connect itself to the source. + * + * \param source The source media node for the connection + * \param sink The sink media node for the connection + * \return True if the connection was successful + */ + bool connectNodes(QObject *, QObject *); + + /** + * Disconnects two previously connected media nodes. It disconnects the sink node from the source node. + * + * \param source The source node for the disconnection + * \param sink The sink node for the disconnection + * \return True if the disconnection was successful + */ + bool disconnectNodes(QObject *, QObject *); + + /** + * Called after a connection between nodes has been changed + * + * \param objects Nodes involved in the disconnection + */ + bool endConnectionChange(QSet); + +Q_SIGNALS: + void objectDescriptionChanged(ObjectDescriptionType); + +private: + mutable QStringList m_supportedMimeTypes; + + DeviceManager *m_deviceManager; + EffectManager *m_effectManager; +}; + +} // namespace VLC +} // namespace Phonon + +#endif // Phonon_VLC_BACKEND_H diff --git a/src/config.h.cmake b/src/config.h.cmake new file mode 100644 index 0000000..9c1f78d --- /dev/null +++ b/src/config.h.cmake @@ -0,0 +1,27 @@ +/* + Copyright (C) 2019 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef PHONON_VLC_CONFIG_H +#define PHONON_VLC_CONFIG_H + +// I'd really love to have inline constexpr QStringViews here, I'm unsure what +// that requires vis-a-vis compiler support though :( + +#cmakedefine PHONON_VLC_VERSION "@PHONON_VLC_VERSION@" +#cmakedefine PHONON_EXPERIMENTAL + +#endif // PHONON_VLC_CONFIG_H diff --git a/src/devicemanager.cpp b/src/devicemanager.cpp new file mode 100644 index 0000000..37d6ee4 --- /dev/null +++ b/src/devicemanager.cpp @@ -0,0 +1,340 @@ +/* + Copyright (C) 2007-2008 Tanguy Krotoff + Copyright (C) 2008 Lukas Durfina + Copyright (C) 2009 Fathi Boudra + Copyright (C) 2009-2010 vlc-phonon AUTHORS + Copyright (C) 2011 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "devicemanager.h" + +#include + +#include + +#include "backend.h" +#include "utils/debug.h" +#include "utils/libvlc.h" +#include "utils/vstring.h" + +namespace Phonon +{ +namespace VLC +{ + +/* + * Device Info + */ + +DeviceInfo::DeviceInfo(const QString &name, bool isAdvanced) +{ + // Get an id + static int counter = 0; + m_id = counter++; + + // Get name and description for the device + m_name = name; + m_isAdvanced = isAdvanced; + m_capabilities = None; + + // A default device should never be advanced + if (name.startsWith(QLatin1String("default"), Qt::CaseInsensitive)) + m_isAdvanced = false; +} + +int DeviceInfo::id() const +{ + return m_id; +} + +const QString& DeviceInfo::name() const +{ + return m_name; +} + +const QString& DeviceInfo::description() const +{ + return m_description; +} + +bool DeviceInfo::isAdvanced() const +{ + return m_isAdvanced; +} + +void DeviceInfo::setAdvanced(bool advanced) +{ + m_isAdvanced = advanced; +} + +const DeviceAccessList& DeviceInfo::accessList() const +{ + return m_accessList; +} + +void DeviceInfo::addAccess(const DeviceAccess& access) +{ + if (m_accessList.isEmpty()) + m_description = access.first + ": " + access.second; + m_accessList.append(access); +} + +quint16 DeviceInfo::capabilities() const +{ + return m_capabilities; +} + +void DeviceInfo::setCapabilities(quint16 cap) +{ + m_capabilities = cap; +} + + +/* + * Device Manager + */ + +DeviceManager::DeviceManager(Backend *parent) + : QObject(parent) + , m_backend(parent) +{ + Q_ASSERT(parent); + updateDeviceList(); +} + +DeviceManager::~DeviceManager() +{ +} + +QList DeviceManager::deviceIds(ObjectDescriptionType type) +{ + DeviceInfo::Capability capability = DeviceInfo::None; + switch (type) { + case Phonon::AudioOutputDeviceType: + capability = DeviceInfo::AudioOutput; + break; + case Phonon::AudioCaptureDeviceType: + capability = DeviceInfo::AudioCapture; + break; + case Phonon::VideoCaptureDeviceType: + capability = DeviceInfo::VideoCapture; + break; + default: ; + } + + QList ids; + foreach (const DeviceInfo &device, m_devices) { + if (device.capabilities() & capability) + ids.append(device.id()); + } + + return ids; +} + +QHash DeviceManager::deviceProperties(int id) +{ + QHash properties; + + foreach (const DeviceInfo &device, m_devices) { + if (device.id() == id) { + properties.insert("name", device.name()); + properties.insert("description", device.description()); + properties.insert("isAdvanced", device.isAdvanced()); + properties.insert("deviceAccessList", QVariant::fromValue(device.accessList())); + properties.insert("discovererIcon", "vlc"); + + if (device.capabilities() & DeviceInfo::AudioOutput) { + properties.insert("icon", QLatin1String("audio-card")); + } + + if (device.capabilities() & DeviceInfo::AudioCapture) { + properties.insert("hasaudio", true); + properties.insert("icon", QLatin1String("audio-input-microphone")); + } + + if (device.capabilities() & DeviceInfo::VideoCapture) { + properties.insert("hasvideo", true); + properties.insert("icon", QLatin1String("camera-web")); + } + break; + } + } + + return properties; +} + +const DeviceInfo *DeviceManager::device(int id) const +{ + for (int i = 0; i < m_devices.size(); i ++) { + if (m_devices[i].id() == id) + return &m_devices[i]; + } + + return NULL; +} + +static QList vlcAudioOutBackends() +{ + QList ret; + + VLC_FOREACH_LIST(audio_output, aout) { + QByteArray name(aout->psz_name); + if (!ret.contains(name)) + ret.append(name); + } + + return ret; +} + +void DeviceManager::updateDeviceList() +{ + QList newDeviceList; + + if (!LibVLC::self || !pvlc_libvlc) + return; + + QList audioOutBackends = vlcAudioOutBackends(); + + PulseSupport *pulse = PulseSupport::getInstance(); + if (pulse && pulse->isUsable()) { + if (audioOutBackends.contains("pulse")) { + DeviceInfo defaultAudioOutputDevice(tr("Default"), false); + defaultAudioOutputDevice.setCapabilities(DeviceInfo::AudioOutput); + defaultAudioOutputDevice.addAccess(DeviceAccess("pulse", "default")); + newDeviceList.append(defaultAudioOutputDevice); + pulse->request(true); + return; + } else { + pulse->enable(false); + } + } + + QList knownSoundSystems; + // Whitelist - Order has no particular impact. + // NOTE: if listing was not intercepted by the PA code above we also need + // to try injecting the pulse aout as otherwise the user would have to + // use the fake PA device in ALSA to output through PA (kind of silly). + knownSoundSystems << QByteArray("pulse") + << QByteArray("alsa") + << QByteArray("oss") + << QByteArray("jack") + << QByteArray("aout_directx") // Windows up to VLC 2.0 + << QByteArray("directsound") // Windows from VLC 2.1 upwards + << QByteArray("auhal"); // Mac + foreach (const QByteArray &soundSystem, knownSoundSystems) { + if (!audioOutBackends.contains(soundSystem)) { + debug() << "Sound system" << soundSystem << "not supported by libvlc"; + continue; + } + + // FIXME: there is a rather ungodly amount of code duplication going + // on here. +#if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(2, 2, 0, 0)) + bool hasDevices = false; + VLC_FOREACH(audio_output_device, + device, + libvlc_audio_output_device_list_get(pvlc_libvlc, soundSystem), + libvlc_audio_output_device_list_release) { + QString idName = QString::fromUtf8(device->psz_device); + QString longName = QString::fromUtf8(device->psz_description); + + debug() << "found device" << soundSystem << idName << longName; + + DeviceInfo info(longName, true); + info.addAccess(DeviceAccess(soundSystem, idName)); + info.setCapabilities(DeviceInfo::AudioOutput); + newDeviceList.append(info); + + hasDevices = true; + } + + if (!hasDevices) { + debug() << "manually injecting sound system" << soundSystem; + DeviceInfo info(QString::fromUtf8(soundSystem), false); + info.addAccess(DeviceAccess(soundSystem, "")); + info.setCapabilities(DeviceInfo::AudioOutput); + newDeviceList.append(info); + } +#else + const int deviceCount = libvlc_audio_output_device_count(pvlc_libvlc, soundSystem); + + for (int i = 0; i < deviceCount; i++) { + VString idName(libvlc_audio_output_device_id(libvlc, soundSystem, i)); + VString longName(libvlc_audio_output_device_longname(pvlc_libvlc, soundSystem, i)); + + debug() << "found device" << soundSystem << idName << longName; + + DeviceInfo info(longName, true); + info.addAccess(DeviceAccess(soundSystem, idName)); + info.setCapabilities(DeviceInfo::AudioOutput); + newDeviceList.append(info); + } + + // libVLC gives no devices for some sound systems, like OSS + if (deviceCount == 0) { + debug() << "manually injecting sound system" << soundSystem; + // NOTE: Do not mark manually injected devices as advanced. + // libphonon filters advanced devices from the default + // selection which on systems such as OSX or Windows can + // lead to an empty device list as the injected device is + // the only available one. + DeviceInfo info(QString::fromUtf8(soundSystem), false); + info.addAccess(DeviceAccess(soundSystem, "")); + info.setCapabilities(DeviceInfo::AudioOutput); + newDeviceList.append(info); + } +#endif + } + + /* + * Compares the list with the devices available at the moment with the last list. If + * a new device is seen, a signal is emitted. If a device disappeared, another signal + * is emitted. + */ + + // Search for added devices + for (int i = 0; i < newDeviceList.count(); ++i) { + int id = newDeviceList[i].id(); + if (!listContainsDevice(m_devices, id)) { + // This is a new device, add it + m_devices.append(newDeviceList[i]); + emit deviceAdded(id); + + debug() << "Added backend device" << newDeviceList[i].name(); + } + } + + // Search for removed devices + for (int i = m_devices.count() - 1; i >= 0; --i) { + int id = m_devices[i].id(); + if (!listContainsDevice(newDeviceList, id)) { + emit deviceRemoved(id); + m_devices.removeAt(i); + } + } +} + +bool DeviceManager::listContainsDevice(const QList &list, int id) +{ + foreach (const DeviceInfo &d, list) { + if (d.id() == id) + return true; + } + return false; +} + +} +} diff --git a/src/devicemanager.h b/src/devicemanager.h new file mode 100644 index 0000000..73f1123 --- /dev/null +++ b/src/devicemanager.h @@ -0,0 +1,146 @@ +/* + Copyright (C) 2009-2010 vlc-phonon AUTHORS + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef Phonon_VLC_DEVICEMANAGER_H +#define Phonon_VLC_DEVICEMANAGER_H + +#include + +#include + +namespace Phonon +{ +namespace VLC +{ + +class Backend; + +/** \brief Container for information about devices supported by libVLC + * + * It includes a (hopefully unique) device identifier, a name identifier, a + * description, a hardware identifier (may be a platform dependent device name), + * and other relevant info. + */ +class DeviceInfo +{ +public: + enum Capability { + None = 0x0000, + AudioOutput = 0x0001, + AudioCapture = 0x0002, + VideoCapture = 0x0004 + }; +public: + /** + * Constructs a device info object and sets it's device identifiers. + */ + explicit DeviceInfo(const QString &name, bool isAdvanced = true); + + int id() const; + const QString& name() const; + const QString& description() const; + bool isAdvanced() const; + void setAdvanced(bool advanced); + const DeviceAccessList& accessList() const; + void addAccess(const DeviceAccess &access); + quint16 capabilities() const; + void setCapabilities(quint16 cap); + +private: + int m_id; + QString m_name; + QString m_description; + bool m_isAdvanced; + DeviceAccessList m_accessList; + quint16 m_capabilities; +}; + +/** \brief Keeps track of audio/video devices that libVLC supports + * + * This class maintains a device list. Types of devices: + * \li audio output devices + * \li audio capture devices + * \li video capture devices + * + * Methods are provided to retrieve information about these devices. + * + * \see EffectManager + */ +class DeviceManager : public QObject +{ + Q_OBJECT + +public: + /** + * Constructs a device manager and immediately updates the devices. + */ + explicit DeviceManager(Backend *parent); + + /** + * Clears all the devices before destroying. + */ + virtual ~DeviceManager(); + + /** + * \param type Only devices with a capability of this type are returned + * The following are supported: + * \li AudioOutputDeviceType + * \li AudioCaptureDeviceType + * \li VideoCaptureDeviceType + * + * \return A list of device identifiers that have capabilities that + * match the desired type + * + * \note The capture devices are temporarily not implemented / removed + */ + QList deviceIds(ObjectDescriptionType type); + + /** + * \param id The identifier for the device + * \return Object description properties for a device + */ + QHash deviceProperties(int id); + + /** + * \param id The identifier for the device + * \return Pointer to DeviceInfo, or NULL if the id is invalid + */ + const DeviceInfo *device(int id) const; + +signals: + void deviceAdded(int); + void deviceRemoved(int); + +public slots: + /** + * Update the current list of active devices. It probes for audio output devices, + * audio capture devices, video capture devices. The methods depend on the + * device types. + */ + void updateDeviceList(); + +private: + static bool listContainsDevice(const QList &list, int id); + +private: + Backend *m_backend; + QList m_devices; +}; +} +} // namespace Phonon::VLC + +#endif // Phonon_VLC_DEVICEMANAGER_H diff --git a/src/effect.cpp b/src/effect.cpp new file mode 100644 index 0000000..58f0262 --- /dev/null +++ b/src/effect.cpp @@ -0,0 +1,225 @@ +/* + Copyright (C) 2007-2008 Tanguy Krotoff + Copyright (C) 2008 Lukas Durfina + Copyright (C) 2009 Fathi Boudra + Copyright (C) 2009-2011 vlc-phonon AUTHORS + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "effect.h" + +#include "effectmanager.h" + +#include "mediaobject.h" + +namespace Phonon +{ +namespace VLC +{ + +Effect::Effect(EffectManager *p_em, int i_effectId, QObject *p_parent) + : QObject(p_parent) + , SinkNode() +{ + Q_UNUSED(p_em); + Q_UNUSED(i_effectId); +// p_effectManager = p_em; +// const QList effects = p_effectManager->effects(); + +// if (i_effectId >= 0 && i_effectId < effects.size()) { +// i_effect_filter = effects[ i_effectId ]->filter(); +// effect_type = effects[ i_effectId ]->type(); +// setupEffectParams(); +// } else { +// // effect ID out of range +// Q_ASSERT(0); +// } +} + +Effect::~Effect() +{ + parameterList.clear(); +} + +void Effect::handleConnectToMediaObject(MediaObject *) +{ + switch (effect_type) { + case EffectInfo::AudioEffect: +// libvlc_audio_filter_add(p_vlc_instance, (libvlc_audio_filter_names_t)i_effect_filter, vlc_exception); +// vlcExceptionRaised(); + break; + case EffectInfo::VideoEffect: +// libvlc_video_filter_add(p_vlc_current_media_player, (libvlc_video_filter_names_t)i_effect_filter, vlc_exception); +// vlcExceptionRaised(); + break; + } +} + +void Effect::handleDisconnectFromMediaObject(MediaObject *) +{ + switch (effect_type) { + case EffectInfo::AudioEffect: +// libvlc_audio_filter_remove(p_vlc_instance, (libvlc_audio_filter_names_t)i_effect_filter, vlc_exception); +// vlcExceptionRaised(); + break; + case EffectInfo::VideoEffect: +// libvlc_video_filter_remove(p_vlc_current_media_player, (libvlc_video_filter_names_t)i_effect_filter, vlc_exception); +// vlcExceptionRaised(); + break; + } +} + +void Effect::setupEffectParams() +{ +// libvlc_filter_parameter_list_t *p_list; + switch (effect_type) { + case EffectInfo::AudioEffect: +// p_list = libvlc_audio_filter_get_parameters(p_vlc_instance, (libvlc_audio_filter_names_t)i_effect_filter, vlc_exception ); +// vlcExceptionRaised(); + break; + case EffectInfo::VideoEffect: +// p_list = libvlc_video_filter_get_parameters(p_vlc_instance, (libvlc_video_filter_names_t)i_effect_filter, vlc_exception ); +// vlcExceptionRaised(); + break; + } +// if( !p_list ) +// return; + +// int i_index = 0; +// libvlc_filter_parameter_list_t *p_parameter_list = p_list; +// while (p_parameter_list) { +// switch (p_parameter_list->var_type) { +// case LIBVLC_BOOL: { +// const QString description = p_parameter_list->psz_description; +// parameterList.append(Phonon::EffectParameter( +// i_index, +// QString(p_parameter_list->psz_parameter_name), +// Phonon::EffectParameter::ToggledHint, // hints +// QVariant((bool) p_parameter_list->default_value.b_bool), +// QVariant((bool) false), +// QVariant((bool) true), +// QVariantList(), +// description)); +// break; +// } +// case LIBVLC_INT: { +// const QString description = p_parameter_list->psz_description; +// parameterList.append(Phonon::EffectParameter( +// i_index, +// QString(p_parameter_list->psz_parameter_name), +// EffectParameter::IntegerHint, // hints +// QVariant((int) p_parameter_list->default_value.i_int), +// QVariant((int) p_parameter_list->min_value.i_int), +// QVariant((int) p_parameter_list->max_value.i_int), +// QVariantList(), +// description)); +// break; +// } +// case LIBVLC_FLOAT: { +// const QString description = p_parameter_list->psz_description; +// parameterList.append(Phonon::EffectParameter( +// i_index, +// QString(p_parameter_list->psz_parameter_name), +// 0, // hints +// QVariant((double) p_parameter_list->default_value.f_float), +// QVariant((double) p_parameter_list->min_value.f_float), +// QVariant((double) p_parameter_list->max_value.f_float), +// QVariantList(), +// description)); +// break; +// } +// case LIBVLC_STRING: { +// const QString description = p_parameter_list->psz_description; +// parameterList.append(Phonon::EffectParameter( +// i_index, +// QString(p_parameter_list->psz_parameter_name), +// 0, // hints +// QVariant((const char *) p_parameter_list->default_value.psz_string), +// NULL, +// NULL, +// QVariantList(), +// description)); +// break; +// } +// } +// i_index++; +// p_parameter_list = p_parameter_list->p_next; +// } +// libvlc_filter_parameters_release(p_list); +} + +QList Effect::parameters() const +{ + return parameterList; +} + +QVariant Effect::parameterValue(const EffectParameter ¶m) const +{ + Q_UNUSED(param); + return QVariant(); +} + +void Effect::setParameterValue(const EffectParameter ¶m, const QVariant &newValue) +{ + Q_UNUSED(param); + Q_UNUSED(newValue); +// libvlc_value_t value; +// libvlc_var_type_t type; +// switch (param.type()) { +// case QVariant::Bool: +// value.b_bool = newValue.toBool(); +// type = LIBVLC_BOOL; +// break; +// case QVariant::Int: +// value.i_int = newValue.toInt(); +// type = LIBVLC_INT; +// break; +// case QVariant::Double: +// value.f_float = (float) newValue.toDouble(); +// type = LIBVLC_FLOAT; +// break; +// case QVariant::String: +// value.psz_string = newValue.toString().toAscii().data(); +// type = LIBVLC_STRING; +// break; +// default: +// break; +// } +// switch (effect_type) { +// case EffectInfo::AudioEffect: +// libvlc_audio_filter_set_parameter( +// p_vlc_instance, +// // (libvlc_audio_filter_names_t) i_effect_filter, +// param.name().toAscii().data(), +// type, +// value, +// vlc_exception); +// vlcExceptionRaised(); +// break; +// case EffectInfo::VideoEffect: +// libvlc_video_filter_set_parameter( +// p_vlc_current_media_player, +// (libvlc_video_filter_names_t) i_effect_filter, +// param.name().toAscii().data(), +// type, +// value, +// vlc_exception); +// vlcExceptionRaised(); +// break; +// } +} + +} +} // Namespace Phonon::VLC diff --git a/src/effect.h b/src/effect.h new file mode 100644 index 0000000..74f3f02 --- /dev/null +++ b/src/effect.h @@ -0,0 +1,80 @@ +/* + Copyright (C) 2007-2008 Tanguy Krotoff + Copyright (C) 2008 Lukas Durfina + Copyright (C) 2009 Fathi Boudra + Copyright (C) 2009-2011 vlc-phonon AUTHORS + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef PHONON_VLC_EFFECT_H +#define PHONON_VLC_EFFECT_H + +#include "sinknode.h" +#include "effectmanager.h" + +#include +#include + +namespace Phonon +{ +namespace VLC +{ + +class EffectManager; + +/** \brief Effect implementation for Phonon-VLC + * + * There are methods to get or set the effect parameters, implemented for + * the EffectInterface. See the Phonon documentation for details. + * + * As a sink node, it provides methods to handle the connection to a media object. + * + * An effect manager is the parent of each effect. + * + * \see EffectManager + * \see VolumeFaderEffect + */ +class Effect : public QObject, public SinkNode, public EffectInterface +{ + Q_OBJECT + Q_INTERFACES(Phonon::EffectInterface) + +public: + + Effect(EffectManager *p_em, int i_effectId, QObject *p_parent); + ~Effect(); + + void setupEffectParams(); + QList parameters() const; + QVariant parameterValue(const EffectParameter ¶m) const; + void setParameterValue(const EffectParameter ¶m, const QVariant &newValue); + + /** \reimp */ + void handleConnectToMediaObject(MediaObject *p_media_object); + /** \reimp */ + void handleDisconnectFromMediaObject(MediaObject *p_media_object); + +private: + + EffectManager *p_effectManager; + int i_effect_filter; + EffectInfo::Type effect_type; + QList parameterList; +}; + +} +} // Namespace Phonon::VLC + +#endif // PHONON_VLC_EFFECT_H diff --git a/src/effectmanager.cpp b/src/effectmanager.cpp new file mode 100644 index 0000000..631af13 --- /dev/null +++ b/src/effectmanager.cpp @@ -0,0 +1,135 @@ +/* + Copyright (C) 2007-2008 Tanguy Krotoff + Copyright (C) 2008 Lukas Durfina + Copyright (C) 2009 Fathi Boudra + Copyright (C) 2009-2011 vlc-phonon AUTHORS + Copyright (C) 2011 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "effectmanager.h" + +#include +#include + +#if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(2, 2, 0, 0)) +#include "equalizereffect.h" +#endif + +#include "utils/debug.h" +#include "utils/libvlc.h" + +namespace Phonon { +namespace VLC { + +EffectInfo::EffectInfo(const QString &name, const QString &description, + const QString &author, int filter, Type type) + : m_name(name) + , m_description(description) + , m_author(author) + , m_filter(filter) + , m_type(type) +{} + +EffectManager::EffectManager(QObject *parent) + : QObject(parent) +{ + if (!pvlc_libvlc) + return; + + updateEffects(); +} + +EffectManager::~EffectManager() +{ + m_audioEffectList.clear(); + m_videoEffectList.clear(); + + // EffectsList holds the same pointers as audio and video, so qDeleteAll on + // this container would cause a double freeing. + m_effectList.clear(); +} + +const QList EffectManager::audioEffects() const +{ + return m_audioEffectList; +} + +const QList EffectManager::videoEffects() const +{ + return m_videoEffectList; +} + +const QList EffectManager::effects() const +{ + return m_effectList; +} + +QObject *EffectManager::createEffect(int id, QObject *parent) +{ + Q_UNUSED(id); +#if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(2, 2, 0, 0)) + return new EqualizerEffect(parent); +#else + Q_UNUSED(parent); +#endif + return 0; +} + +void EffectManager::updateEffects() +{ + DEBUG_BLOCK; + + m_effectList.clear(); + m_audioEffectList.clear(); + m_videoEffectList.clear(); + + // Generic effect activation etc is entirely kaput and equalizer has specific + // API anyway, so we simply manually insert it \o/ + +#if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(2, 2, 0, 0)) + const QString eqName = QString("equalizer-%1bands").arg(QString::number(libvlc_audio_equalizer_get_band_count())); + m_audioEffectList.append(EffectInfo( + eqName, + QString(""), + QString(""), + 0, + EffectInfo::AudioEffect)); +#endif + +// int moduleCount = -1; +// VLC_FOREACH_MODULE(module, libvlc_audio_filter_list_get(libvlc)) { +// m_audioEffectList.append(new EffectInfo(module->psz_longname, +// module->psz_help, +// QString(), +// ++moduleCount, +// EffectInfo::AudioEffect)); +// } + +// moduleCount = -1; +// VLC_FOREACH_MODULE(module, libvlc_video_filter_list_get(libvlc)) { +// m_videoEffectList.append(new EffectInfo(module->psz_longname, +// module->psz_help, +// QString(), +// ++moduleCount, +// EffectInfo::VideoEffect)); +// } + + m_effectList.append(m_audioEffectList); + m_effectList.append(m_videoEffectList); +} + +} // namespace VLC +} // namespace Phonon diff --git a/src/effectmanager.h b/src/effectmanager.h new file mode 100644 index 0000000..5293cd4 --- /dev/null +++ b/src/effectmanager.h @@ -0,0 +1,119 @@ +/* + Copyright (C) 2007-2008 Tanguy Krotoff + Copyright (C) 2008 Lukas Durfina + Copyright (C) 2009 Fathi Boudra + Copyright (C) 2009-2011 vlc-phonon AUTHORS + Copyright (C) 2011 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef Phonon_VLC_EFFECTMANAGER_H +#define Phonon_VLC_EFFECTMANAGER_H + +#include + +namespace Phonon { +namespace VLC { + +class Backend; +class EffectManager; + +/// Holds information about an effect +class EffectInfo +{ +public: + + enum Type {AudioEffect, VideoEffect}; + + EffectInfo(const QString &name, + const QString &description, + const QString &author, + int filter, + Type type); + + QString name() const { + return m_name; + } + + QString description() const { + return m_description; + } + + QString author() const { + return m_author; + } + + int filter() const { + return m_filter; + } + + Type type() const { + return m_type; + } + +private: + QString m_name; + QString m_description; + QString m_author; + int m_filter; + Type m_type; +}; + +/** \brief Manages a list of effects. + * + * \see EffectInfo + */ +class EffectManager : public QObject +{ + Q_OBJECT +public: + /** + * Creates a new effect manager. It creates the lists of effects. + * + * \param backend A parent backend object for the effect manager + * + * \warning Currently it doesn't add any effects, everything is disabled. + * \see EffectInfo + */ + explicit EffectManager(QObject *parent = 0); + + /// Deletes all the effects from the lists and destroys the effect manager. + ~EffectManager(); + + /// Returns a list of available audio effects + const QList audioEffects() const; + + /// Returns a list of available video effects + const QList videoEffects() const; + + /// Returns a list of available effects + const QList effects() const; + + QObject *createEffect(int id, QObject *parent); + +private: + /// Generates the aggegated list of effects from both video and audio + void updateEffects(); + + QList m_effectList; + QList m_audioEffectList; + QList m_videoEffectList; + bool m_equalizerEnabled; +}; + +} // namespace VLC +} // namespace Phonon + +#endif // Phonon_VLC_EFFECTMANAGER_H diff --git a/src/equalizereffect.cpp b/src/equalizereffect.cpp new file mode 100644 index 0000000..64814d0 --- /dev/null +++ b/src/equalizereffect.cpp @@ -0,0 +1,80 @@ +/* + Copyright (C) 2013 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "equalizereffect.h" + +#include "mediaplayer.h" +#include "utils/debug.h" + +namespace Phonon { +namespace VLC { + +EqualizerEffect::EqualizerEffect(QObject *parent) + : QObject(parent) + , SinkNode() + , EffectInterface() + , m_equalizer(libvlc_audio_equalizer_new()) +{ + // Amarok decided to make up rules because phonon didn't manage to + // pre-amp string needs to be pre-amp + // bands need to be xxHz + // That way they can be consistently mapped to localized/formatted strings. + + EffectParameter preamp(-1, "pre-amp", 0 /* hint */, 0.0f, -20.0f, 20.0f); + m_bands.append(preamp); + + const unsigned int bandCount = libvlc_audio_equalizer_get_band_count(); + for (unsigned int i = 0; i < bandCount; ++i) { + const float frequency = libvlc_audio_equalizer_get_band_frequency(i); + const QString name = QString("%1Hz").arg(QString::number(frequency)); + EffectParameter parameter(i, name, 0 /* hint */, 0.0f, -20.0f, 20.0f); + m_bands.append(parameter); + } +} + +EqualizerEffect::~EqualizerEffect() +{ + libvlc_audio_equalizer_release(m_equalizer); +} + +QList EqualizerEffect::parameters() const +{ + return m_bands; +} + +QVariant EqualizerEffect::parameterValue(const EffectParameter ¶meter) const +{ + return libvlc_audio_equalizer_get_amp_at_index(m_equalizer, parameter.id()); +} + +void EqualizerEffect::setParameterValue(const EffectParameter ¶meter, + const QVariant &newValue) +{ + if (parameter.id() == -1) + libvlc_audio_equalizer_set_preamp(m_equalizer, newValue.toFloat()); + else + libvlc_audio_equalizer_set_amp_at_index(m_equalizer, newValue.toFloat(), parameter.id()); +} + +void EqualizerEffect::handleConnectToMediaObject(MediaObject *mediaObject) +{ + Q_UNUSED(mediaObject); + m_player->setEqualizer(m_equalizer); +} + +} // namespace VLC +} // namespace Phonon diff --git a/src/equalizereffect.h b/src/equalizereffect.h new file mode 100644 index 0000000..a1b050d --- /dev/null +++ b/src/equalizereffect.h @@ -0,0 +1,55 @@ +/* + Copyright (C) 2013 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef PHONON_VLC_EQUALIZEREFFECT_H +#define PHONON_VLC_EQUALIZEREFFECT_H + +#include + +#include +#include + +#include + +#include "sinknode.h" + +namespace Phonon { +namespace VLC { + +class EqualizerEffect : public QObject, public SinkNode, public EffectInterface +{ + Q_OBJECT + Q_INTERFACES(Phonon::EffectInterface) +public: + explicit EqualizerEffect(QObject *parent = 0); + ~EqualizerEffect(); + + QList parameters() const; + QVariant parameterValue(const EffectParameter ¶meter) const; + void setParameterValue(const EffectParameter ¶meter, const QVariant &newValue); + + void handleConnectToMediaObject(MediaObject *mediaObject); + +private: + libvlc_equalizer_t *m_equalizer; + QList m_bands; +}; + +} // namespace VLC +} // namespace Phonon + +#endif // PHONON_VLC_EQUALIZEREFFECT_H diff --git a/src/media.cpp b/src/media.cpp new file mode 100644 index 0000000..d27ccf7 --- /dev/null +++ b/src/media.cpp @@ -0,0 +1,109 @@ +/* + Copyright (C) 2011 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "media.h" + +#include + +#include + +#include "utils/debug.h" +#include "utils/libvlc.h" +#include "utils/vstring.h" + +namespace Phonon { +namespace VLC { + +Media::Media(const QByteArray &mrl, QObject *parent) : + QObject(parent), + m_media(libvlc_media_new_location(pvlc_libvlc, mrl.constData())), + m_mrl(mrl) +{ + Q_ASSERT(m_media); + + libvlc_event_manager_t *manager = libvlc_media_event_manager(m_media); + libvlc_event_type_t events[] = { + libvlc_MediaMetaChanged, + libvlc_MediaSubItemAdded, + libvlc_MediaDurationChanged, + libvlc_MediaParsedChanged, + libvlc_MediaFreed, + libvlc_MediaStateChanged + }; + const int eventCount = sizeof(events) / sizeof(*events); + for (int i = 0; i < eventCount; ++i) { + libvlc_event_attach(manager, events[i], event_cb, this); + } +} + +Media::~Media() +{ + if (m_media) { + libvlc_media_release(m_media); + m_media = 0; + } +} + +void Media::addOption(const QString &option) +{ + libvlc_media_add_option_flag(m_media, + option.toUtf8().data(), + libvlc_media_option_trusted); +} + +QString Media::meta(libvlc_meta_t meta) +{ + return VString(libvlc_media_get_meta(m_media, meta)).toQString(); +} + +void Media::event_cb(const libvlc_event_t *event, void *opaque) +{ + Media *that = reinterpret_cast(opaque); + Q_ASSERT(that); + + switch (event->type) { + case libvlc_MediaDurationChanged: + QMetaObject::invokeMethod( + that, "durationChanged", + Qt::QueuedConnection, + Q_ARG(qint64, event->u.media_duration_changed.new_duration)); + break; + case libvlc_MediaMetaChanged: + QMetaObject::invokeMethod( + that, "metaDataChanged", + Qt::QueuedConnection); + break; + case libvlc_MediaSubItemAdded: + case libvlc_MediaParsedChanged: + case libvlc_MediaFreed: + case libvlc_MediaStateChanged: + default: + break; + QString msg = QString("Unknown event: ") + QString(libvlc_event_type_name(event->type)); + Q_ASSERT_X(false, "event_cb", qPrintable(msg)); + break; + } +} + +void Media::setCdTrack(int track) +{ + debug() << "setting CDDA track" << track; + addOption(QLatin1String(":cdda-track="), QVariant(track)); +} + +} // namespace VLC +} // namespace Phonon diff --git a/src/media.h b/src/media.h new file mode 100644 index 0000000..373045c --- /dev/null +++ b/src/media.h @@ -0,0 +1,77 @@ +/* + Copyright (C) 2011 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef PHONON_VLC_MEDIA_H +#define PHONON_VLC_MEDIA_H + +#include +#include +#include + +#include +#include + +#define INTPTR_PTR(x) reinterpret_cast(x) +#define INTPTR_FUNC(x) reinterpret_cast(&x) + +namespace Phonon { +namespace VLC { + +class Media : public QObject +{ + Q_OBJECT +public: + explicit Media(const QByteArray &mrl, QObject *parent = 0); + ~Media(); + + inline libvlc_media_t *libvlc_media() const { return m_media; } + inline operator libvlc_media_t *() const { return m_media; } + + inline void addOption(const QString &option, const QVariant &argument) + { + addOption(option % argument.toString()); + } + + inline void addOption(const QString &option, intptr_t functionPtr) + { + QString optionWithPtr = option; + optionWithPtr.append(QString::number(static_cast(functionPtr))); + addOption(optionWithPtr); + } + + void addOption(const QString &option); + + QString meta(libvlc_meta_t meta); + + void setCdTrack(int track); + +signals: + void durationChanged(qint64 duration); + void metaDataChanged(); + +private: + static void event_cb(const libvlc_event_t *event, void *opaque); + + libvlc_media_t *m_media; + libvlc_state_t m_state; + QByteArray m_mrl; +}; + +} // namespace VLC +} // namespace Phonon + +#endif // PHONON_VLC_MEDIA_H diff --git a/src/mediacontroller.cpp b/src/mediacontroller.cpp new file mode 100644 index 0000000..06cfbba --- /dev/null +++ b/src/mediacontroller.cpp @@ -0,0 +1,497 @@ +/* + Copyright (C) 2007-2008 Tanguy Krotoff + Copyright (C) 2008 Lukas Durfina + Copyright (C) 2009 Fathi Boudra + Copyright (C) 2009-2011 vlc-phonon AUTHORS + Copyright (C) 2011-2018 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "mediacontroller.h" + +#include + +#include + +#include "utils/debug.h" +#include "utils/libvlc.h" +#include "mediaplayer.h" + +namespace Phonon { +namespace VLC { + +#ifdef __GNUC__ +#warning titles and chapters not covered by globaldescriptioncontainer!! +#endif + +MediaController::MediaController() + : m_subtitleAutodetect(true) + , m_subtitleEncoding("UTF-8") + , m_subtitleFontChanged(false) + , m_player(0) + , m_refreshTimer(new QTimer(dynamic_cast(this))) + , m_attemptingAutoplay(false) +{ + GlobalSubtitles::instance()->register_(this); + GlobalAudioChannels::instance()->register_(this); + resetMembers(); +} + +MediaController::~MediaController() +{ + GlobalSubtitles::instance()->unregister_(this); + GlobalAudioChannels::instance()->unregister_(this); +} + +bool MediaController::hasInterface(Interface iface) const +{ + switch (iface) { + case AddonInterface::NavigationInterface: + return true; + break; + case AddonInterface::ChapterInterface: + return true; + break; + case AddonInterface::AngleInterface: + return false; + break; + case AddonInterface::TitleInterface: + return true; + break; + case AddonInterface::SubtitleInterface: + return true; + break; + case AddonInterface::AudioChannelInterface: + return true; + break; + } + + warning() << "Interface" << iface << "is not supported by Phonon VLC :("; + return false; +} + +QVariant MediaController::interfaceCall(Interface iface, int i_command, const QList & arguments) +{ + DEBUG_BLOCK; + switch (iface) { + case AddonInterface::ChapterInterface: + switch (static_cast(i_command)) { + case AddonInterface::availableChapters: + return availableChapters(); + case AddonInterface::chapter: + return currentChapter(); + case AddonInterface::setChapter: + if (arguments.isEmpty() || !arguments.first().canConvert(QVariant::Int)) { + error() << Q_FUNC_INFO << "arguments invalid"; + return false; + } + setCurrentChapter(arguments.first().toInt()); + return true; + } + break; + case AddonInterface::TitleInterface: + switch (static_cast(i_command)) { + case AddonInterface::availableTitles: + return availableTitles(); + case AddonInterface::title: + return currentTitle(); + case AddonInterface::setTitle: + if (arguments.isEmpty() || !arguments.first().canConvert(QVariant::Int)) { + error() << Q_FUNC_INFO << "arguments invalid"; + return false; + } + setCurrentTitle(arguments.first().toInt()); + return true; + case AddonInterface::autoplayTitles: + return autoplayTitles(); + case AddonInterface::setAutoplayTitles: + if (arguments.isEmpty() || !arguments.first().canConvert(QVariant::Bool)) { + error() << Q_FUNC_INFO << " arguments invalid"; + return false; + } + setAutoplayTitles(arguments.first().toBool()); + return true; + } + break; + case AddonInterface::AngleInterface: + warning() << "AddonInterface::AngleInterface not supported!"; + break; + case AddonInterface::SubtitleInterface: + switch (static_cast(i_command)) { + case AddonInterface::availableSubtitles: + return QVariant::fromValue(availableSubtitles()); + case AddonInterface::currentSubtitle: + return QVariant::fromValue(currentSubtitle()); + case AddonInterface::setCurrentSubtitle: + if (arguments.isEmpty() || !arguments.first().canConvert()) { + error() << Q_FUNC_INFO << "arguments invalid"; + return false; + } + setCurrentSubtitle(arguments.first().value()); + return true; + case AddonInterface::setCurrentSubtitleFile: + if (arguments.isEmpty() || !arguments.first().canConvert()) { + error() << Q_FUNC_INFO << " arguments invalid"; + return false; + } + setCurrentSubtitleFile(arguments.first().value()); + case AddonInterface::subtitleAutodetect: + return QVariant::fromValue(subtitleAutodetect()); + case AddonInterface::setSubtitleAutodetect: + if (arguments.isEmpty() || !arguments.first().canConvert()) { + error() << Q_FUNC_INFO << " arguments invalid"; + return false; + } + setSubtitleAutodetect(arguments.first().value()); + return true; + case AddonInterface::subtitleEncoding: + return subtitleEncoding(); + case AddonInterface::setSubtitleEncoding: + if (arguments.isEmpty() || !arguments.first().canConvert()) { + error() << Q_FUNC_INFO << " arguments invalid"; + return false; + } + setSubtitleEncoding(arguments.first().value()); + return true; + case AddonInterface::subtitleFont: + return subtitleFont(); + case AddonInterface::setSubtitleFont: + if (arguments.isEmpty() || !arguments.first().canConvert()) { + error() << Q_FUNC_INFO << " arguments invalid"; + return false; + } + setSubtitleFont(arguments.first().value()); + return true; + } + break; + case AddonInterface::AudioChannelInterface: + switch (static_cast(i_command)) { + case AddonInterface::availableAudioChannels: + return QVariant::fromValue(availableAudioChannels()); + case AddonInterface::currentAudioChannel: + return QVariant::fromValue(currentAudioChannel()); + case AddonInterface::setCurrentAudioChannel: + if (arguments.isEmpty() || !arguments.first().canConvert()) { + error() << Q_FUNC_INFO << "arguments invalid"; + return false; + } + setCurrentAudioChannel(arguments.first().value()); + return true; + } + break; + } + + error() << Q_FUNC_INFO << "unsupported AddonInterface::Interface:" << iface; + + return QVariant(); +} + +void MediaController::resetMediaController() +{ + resetMembers(); + emit availableAudioChannelsChanged(); + emit availableSubtitlesChanged(); + emit availableTitlesChanged(0); + emit availableChaptersChanged(0); +} + +void MediaController::resetMembers() +{ + m_currentAudioChannel = Phonon::AudioChannelDescription(); + GlobalAudioChannels::self->clearListFor(this); + + m_currentSubtitle = Phonon::SubtitleDescription(); + GlobalSubtitles::instance()->clearListFor(this); + + m_currentChapter = 0; + m_availableChapters = 0; + + m_currentTitle = 1; + m_availableTitles = 0; + + m_attemptingAutoplay = false; +} + +// ----------------------------- Audio Channel ------------------------------ // +void MediaController::setCurrentAudioChannel(const Phonon::AudioChannelDescription &audioChannel) +{ + const int localIndex = GlobalAudioChannels::instance()->localIdFor(this, audioChannel.index()); + if (!m_player->setAudioTrack(localIndex)) + error() << "libVLC:" << LibVLC::errorMessage(); + else + m_currentAudioChannel = audioChannel; +} + +QList MediaController::availableAudioChannels() const +{ + return GlobalAudioChannels::instance()->listFor(this); +} + +Phonon::AudioChannelDescription MediaController::currentAudioChannel() const +{ + return m_currentAudioChannel; +} + +void MediaController::refreshAudioChannels() +{ + GlobalAudioChannels::instance()->clearListFor(this); + + const int currentChannelId = m_player->audioTrack(); + + int idCount = 0; + VLC_FOREACH_TRACK(it, m_player->audioTrackDescription()) { +#if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(3, 0, 0, 0)) + idCount= it->i_id; +#else + // LibVLC's internal ID is broken, so we simply count up as internally + // the setter will simply go by position in list anyway. +#endif + GlobalAudioChannels::instance()->add(this, idCount, QString::fromUtf8(it->psz_name), ""); + if (idCount == currentChannelId) { +#ifdef __GNUC__ +#warning GlobalDescriptionContainer does not allow reverse resolution from local to descriptor! +#endif + const QList list = GlobalAudioChannels::instance()->listFor(this); + foreach (const AudioChannelDescription &descriptor, list) { + if (descriptor.name() == QString::fromUtf8(it->psz_name)) { + m_currentAudioChannel = descriptor; + } + } + } + ++idCount; + } + + emit availableAudioChannelsChanged(); +} + +// -------------------------------- Subtitle -------------------------------- // +void MediaController::setCurrentSubtitle(const Phonon::SubtitleDescription &subtitle) +{ + DEBUG_BLOCK; + QString type = subtitle.property("type").toString(); + + debug() << subtitle; + + if (type == "file") { + QString filename = subtitle.property("name").toString(); + if (!filename.isEmpty()) { + if (!m_player->setSubtitle(filename)) + error() << "libVLC:" << LibVLC::errorMessage(); + else + m_currentSubtitle = subtitle; + + // There is no subtitle event inside libvlc so let's send our own event... + GlobalSubtitles::instance()->add(this, m_currentSubtitle); + emit availableSubtitlesChanged(); + } + } else { + const int localIndex = GlobalSubtitles::instance()->localIdFor(this, subtitle.index()); + debug () << "localid" << localIndex; + if (!m_player->setSubtitle(localIndex)) + error() << "libVLC:" << LibVLC::errorMessage(); + else + m_currentSubtitle = subtitle; + } +} + +void MediaController::setCurrentSubtitleFile(const QUrl &url) +{ + const QString file = url.toLocalFile(); + if (!m_player->setSubtitle(file)) + error() << "libVLC failed to set subtitle file:" << LibVLC::errorMessage(); + // Unfortunately the addition of SPUs does not trigger an event in the + // VLC mediaplayer, yet the actual addition to the descriptor is async. + // So for the time being our best shot at getting an up-to-date list of SPUs + // is shooting in the dark and hoping we hit something. + // Refresha after 1, 2 and 5 seconds. If we have no updated list after 5 + // seconds we are out of luck. + // https://trac.videolan.org/vlc/ticket/9796 + QObject *mediaObject = dynamic_cast(this); // MediaObject : QObject, MediaController + m_refreshTimer->singleShot(1 * 1000, mediaObject, SLOT(refreshDescriptors())); + m_refreshTimer->singleShot(2 * 1000, mediaObject, SLOT(refreshDescriptors())); + m_refreshTimer->singleShot(5 * 1000, mediaObject, SLOT(refreshDescriptors())); +} + +QList MediaController::availableSubtitles() const +{ + return GlobalSubtitles::instance()->listFor(this); +} + +Phonon::SubtitleDescription MediaController::currentSubtitle() const +{ + return m_currentSubtitle; +} + +void MediaController::refreshSubtitles() +{ + DEBUG_BLOCK; + GlobalSubtitles::instance()->clearListFor(this); + + const int currentSubtitleId = m_player->subtitle(); + + VLC_FOREACH_TRACK(it, m_player->videoSubtitleDescription()) { + debug() << "found subtitle" << it->psz_name << "[" << it->i_id << "]"; + GlobalSubtitles::instance()->add(this, it->i_id, QString::fromUtf8(it->psz_name), ""); + if (it->i_id == currentSubtitleId) { +#ifdef __GNUC__ +#warning GlobalDescriptionContainer does not allow reverse resolution from local to descriptor! +#endif + const QList list = GlobalSubtitles::instance()->listFor(this); + foreach (const SubtitleDescription &descriptor, list) { + if (descriptor.name() == QString::fromUtf8(it->psz_name)) { + m_currentSubtitle = descriptor; + } + } + } + } + + emit availableSubtitlesChanged(); +} + +bool MediaController::subtitleAutodetect() const +{ + return m_subtitleAutodetect; +} + +void MediaController::setSubtitleAutodetect(bool enabled) +{ + m_subtitleAutodetect = enabled; +} + +QString MediaController::subtitleEncoding() const +{ + return m_subtitleEncoding; +} + +void MediaController::setSubtitleEncoding(const QString &encoding) +{ + m_subtitleEncoding = encoding; +} + +QFont MediaController::subtitleFont() const +{ + return m_subtitleFont; +} + +void MediaController::setSubtitleFont(const QFont &font) +{ + m_subtitleFontChanged = true; + m_subtitleFont = font; +} + +// --------------------------------- Title ---------------------------------- // +void MediaController::setCurrentTitle(int title) +{ + DEBUG_BLOCK; + m_currentTitle = title; + + switch (source().discType()) { + case Cd: + m_player->setCdTrack(title); + return; + case Dvd: + case Vcd: + case BluRay: + m_player->setTitle(title); + return; + case NoDisc: + warning() << "Current media source is not a CD, DVD or VCD!"; + return; + } + + warning() << "MediaSource does not support setting of tile in this version of Phonon VLC!" + << "Type is" << source().discType(); +} + +int MediaController::availableTitles() const +{ + return m_availableTitles; +} + +int MediaController::currentTitle() const +{ + return m_currentTitle; +} + +void MediaController::setAutoplayTitles(bool autoplay) +{ + m_autoPlayTitles = autoplay; +} + +bool MediaController::autoplayTitles() const +{ + return m_autoPlayTitles; +} + +void MediaController::refreshTitles() +{ + m_availableTitles = 0; + +#if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(3, 0, 0, 0)) + SharedTitleDescriptions list = m_player->titleDescription(); + for (unsigned int i = 0; i < list->size(); ++i) { + ++m_availableTitles; + emit availableTitlesChanged(m_availableTitles); + } +#else + VLC_FOREACH_TRACK(it, m_player->titleDescription()) { + ++m_availableTitles; + emit availableTitlesChanged(m_availableTitles); + } +#endif +} + +// -------------------------------- Chapter --------------------------------- // +void MediaController::setCurrentChapter(int chapter) +{ + m_currentChapter = chapter; + m_player->setChapter(chapter); +} + +int MediaController::availableChapters() const +{ + return m_availableChapters; +} + +int MediaController::currentChapter() const +{ + return m_currentChapter; +} + +// We need to rebuild available chapters when title is changed +void MediaController::refreshChapters(int title) +{ + m_availableChapters = 0; + + // Get the description of available chapters for specific title +#if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(3, 0, 0, 0)) + SharedChapterDescriptions list = m_player->videoChapterDescription(title); + for (unsigned int i = 0; i < list->size(); ++i) { + ++m_availableChapters; + emit availableChaptersChanged(m_availableChapters); + } +#else + VLC_FOREACH_TRACK(it, m_player->videoChapterDescription(title)) { + ++m_availableChapters; + emit availableChaptersChanged(m_availableChapters); + } +#endif +} + +// --------------------------------- Angle ---------------------------------- // +// NOT SUPPORTED IN LIBVLC // + +} // namespace VLC +} // namespace Phonon diff --git a/src/mediacontroller.h b/src/mediacontroller.h new file mode 100644 index 0000000..a6b0561 --- /dev/null +++ b/src/mediacontroller.h @@ -0,0 +1,156 @@ +/* + Copyright (C) 2007-2008 Tanguy Krotoff + Copyright (C) 2008 Lukas Durfina + Copyright (C) 2009 Fathi Boudra + Copyright (C) 2009-2011 vlc-phonon AUTHORS + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef PHONON_VLC_MEDIACONTROLLER_H +#define PHONON_VLC_MEDIACONTROLLER_H + +#include +#include +#include + +#include + +class QTimer; + +namespace Phonon { +namespace VLC { + +class MediaPlayer; + +/** + * \brief Interface for AddonInterface. + * + * Provides a bridge between Phonon's AddonInterface and MediaController. + * + * This class cannot inherit from QObject has MediaObject already inherit from QObject. + * This is a Qt limitation: there is no possibility to inherit virtual Qobject :/ + * See http://doc.trolltech.com/qq/qq15-academic.html + * Phonon implementation got the same problem. + * + * \see MediaObject + */ +class MediaController : public AddonInterface +{ +public: + + MediaController(); + virtual ~MediaController(); + + bool hasInterface(Interface iface) const; + + QVariant interfaceCall(Interface iface, int i_command, const QList & arguments = QList()); + + /** + * Overloaded by MediaObject through MediaObjectInterface. + * Access to the media source is necessary to identify the type of the source + * and behave accordingly. + * + * For example setTitle calls need to work on both DVDs and CDs, however + * in libvlc titles and tracks are two different concepts. + */ + virtual MediaSource source() const = 0; + + // MediaController signals + virtual void availableSubtitlesChanged() = 0; + virtual void availableAudioChannelsChanged() = 0; + + virtual void availableChaptersChanged(int) = 0; + virtual void availableTitlesChanged(int) = 0; + + void titleAdded(int id, const QString &name); + void chapterAdded(int titleId, const QString &name); + +protected: + // AudioChannel + void setCurrentAudioChannel(const Phonon::AudioChannelDescription &audioChannel); + QList availableAudioChannels() const; + Phonon::AudioChannelDescription currentAudioChannel() const; + void refreshAudioChannels(); + + // Subtitle + void setCurrentSubtitle(const Phonon::SubtitleDescription &subtitle); + void setCurrentSubtitleFile(const QUrl &url); + QList availableSubtitles() const; + Phonon::SubtitleDescription currentSubtitle() const; + void refreshSubtitles(); + bool subtitleAutodetect() const; + void setSubtitleAutodetect(bool enabled); + QString subtitleEncoding() const; + void setSubtitleEncoding(const QString &encoding); + QFont subtitleFont() const; + void setSubtitleFont(const QFont &font); + + // Chapter + void setCurrentChapter(int chapterNumber); + int availableChapters() const; + int currentChapter() const; + void refreshChapters(int title); + + // Title + void setCurrentTitle(int titleNumber); + int availableTitles() const; + int currentTitle() const; + void setAutoplayTitles(bool autoplay); + bool autoplayTitles() const; + void refreshTitles(); + + /** + * Clear all member variables and emit appropriate signals. + * This is used each time we restart the video. + * + * \see resetMembers + */ + void resetMediaController(); + + /** + * Reset all member variables. + * + * \see resetMediaController + */ + void resetMembers(); + + Phonon::AudioChannelDescription m_currentAudioChannel; + Phonon::SubtitleDescription m_currentSubtitle; + + int m_currentChapter; + int m_availableChapters; + + int m_currentTitle; + int m_availableTitles; + + bool m_autoPlayTitles; + + bool m_subtitleAutodetect; + QString m_subtitleEncoding; + bool m_subtitleFontChanged; + QFont m_subtitleFont; + + // MediaPlayer + MediaPlayer *m_player; + + QTimer *m_refreshTimer; + + bool m_attemptingAutoplay; +}; + +} // namespace VLC +} // namespace Phonon + +#endif // PHONON_VLC_MEDIACONTROLLER_H diff --git a/src/mediaobject.cpp b/src/mediaobject.cpp new file mode 100644 index 0000000..2a6bf6f --- /dev/null +++ b/src/mediaobject.cpp @@ -0,0 +1,844 @@ +/* + Copyright (C) 2007-2008 Tanguy Krotoff + Copyright (C) 2008 Lukas Durfina + Copyright (C) 2009 Fathi Boudra + Copyright (C) 2010 Ben Cooksley + Copyright (C) 2009-2011 vlc-phonon AUTHORS + Copyright (C) 2010-2021 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "mediaobject.h" + +#include +#include +#include + +#include + +#include +#include + +#include "utils/debug.h" +#include "utils/libvlc.h" +#include "media.h" +#include "sinknode.h" +#include "streamreader.h" + +//Time in milliseconds before sending aboutToFinish() signal +//2 seconds +static const int ABOUT_TO_FINISH_TIME = 2000; + +namespace Phonon { +namespace VLC { + +MediaObject::MediaObject(QObject *parent) + : QObject(parent) + , m_nextSource(MediaSource(QUrl())) + , m_streamReader(0) + , m_state(Phonon::StoppedState) + , m_tickInterval(0) + , m_transitionTime(0) + , m_media(0) +{ + qRegisterMetaType >("QMultiMap"); + + m_player = new MediaPlayer(this); + Q_ASSERT(m_player); + if (!m_player->libvlc_media_player()) + error() << "libVLC:" << LibVLC::errorMessage(); + + // Player signals. + connect(m_player, SIGNAL(seekableChanged(bool)), this, SIGNAL(seekableChanged(bool))); + connect(m_player, SIGNAL(timeChanged(qint64)), this, SLOT(timeChanged(qint64))); + connect(m_player, SIGNAL(stateChanged(MediaPlayer::State)), this, SLOT(updateState(MediaPlayer::State))); + connect(m_player, SIGNAL(hasVideoChanged(bool)), this, SLOT(onHasVideoChanged(bool))); + connect(m_player, SIGNAL(bufferChanged(int)), this, SLOT(setBufferStatus(int))); + connect(m_player, SIGNAL(timeChanged(qint64)), this, SLOT(timeChanged(qint64))); + + // Internal Signals. + connect(this, SIGNAL(moveToNext()), SLOT(moveToNextSource())); + connect(m_refreshTimer, SIGNAL(timeout()), this, SLOT(refreshDescriptors())); + + resetMembers(); +} + +MediaObject::~MediaObject() +{ + unloadMedia(); + // Shutdown the pulseaudio mainloop before the MediaPlayer gets destroyed + // (it is a child of the MO). There appears to be a peculiar race condition + // between the pa_thread_mainloop used by VLC and the pa_glib_mainloop used + // by Phonon's PulseSupport where for a very short time frame after the + // former was stopped and freed the latter can run and fall over + // Invalid read from eventfd: Bad file descriptor + // Code should not be reached at pulsecore/fdsem.c:157, function flush(). Aborting. + // Since we don't use PulseSupport since VLC 2.2 we can simply force a + // loop shutdown even when the application isn't about to terminate. + // The instance gets created again anyway. + PulseSupport::shutdown(); +} + +void MediaObject::resetMembers() +{ + // default to -1, so that streams won't break and to comply with the docs (-1 if unknown) + m_totalTime = -1; + m_hasVideo = false; + m_seekpoint = 0; + + m_prefinishEmitted = false; + m_aboutToFinishEmitted = false; + + m_lastTick = 0; + + m_timesVideoChecked = 0; + + m_buffering = false; + m_stateAfterBuffering = ErrorState; + + resetMediaController(); + + // Forcefully shutdown plusesupport to prevent crashing between the PS PA glib mainloop + // and the VLC PA threaded mainloop. See destructor. + PulseSupport::shutdown(); +} + +void MediaObject::play() +{ + DEBUG_BLOCK; + + switch (m_state) { + case PlayingState: + // Do not do anything if we are already playing (as per documentation). + return; + case PausedState: + m_player->resume(); + break; + default: + setupMedia(); + if (m_player->play()) + error() << "libVLC:" << LibVLC::errorMessage(); + break; + } +} + +void MediaObject::pause() +{ + DEBUG_BLOCK; + switch (m_state) { + case BufferingState: + case PlayingState: + m_player->pause(); + break; + case PausedState: + return; + default: + debug() << "doing paused play"; + setupMedia(); + m_player->pausedPlay(); + break; + } +} + +void MediaObject::stop() +{ + DEBUG_BLOCK; + if (m_streamReader) + m_streamReader->unlock(); + m_nextSource = MediaSource(QUrl()); + m_player->stop(); +} + +void MediaObject::seek(qint64 milliseconds) +{ + DEBUG_BLOCK; + + switch (m_state) { + case PlayingState: + case PausedState: + case BufferingState: + break; + default: + // Seeking while not being in a playingish state is cached for later. + m_seekpoint = milliseconds; + return; + } + + debug() << "seeking" << milliseconds << "msec"; + + m_player->setTime(milliseconds); + + const qint64 time = currentTime(); + const qint64 total = totalTime(); + + // Reset last tick marker so we emit time even after seeking + if (time < m_lastTick) + m_lastTick = time; + if (time < total - m_prefinishMark) + m_prefinishEmitted = false; + if (time < total - ABOUT_TO_FINISH_TIME) + m_aboutToFinishEmitted = false; +} + +void MediaObject::timeChanged(qint64 time) +{ + const qint64 totalTime = m_totalTime; + + switch (m_state) { + case PlayingState: + case BufferingState: + case PausedState: + emitTick(time); + default: + break; + } + + if (m_state == PlayingState || m_state == BufferingState) { // Buffering is concurrent + if (time >= totalTime - m_prefinishMark) { + if (!m_prefinishEmitted) { + m_prefinishEmitted = true; + emit prefinishMarkReached(totalTime - time); + } + } + // Note that when the totalTime is <= 0 we cannot calculate any sane delta. + if (totalTime > 0 && time >= totalTime - ABOUT_TO_FINISH_TIME) + emitAboutToFinish(); + } +} + +void MediaObject::emitTick(qint64 time) +{ + if (m_tickInterval == 0) // Make sure we do not ever emit ticks when deactivated.\] + return; + if (time + m_tickInterval >= m_lastTick) { + m_lastTick = time; + emit tick(time); + } +} + +void MediaObject::loadMedia(const QByteArray &mrl) +{ + DEBUG_BLOCK; + + // Initial state is loading, from which we quickly progress to stopped because + // libvlc does not provide feedback on loading and the media does not get loaded + // until we play it. + // FIXME: libvlc should really allow for this as it can cause unexpected delay + // even though the GUI might indicate that playback should start right away. + changeState(Phonon::LoadingState); + + m_mrl = mrl; + debug() << "loading encoded:" << m_mrl; + + // We do not have a loading state generally speaking, usually the backend + // is exepected to go to loading state and then at some point reach stopped, + // at which point playback can be started. + // See state enum documentation for more information. + changeState(Phonon::StoppedState); +} + +void MediaObject::loadMedia(const QString &mrl) +{ + loadMedia(mrl.toUtf8()); +} + +qint32 MediaObject::tickInterval() const +{ + return m_tickInterval; +} + +/** + * Supports runtime changes. + * If the user goes to tick(0) we stop the timer, otherwise we fire it up. + */ +void MediaObject::setTickInterval(qint32 interval) +{ + m_tickInterval = interval; +} + +qint64 MediaObject::currentTime() const +{ + qint64 time = -1; + + switch (state()) { + case Phonon::PausedState: + case Phonon::BufferingState: + case Phonon::PlayingState: + time = m_player->time(); + break; + case Phonon::StoppedState: + case Phonon::LoadingState: + time = 0; + break; + case Phonon::ErrorState: + time = -1; + break; + } + + return time; +} + +Phonon::State MediaObject::state() const +{ + return m_state; +} + +Phonon::ErrorType MediaObject::errorType() const +{ + return Phonon::NormalError; +} + +MediaSource MediaObject::source() const +{ + return m_mediaSource; +} + +void MediaObject::setSource(const MediaSource &source) +{ + DEBUG_BLOCK; + + // Reset previous streamereaders + if (m_streamReader) { + m_streamReader->unlock(); + delete m_streamReader; + m_streamReader = 0; + // For streamreaders we exchanage the player's seekability with the + // reader's so here we change it back. + // Note: the reader auto-disconnects due to destruction. + connect(m_player, SIGNAL(seekableChanged(bool)), this, SIGNAL(seekableChanged(bool))); + } + + // Reset previous isScreen flag + m_isScreen = false; + + m_mediaSource = source; + + QByteArray url; + switch (source.type()) { + case MediaSource::Invalid: + error() << Q_FUNC_INFO << "MediaSource Type is Invalid:" << source.type(); + break; + case MediaSource::Empty: + error() << Q_FUNC_INFO << "MediaSource is empty."; + break; + case MediaSource::LocalFile: + case MediaSource::Url: + debug() << "MediaSource::Url:" << source.url(); + if (source.url().scheme().isEmpty()) { + url = "file://"; + // QUrl considers url.scheme.isEmpty() == url.isRelative(), + // so to be sure the url is not actually absolute we just + // check the first character + if (!source.url().toString().startsWith('/')) + url.append(QFile::encodeName(QDir::currentPath()) + '/'); + } + url += source.url().toEncoded(); + loadMedia(url); + break; + case MediaSource::Disc: + switch (source.discType()) { + case Phonon::NoDisc: + error() << Q_FUNC_INFO << "the MediaSource::Disc doesn't specify which one (Phonon::NoDisc)"; + return; + case Phonon::Cd: + loadMedia(QLatin1Literal("cdda://") % m_mediaSource.deviceName()); + break; + case Phonon::Dvd: + loadMedia(QLatin1Literal("dvd://") % m_mediaSource.deviceName()); + break; + case Phonon::Vcd: + loadMedia(QLatin1Literal("vcd://") % m_mediaSource.deviceName()); + break; + case Phonon::BluRay: + loadMedia(QLatin1Literal("bluray://") % m_mediaSource.deviceName()); + break; + } + break; + case MediaSource::CaptureDevice: { + QByteArray driverName; + QString deviceName; + + if (source.deviceAccessList().isEmpty()) { + error() << Q_FUNC_INFO << "No device access list for this capture device"; + break; + } + + // TODO try every device in the access list until it works, not just the first one + driverName = source.deviceAccessList().first().first; + deviceName = source.deviceAccessList().first().second; + + if (driverName == QByteArray("v4l2")) { + loadMedia(QLatin1Literal("v4l2://") % deviceName); + } else if (driverName == QByteArray("alsa")) { + /* + * Replace "default" and "plughw" and "x-phonon" with "hw" for capture device names, because + * VLC does not want to open them when using default instead of hw. + * plughw also does not work. + * + * TODO investigate what happens + */ + if (deviceName.startsWith(QLatin1String("default"))) { + deviceName.replace(0, 7, "hw"); + } + if (deviceName.startsWith(QLatin1String("plughw"))) { + deviceName.replace(0, 6, "hw"); + } + if (deviceName.startsWith(QLatin1String("x-phonon"))) { + deviceName.replace(0, 8, "hw"); + } + + loadMedia(QLatin1Literal("alsa://") % deviceName); + } else if (driverName == "screen") { + loadMedia(QLatin1Literal("screen://") % deviceName); + + // Set the isScreen flag needed to add extra options in playInternal + m_isScreen = true; + } else { + error() << Q_FUNC_INFO << "Unsupported MediaSource::CaptureDevice:" << driverName; + break; + } + break; + } + case MediaSource::Stream: + m_streamReader = new StreamReader(this); + // LibVLC refuses to emit seekability as it does a try-and-seek approach + // to work around this we exchange the player's seekability signal + // for the readers + // https://bugs.kde.org/show_bug.cgi?id=293012 + connect(m_streamReader, SIGNAL(streamSeekableChanged(bool)), this, SIGNAL(seekableChanged(bool))); + disconnect(m_player, SIGNAL(seekableChanged(bool)), this, SIGNAL(seekableChanged(bool))); + // Only connect now to avoid seekability detection before we are connected. + m_streamReader->connectToSource(source); + loadMedia(QByteArray("imem://")); + break; + } + + debug() << "Sending currentSourceChanged"; + emit currentSourceChanged(m_mediaSource); +} + +void MediaObject::setNextSource(const MediaSource &source) +{ + DEBUG_BLOCK; + debug() << source.url(); + m_nextSource = source; + // This function is not ever called by the consumer but only libphonon. + // Furthermore libphonon only calls this function in its aboutToFinish slot, + // iff sources are already in the queue. In case our aboutToFinish was too + // late we may already be stopped when the slot gets activated. + // Therefore we need to make sure that we move to the next source iff + // this function is called when we are in stoppedstate. + if (m_state == StoppedState) + moveToNext(); +} + +qint32 MediaObject::prefinishMark() const +{ + return m_prefinishMark; +} + +void MediaObject::setPrefinishMark(qint32 msecToEnd) +{ + m_prefinishMark = msecToEnd; + if (currentTime() < totalTime() - m_prefinishMark) { + // Not about to finish + m_prefinishEmitted = false; + } +} + +qint32 MediaObject::transitionTime() const +{ + return m_transitionTime; +} + +void MediaObject::setTransitionTime(qint32 time) +{ + m_transitionTime = time; +} + +void MediaObject::emitAboutToFinish() +{ + if (!m_aboutToFinishEmitted) { + // Track is about to finish + m_aboutToFinishEmitted = true; + emit aboutToFinish(); + } +} + +// State changes are force queued by libphonon. +void MediaObject::changeState(Phonon::State newState) +{ + DEBUG_BLOCK; + + // State not changed + if (newState == m_state) + return; + + debug() << m_state << "-->" << newState; + +#ifdef __GNUC__ +#warning do we actually need m_seekpoint? if a consumer seeks before playing state that is their problem?! +#endif + // Workaround that seeking needs to work before the file is being played... + // We store seeks and apply them when going to seek (or discard them on reset). + if (newState == PlayingState) { + if (m_seekpoint != 0) { + seek(m_seekpoint); + m_seekpoint = 0; + } + } + + // State changed + Phonon::State previousState = m_state; + m_state = newState; + emit stateChanged(m_state, previousState); +} + +void MediaObject::moveToNextSource() +{ + DEBUG_BLOCK; + + setSource(m_nextSource); + + // The consumer may set an invalid source as final source to force a + // queued stop, regardless of how fast the consumer is at actually calling + // stop. Such a source must not cause an actual move (moving ~= state + // changes towards playing) but instead we only set the source to reflect + // that we got the setNextSource call. + if (hasNextTrack()) + play(); + + m_nextSource = MediaSource(QUrl()); +} + +inline bool MediaObject::hasNextTrack() +{ + return m_nextSource.type() != MediaSource::Invalid && m_nextSource.type() != MediaSource::Empty; +} + +inline void MediaObject::unloadMedia() +{ + if (m_media) { + m_media->disconnect(this); + m_media->deleteLater(); + m_media = 0; + } +} + +void MediaObject::setupMedia() +{ + DEBUG_BLOCK; + + unloadMedia(); + resetMembers(); + + // Create a media with the given MRL + m_media = new Media(m_mrl, this); + if (!m_media) + error() << "libVLC:" << LibVLC::errorMessage(); + + if (m_isScreen) { + m_media->addOption(QLatin1String("screen-fps=24.0")); + m_media->addOption(QLatin1String("screen-caching=300")); + } + + if (source().discType() == Cd && m_currentTitle > 0) + m_media->setCdTrack(m_currentTitle); + + if (m_streamReader) + // StreamReader is no sink but a source, for this we have no concept right now + // also we do not need one since the reader is the only source we have. + // Consequently we need to manually tell the StreamReader to attach to the Media. + m_streamReader->addToMedia(m_media); + + if (!m_subtitleAutodetect) + m_media->addOption(QLatin1String(":no-sub-autodetect-file")); + + if (m_subtitleEncoding != QLatin1String("UTF-8")) // utf8 is phonon default, so let vlc handle it + m_media->addOption(QLatin1String(":subsdec-encoding="), m_subtitleEncoding); + + if (!m_subtitleFontChanged) // Update font settings + m_subtitleFont = QFont(); + +#ifdef __GNUC__ +#warning freetype module is not working as expected - font api not working +#endif + // BUG: VLC's freetype module doesn't pick up per-media options + // vlc -vvvv --freetype-font="Comic Sans MS" multiple_sub_sample.mkv :freetype-font=Arial + // https://trac.videolan.org/vlc/ticket/9797 + m_media->addOption(QLatin1String(":freetype-font="), m_subtitleFont.family()); + m_media->addOption(QLatin1String(":freetype-fontsize="), m_subtitleFont.pointSize()); + if (m_subtitleFont.bold()) + m_media->addOption(QLatin1String(":freetype-bold")); + else + m_media->addOption(QLatin1String(":no-freetype-bold")); + + foreach (SinkNode *sink, m_sinks) { + sink->addToMedia(m_media); + } + + // Connect to Media signals. Disconnection is done at unloading. + connect(m_media, SIGNAL(durationChanged(qint64)), + this, SLOT(updateDuration(qint64))); + connect(m_media, SIGNAL(metaDataChanged()), + this, SLOT(updateMetaData())); + + // Update available audio channels/subtitles/angles/chapters/etc... + // i.e everything from MediaController + // There is no audio channel/subtitle/angle/chapter events inside libvlc + // so let's send our own events... + // This will reset the GUI + resetMediaController(); + + // Play + m_player->setMedia(m_media); +} + +QString MediaObject::errorString() const +{ + return libvlc_errmsg(); +} + +bool MediaObject::hasVideo() const +{ + // Cached: sometimes 4.0.0-dev sends the vout event but then + // has_vout is still false. Guard against this by simply always reporting + // the last hasVideoChanged value. If that is off we can still drop into + // libvlc in case it changed meanwhile. + return m_hasVideo || m_player->hasVideoOutput(); +} + +bool MediaObject::isSeekable() const +{ + if (m_streamReader) + return m_streamReader->streamSeekable(); + return m_player->isSeekable(); +} + +void MediaObject::updateDuration(qint64 newDuration) +{ + // This here cache is needed because we need to provide -1 as totalTime() + // for as long as we do not get a proper update through this slot. + // VLC reports -1 with no media but 0 if it does not know the duration, so + // apps that assume 0 = unknown get screwed if they query too early. + // http://bugs.tomahawk-player.org/browse/TWK-1029 + m_totalTime = newDuration; + emit totalTimeChanged(m_totalTime); +} + +void MediaObject::updateMetaData() +{ + QMultiMap metaDataMap; + + const QString artist = m_media->meta(libvlc_meta_Artist); + const QString title = m_media->meta(libvlc_meta_Title); + const QString nowPlaying = m_media->meta(libvlc_meta_NowPlaying); + + // Streams sometimes have the artist and title munged in nowplaying. + // With ALBUM = Title and TITLE = NowPlaying it will still show up nicely in Amarok. + if (artist.isEmpty() && !nowPlaying.isEmpty()) { + metaDataMap.insert(QLatin1String("ALBUM"), title); + metaDataMap.insert(QLatin1String("TITLE"), nowPlaying); + } else { + metaDataMap.insert(QLatin1String("ALBUM"), m_media->meta(libvlc_meta_Album)); + metaDataMap.insert(QLatin1String("TITLE"), title); + } + + metaDataMap.insert(QLatin1String("ARTIST"), artist); + metaDataMap.insert(QLatin1String("DATE"), m_media->meta(libvlc_meta_Date)); + metaDataMap.insert(QLatin1String("GENRE"), m_media->meta(libvlc_meta_Genre)); + metaDataMap.insert(QLatin1String("TRACKNUMBER"), m_media->meta(libvlc_meta_TrackNumber)); + metaDataMap.insert(QLatin1String("DESCRIPTION"), m_media->meta(libvlc_meta_Description)); + metaDataMap.insert(QLatin1String("COPYRIGHT"), m_media->meta(libvlc_meta_Copyright)); + metaDataMap.insert(QLatin1String("URL"), m_media->meta(libvlc_meta_URL)); + metaDataMap.insert(QLatin1String("ENCODEDBY"), m_media->meta(libvlc_meta_EncodedBy)); + + if (metaDataMap == m_vlcMetaData) { + // No need to issue any change, the data is the same + return; + } + m_vlcMetaData = metaDataMap; + + emit metaDataChanged(metaDataMap); +} + +void MediaObject::updateState(MediaPlayer::State state) +{ + DEBUG_BLOCK; + debug() << state; + debug() << "attempted autoplay?" << m_attemptingAutoplay; + + if (m_attemptingAutoplay) { + switch (state) { + case MediaPlayer::PlayingState: + case MediaPlayer::PausedState: + m_attemptingAutoplay = false; + break; + case MediaPlayer::ErrorState: + debug() << "autoplay failed, must be end of media."; + // The error should not be reflected to the consumer. So we swap it + // for finished() which is actually what is happening here. + // Or so we think ;) + state = MediaPlayer::EndedState; + --m_currentTitle; + break; + default: + debug() << "not handling as part of autplay:" << state; + break; + } + } + + switch (state) { + case MediaPlayer::NoState: + changeState(LoadingState); + break; + case MediaPlayer::OpeningState: + changeState(LoadingState); + break; + case MediaPlayer::BufferingState: + changeState(BufferingState); + break; + case MediaPlayer::PlayingState: + changeState(PlayingState); + break; + case MediaPlayer::PausedState: + changeState(PausedState); + break; + case MediaPlayer::StoppedState: + changeState(StoppedState); + break; + case MediaPlayer::EndedState: + if (hasNextTrack()) { + moveToNextSource(); + } else if (source().discType() == Cd && m_autoPlayTitles && !m_attemptingAutoplay) { + debug() << "trying to simulate autoplay"; + m_attemptingAutoplay = true; + m_player->setCdTrack(++m_currentTitle); + } else { + m_attemptingAutoplay = false; + emitAboutToFinish(); + emit finished(); + changeState(StoppedState); + } + break; + case MediaPlayer::ErrorState: + debug() << errorString(); + emitAboutToFinish(); + emit finished(); + changeState(ErrorState); + break; + } + + if (m_buffering) { + switch (state) { + case MediaPlayer::BufferingState: + break; + case MediaPlayer::PlayingState: + debug() << "Restoring buffering state after state change to Playing"; + changeState(BufferingState); + m_stateAfterBuffering = PlayingState; + break; + case MediaPlayer::PausedState: + debug() << "Restoring buffering state after state change to Paused"; + changeState(BufferingState); + m_stateAfterBuffering = PausedState; + break; + default: + debug() << "Buffering aborted!"; + m_buffering = false; + break; + } + } + + return; +} + +void MediaObject::onHasVideoChanged(bool hasVideo) +{ + DEBUG_BLOCK; + if (m_hasVideo != hasVideo) { + m_hasVideo = hasVideo; + emit hasVideoChanged(m_hasVideo); + } else { + // We can simply return if we are have the appropriate caching already. + // Otherwise we'd do pointless rescans of mediacontroller stuff. + // MC and MO are force-reset on media changes anyway. + return; + } + + refreshDescriptors(); +} + +void MediaObject::setBufferStatus(int percent) +{ + // VLC does not have a buffering state (surprise!) but instead only sends the + // event (surprise!). Hence we need to simulate the state change. + // Problem with BufferingState is that it is actually concurrent to Playing or Paused + // meaning that while you are buffering you can also pause, thus triggering + // a state change to paused. To handle this we let updateState change the + // state accordingly (as we need to allow the UI to update itself, and + // immediately after that we change back to buffering again. + // This loop can only be interrupted by a state change to !Playing & !Paused + // or by reaching a 100 % buffer caching (i.e. full cache). + + m_buffering = true; + if (m_state != BufferingState) { + m_stateAfterBuffering = m_state; + changeState(BufferingState); + } + + emit bufferStatus(percent); + + // Transit to actual state only after emission so the signal is still + // delivered while in BufferingState. + if (percent >= 100) { // http://trac.videolan.org/vlc/ticket/5277 + m_buffering = false; + changeState(m_stateAfterBuffering); + } +} + +void MediaObject::refreshDescriptors() +{ + if (m_player->titleCount() > 0) + refreshTitles(); + + if (hasVideo()) { + refreshAudioChannels(); + refreshSubtitles(); + + if (m_player->videoChapterCount() > 0) + refreshChapters(m_player->title()); + } +} + +qint64 MediaObject::totalTime() const +{ + return m_totalTime; +} + +void MediaObject::addSink(SinkNode *node) +{ + Q_ASSERT(!m_sinks.contains(node)); + m_sinks.append(node); +} + +void MediaObject::removeSink(SinkNode *node) +{ + Q_ASSERT(node); + m_sinks.removeAll(node); +} + +} // namespace VLC +} // namespace Phonon diff --git a/src/mediaobject.h b/src/mediaobject.h new file mode 100644 index 0000000..8033113 --- /dev/null +++ b/src/mediaobject.h @@ -0,0 +1,347 @@ +/* + Copyright (C) 2007-2008 Tanguy Krotoff + Copyright (C) 2008 Lukas Durfina + Copyright (C) 2009 Fathi Boudra + Copyright (C) 2009-2011 vlc-phonon AUTHORS + Copyright (C) 2010-2011 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef PHONON_VLC_MEDIAOBJECT_H +#define PHONON_VLC_MEDIAOBJECT_H + +#include +#include + +#include +#include + +#include "mediacontroller.h" +#include "mediaplayer.h" + +namespace Phonon +{ +namespace VLC +{ + +class Media; +class SinkNode; +class StreamReader; + +/** \brief Implementation for the most important class in Phonon + * + * The MediaObject class is the workhorse for Phonon. It handles what is needed + * to play media. It has a media source object used to configure what media to + * play. + * + * It provides the essential methods setSource(), play(), seek() and additional + * methods to configure the next media source, the transition between sources, + * transition times, ticks, other. + * + * There are numerous signals that provide information about the state of the media + * and of the playing process. The aboutToFinish() and finished() signals are used + * to see when the current media is finished. + * + * This class does not contain methods directly involved with libVLC. This part is + * handled by the VLCMediaObject class. There are protected methods and slots + * inherited by that class, like playInternal(), seekInternal(). + * These methods have no implementation here. + * + * For documentation regarding the methods implemented for MediaObjectInterface, see + * the Phonon documentation. + * + * \see Phonon::MediaObjectInterface + * \see VLCMediaObject + */ +class MediaObject : public QObject, public MediaObjectInterface, public MediaController +{ + Q_OBJECT + Q_INTERFACES(Phonon::MediaObjectInterface Phonon::AddonInterface) + friend class SinkNode; + +public: + /** + * Initializes the members, connects the private slots to their corresponding signals, + * sets the next media source to an empty media source. + * + * \param parent A parent for the QObject + */ + explicit MediaObject(QObject *parent); + ~MediaObject(); + + /** + * Reset members (those that need resetting anyway). + * Should always be called before going to a new source. + */ + void resetMembers(); + + /** + * If the current state is paused, it resumes playing. Else, the playback + * is commenced. + */ + void play(); + + /// Pauses the playback for the media player. + void pause(); + + /// Sets the next media source to an empty one and stops playback. + void stop(); + + /// \returns \c true when there is a video available, \c false otherwise + bool hasVideo() const; + + /// \returns \c true when the MediaObject is seekable, \c false otherwise + bool isSeekable() const; + + /// \returns total time (length, duration) of the current MediaSource (-1 if unknown) + qint64 totalTime() const; + + /// \returns An error message with the last libVLC error. + QString errorString() const; + + /** + * Adds a sink for this media object. During playInternal(), all the sinks + * will have their addToMedia() called. + * + * \see playInternal() + * \see SinkNode::addToMedia() + */ + void addSink(SinkNode *node); + + /// Removes a sink from this media object. + void removeSink(SinkNode *node); + + /** + * Pushes a seek command to the SeekStack for this media object. The SeekStack then + * calls seekInternal() when it's popped. + */ + void seek(qint64 milliseconds); + + /** + * \return The interval between successive tick() signals. If set to 0, the emission + * of these signals is disabled. + */ + qint32 tickInterval() const; + + /** + * Sets the interval between successive tick() signals. If set to 0, it disables the + * emission of these signals. + */ + void setTickInterval(qint32 tickInterval); + + /** + * \return The current time of the media, depending on the current state. + * If the current state is stopped or loading, 0 is returned. + * If the current state is error or unknown, -1 is returned. + */ + qint64 currentTime() const; + + /// \return The current state for this media object. + Phonon::State state() const; + + /// All errors are categorized as normal errors. + Phonon::ErrorType errorType() const; + + /// \return The current media source for this media object. + MediaSource source() const; + + /** + * Sets the current media source for this media object. Depending on the source type, + * the media object loads the specified media. The MRL is passed to loadMedia(), if the media + * is not a stream. The currentSourceChanged() signal + * is emitted. + * + * Supported media source types: + * \li local files + * \li URL + * \li discs (CD, DVD, VCD) + * \li capture devices (V4L) + * \li streams + * + * \param source The media source that will become the current source. + * + * \see loadMedia() + */ + void setSource(const MediaSource &source); + + /// Sets the media source that will replace the current one, after the playback for it finishes. + void setNextSource(const MediaSource &source); + + qint32 prefinishMark() const; + void setPrefinishMark(qint32 msecToEnd); + + qint32 transitionTime() const; + void setTransitionTime(qint32); + + void emitAboutToFinish(); + +signals: + // MediaController signals + void availableSubtitlesChanged(); + void availableAudioChannelsChanged(); + + void availableChaptersChanged(int); + void availableTitlesChanged(int); + + void chapterChanged(int chapterNumber); + void titleChanged(int titleNumber); + void durationChanged(qint64 newDuration); + + /** + * New widget size computed by VLC. + * + * It should be applied to the widget that contains the VLC video. + */ + void videoWidgetSizeChanged(int i_width, int i_height); + + void aboutToFinish(); + void bufferStatus(int percentFilled); + void currentSourceChanged(const MediaSource &newSource); + void finished(); + void hasVideoChanged(bool b_has_video); + void metaDataChanged(const QMultiMap & metaData); + void prefinishMarkReached(qint32 msecToEnd); + void seekableChanged(bool seekable); + void stateChanged(Phonon::State newState, Phonon::State oldState); + void tick(qint64 time); + void totalTimeChanged(qint64 newTotalTime); + + void moveToNext(); + +private slots: + /** + * If the new state is different from the current state, the current state is + * changed and the corresponding signal is emitted. + */ + void changeState(Phonon::State newState); + + /** + * Checks when the tick(), prefinishMarkReached(), aboutToFinish() signals need to + * be emitted and emits them if necessary. + * + * \param currentTime The current play time for the media, in miliseconds. + */ + void timeChanged(qint64 time); + void emitTick(qint64 time); + + /** + * If the next media source is valid, the current source is replaced and playback is commenced. + * The next source is set to an empty source. + * + * \see setNextSource() + */ + void moveToNextSource(); + + /*** Update media duration time - see comment in CPP */ + void updateDuration(qint64 newDuration); + + /** Retrieve meta data of a file (i.e ARTIST, TITLE, ALBUM, etc...). */ + void updateMetaData(); + void updateState(MediaPlayer::State state); + + /** Called when the availability of video output changed */ + void onHasVideoChanged(bool hasVideo); + + void setBufferStatus(int percent); + + /** Refreshes all MediaController descriptors if Video is present. */ + void refreshDescriptors(); + +private: + /** + * This method actually calls the functions needed to begin playing the media. + * If another media is already playing, it is discarded. The new media filename is set + * with loadMediaInternal(). A new VLC Media is created and set into the VLC Media Player. + * All the connected sink nodes are connected to the new media. It connects the media object + * to the events for the VLC Media, updates the meta-data, sets up the video widget id, and + * starts playing. + * + * \see loadMediaInternal() + * \see connectToMediaVLCEvents() + * \see updateMetaData() + * \see setVLCWidgetId() + */ + void setupMedia(); + + /** + * Seeks to the required position. If the state is not playing, the seek position is remembered. + */ + void seekInternal(qint64 milliseconds); + + bool hasNextTrack(); + + /** + * Changes the current state to buffering and sets the new current file. + * + * \param filename The MRL of the media source + */ + void loadMedia(const QByteArray &mrl); + + /** + * Overload for loadMedia, converting a QString to a QByteArray. + * + * \param filename The MRL of the media source + * + * \see loadMedia + */ + void loadMedia(const QString &mrl); + + /** + * Uninitializes the media + */ + void unloadMedia(); + + MediaSource m_nextSource; + + MediaSource m_mediaSource; + StreamReader *m_streamReader; + Phonon::State m_state; + + qint32 m_prefinishMark; + bool m_prefinishEmitted; + + bool m_aboutToFinishEmitted; + + qint32 m_tickInterval; + qint64 m_lastTick; + qint32 m_transitionTime; + + Media *m_media; + + qint64 m_totalTime; + QByteArray m_mrl; + QMultiMap m_vlcMetaData; + QList m_sinks; + + bool m_hasVideo; + bool m_isScreen; + + /** + * Workaround for being able to seek before VLC goes to playing state. + * Seeks before playing are stored in this var, and processed on state change + * to Playing. + */ + qint64 m_seekpoint; + + int m_timesVideoChecked; + + bool m_buffering; + Phonon::State m_stateAfterBuffering; +}; + +} // namespace VLC +} // namespace Phonon + +#endif // PHONON_VLC_MEDIAOBJECT_H diff --git a/src/mediaplayer.cpp b/src/mediaplayer.cpp new file mode 100644 index 0000000..0ae95d9 --- /dev/null +++ b/src/mediaplayer.cpp @@ -0,0 +1,413 @@ +/* + Copyright (C) 2011-2018 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "mediaplayer.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "utils/libvlc.h" +#include "media.h" + +// Callbacks come from a VLC thread. In some cases Qt fails to detect this and +// tries to invoke directly (i.e. from same thread). This can lead to thread +// pollution throughout Phonon, which is very much not desired. +#define P_EMIT_HAS_VIDEO(hasVideo) \ + QMetaObject::invokeMethod(\ + that, "hasVideoChanged", \ + Qt::QueuedConnection, \ + Q_ARG(bool, hasVideo)) + +#define P_EMIT_STATE(__state) \ + QMetaObject::invokeMethod(\ + that, "stateChanged", \ + Qt::QueuedConnection, \ + Q_ARG(MediaPlayer::State, __state)) + +namespace Phonon { +namespace VLC { + +MediaPlayer::MediaPlayer(QObject *parent) + : QObject(parent) + , m_media(0) + , m_player(libvlc_media_player_new(pvlc_libvlc)) + , m_doingPausedPlay(false) + , m_volume(75) + , m_fadeAmount(1.0f) +{ + Q_ASSERT(m_player); + + qRegisterMetaType("MediaPlayer::State"); + + libvlc_event_manager_t *manager = libvlc_media_player_event_manager(m_player); + libvlc_event_type_t events[] = { + libvlc_MediaPlayerMediaChanged, + libvlc_MediaPlayerNothingSpecial, + libvlc_MediaPlayerOpening, + libvlc_MediaPlayerBuffering, + libvlc_MediaPlayerPlaying, + libvlc_MediaPlayerPaused, + libvlc_MediaPlayerStopped, + libvlc_MediaPlayerForward, + libvlc_MediaPlayerBackward, + libvlc_MediaPlayerEndReached, + libvlc_MediaPlayerEncounteredError, + libvlc_MediaPlayerTimeChanged, + libvlc_MediaPlayerPositionChanged, + libvlc_MediaPlayerSeekableChanged, + libvlc_MediaPlayerPausableChanged, + libvlc_MediaPlayerTitleChanged, + libvlc_MediaPlayerSnapshotTaken, + libvlc_MediaPlayerLengthChanged, + libvlc_MediaPlayerVout + #if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(2, 2, 2, 0)) + , libvlc_MediaPlayerCorked, + libvlc_MediaPlayerUncorked, + libvlc_MediaPlayerMuted, + libvlc_MediaPlayerUnmuted, + libvlc_MediaPlayerAudioVolume + #endif + }; + const int eventCount = sizeof(events) / sizeof(*events); + for (int i = 0; i < eventCount; ++i) { + libvlc_event_attach(manager, events[i], event_cb, this); + } + + // Deactivate video title overlay (i.e. name of the video displaying + // at start. Since 2.1 that is handled via the API which in general is more + // reliable than setting it via libvlc_new (or so I have been told....) + libvlc_media_player_set_video_title_display(m_player, libvlc_position_disable, 0); +} + +MediaPlayer::~MediaPlayer() +{ + libvlc_media_player_release(m_player); +} + +void MediaPlayer::setMedia(Media *media) +{ + m_media = media; + libvlc_media_player_set_media(m_player, *m_media); +} + +bool MediaPlayer::play() +{ + m_doingPausedPlay = false; + return libvlc_media_player_play(m_player) == 0; +} + +void MediaPlayer::pause() +{ + m_doingPausedPlay = false; + libvlc_media_player_set_pause(m_player, 1); +} + +void MediaPlayer::pausedPlay() +{ + m_doingPausedPlay = true; + libvlc_media_player_play(m_player); +} + +void MediaPlayer::resume() +{ + m_doingPausedPlay = false; + libvlc_media_player_set_pause(m_player, 0); +} + +void MediaPlayer::togglePause() +{ + libvlc_media_player_pause(m_player); +} + +void MediaPlayer::stop() +{ + m_doingPausedPlay = false; + libvlc_media_player_stop(m_player); +} + +qint64 MediaPlayer::length() const +{ + return libvlc_media_player_get_length(m_player); +} + +qint64 MediaPlayer::time() const +{ + return libvlc_media_player_get_time(m_player); +} + +void MediaPlayer::setTime(qint64 newTime) +{ + libvlc_media_player_set_time(m_player, newTime); +} + +bool MediaPlayer::isSeekable() const +{ + return libvlc_media_player_is_seekable(m_player); +} + +bool MediaPlayer::hasVideoOutput() const +{ + return libvlc_media_player_has_vout(m_player) > 0; +} + +bool MediaPlayer::setSubtitle(int subtitle) +{ + return libvlc_video_set_spu(m_player, subtitle) == 0; +} + +bool MediaPlayer::setSubtitle(const QString &file) +{ +#if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(3, 0, 0, 0)) + return libvlc_media_player_add_slave(m_player, + libvlc_media_slave_type_subtitle, + file.toUtf8().data(), + true) == 0; +#else + return libvlc_video_set_subtitle_file(m_player, file.toUtf8().data()) == 1; +#endif +} + +void MediaPlayer::setTitle(int title) +{ + libvlc_media_player_set_title(m_player, title); +} + +void MediaPlayer::setChapter(int chapter) +{ + libvlc_media_player_set_chapter(m_player, chapter); +} + +QImage MediaPlayer::snapshot() const +{ + QTemporaryFile tempFile(QDir::tempPath() % QDir::separator() % QLatin1Literal("phonon-vlc-snapshot")); + tempFile.open(); + + // This function is sync. + if (libvlc_video_take_snapshot(m_player, 0, tempFile.fileName().toLocal8Bit().data(), 0, 0) != 0) + return QImage(); + + return QImage(tempFile.fileName()); +} + +bool MediaPlayer::setAudioTrack(int track) +{ + return libvlc_audio_set_track(m_player, track) == 0; +} + +void MediaPlayer::event_cb(const libvlc_event_t *event, void *opaque) +{ + MediaPlayer *that = reinterpret_cast(opaque); + Q_ASSERT(that); + + // Do not forget to register for the events you want to handle here! + switch (event->type) { + case libvlc_MediaPlayerTimeChanged: + QMetaObject::invokeMethod( + that, "timeChanged", + Qt::QueuedConnection, + Q_ARG(qint64, event->u.media_player_time_changed.new_time)); + break; + case libvlc_MediaPlayerSeekableChanged: + QMetaObject::invokeMethod( + that, "seekableChanged", + Qt::QueuedConnection, + Q_ARG(bool, event->u.media_player_seekable_changed.new_seekable)); + break; + case libvlc_MediaPlayerLengthChanged: + QMetaObject::invokeMethod( + that, "lengthChanged", + Qt::QueuedConnection, + Q_ARG(qint64, event->u.media_player_length_changed.new_length)); + break; + case libvlc_MediaPlayerNothingSpecial: + P_EMIT_STATE(NoState); + break; + case libvlc_MediaPlayerOpening: + P_EMIT_STATE(OpeningState); + break; + case libvlc_MediaPlayerBuffering: + QMetaObject::invokeMethod( + that, "bufferChanged", + Qt::QueuedConnection, + Q_ARG(int, event->u.media_player_buffering.new_cache)); + break; + case libvlc_MediaPlayerPlaying: + // Intercept state change and apply pausing once playing. + if (that->m_doingPausedPlay) { + that->m_doingPausedPlay = false; + // VLC internally will call stop if a player can not be paused, this + // can lead to deadlocks as stop is partially blocking, to avoid this + // we explicitly do a queued stop whenever a player can not be paused. + // In those situations pausedplay capability can not be provided, so + // applications wanting to do pausedplay will need to handle it anyway + // as faking a paused state when there is none would be a very code + // intense workaround asking for weird abstraction leakage. + // See kde bug 337604. + if (libvlc_media_player_can_pause(that->m_player)) { + that->pause(); + } else { + QMetaObject::invokeMethod(that, "stop", Qt::QueuedConnection); + } + } else + P_EMIT_STATE(PlayingState); + break; + case libvlc_MediaPlayerPaused: + P_EMIT_STATE(PausedState); + break; + case libvlc_MediaPlayerStopped: + P_EMIT_STATE(StoppedState); + break; + case libvlc_MediaPlayerEndReached: + P_EMIT_STATE(EndedState); + break; + case libvlc_MediaPlayerEncounteredError: + P_EMIT_STATE(ErrorState); + break; + case libvlc_MediaPlayerVout: + if (event->u.media_player_vout.new_count > 0) { + P_EMIT_HAS_VIDEO(true); + } else { + P_EMIT_HAS_VIDEO(false); + } + break; + case libvlc_MediaPlayerMediaChanged: + break; +#if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(2, 2, 2, 0)) + case libvlc_MediaPlayerCorked: + that->pause(); + break; + case libvlc_MediaPlayerUncorked: + that->play(); + break; + case libvlc_MediaPlayerMuted: + QMetaObject::invokeMethod( + that, "mutedChanged", + Qt::QueuedConnection, + Q_ARG(bool, true)); + break; + case libvlc_MediaPlayerUnmuted: + QMetaObject::invokeMethod( + that, "mutedChanged", + Qt::QueuedConnection, + Q_ARG(bool, false)); + break; + case libvlc_MediaPlayerAudioVolume: + QMetaObject::invokeMethod( + that, "volumeChanged", + Qt::QueuedConnection, + Q_ARG(float, event->u.media_player_audio_volume.volume)); + break; +#endif + case libvlc_MediaPlayerForward: + case libvlc_MediaPlayerBackward: + case libvlc_MediaPlayerPositionChanged: + case libvlc_MediaPlayerPausableChanged: + case libvlc_MediaPlayerTitleChanged: + case libvlc_MediaPlayerSnapshotTaken: // Snapshot call is sync, so this is useless. + default: + break; + QString msg = QString("Unknown event: ") + QString(libvlc_event_type_name(event->type)); + Q_ASSERT_X(false, "event_cb", qPrintable(msg)); + break; + } +} + +QDebug operator<<(QDebug dbg, const MediaPlayer::State &s) +{ + QString name; + switch (s) { + case MediaPlayer::NoState: + name = QLatin1String("MediaPlayer::NoState"); + break; + case MediaPlayer::OpeningState: + name = QLatin1String("MediaPlayer::OpeningState"); + break; + case MediaPlayer::BufferingState: + name = QLatin1String("MediaPlayer::BufferingState"); + break; + case MediaPlayer::PlayingState: + name = QLatin1String("MediaPlayer::PlayingState"); + break; + case MediaPlayer::PausedState: + name = QLatin1String("MediaPlayer::PausedState"); + break; + case MediaPlayer::StoppedState: + name = QLatin1String("MediaPlayer::StoppedState"); + break; + case MediaPlayer::EndedState: + name = QLatin1String("MediaPlayer::EndedState"); + break; + case MediaPlayer::ErrorState: + name = QLatin1String("MediaPlayer::ErrorState"); + break; + } + dbg.nospace() << "State(" << qPrintable(name) << ")"; + return dbg.space(); +} + +void MediaPlayer::setAudioFade(qreal fade) +{ + m_fadeAmount = fade; + setVolumeInternal(); +} + +void MediaPlayer::setAudioVolume(int volume) +{ + m_volume = volume; + setVolumeInternal(); +} + +bool MediaPlayer::mute() const +{ + return libvlc_audio_get_mute(m_player); +} + +void MediaPlayer::setMute(bool mute) +{ + libvlc_audio_set_mute(m_player, mute); +} + +void MediaPlayer::setVolumeInternal() +{ + libvlc_audio_set_volume(m_player, m_volume * m_fadeAmount); +} + +void MediaPlayer::setCdTrack(int track) +{ + if (!m_media) + return; + libvlc_media_player_stop(m_player); + m_media->setCdTrack(track); + libvlc_media_player_set_media(m_player, *m_media); + libvlc_media_player_play(m_player); +} + +#if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(2, 2, 0, 0)) +void MediaPlayer::setEqualizer(libvlc_equalizer_t *equalizer) +{ + libvlc_media_player_set_equalizer(m_player, equalizer); +} +#endif + +} // namespace VLC +} // namespace Phonon diff --git a/src/mediaplayer.h b/src/mediaplayer.h new file mode 100644 index 0000000..cd847db --- /dev/null +++ b/src/mediaplayer.h @@ -0,0 +1,277 @@ +/* + Copyright (C) 2011-2018 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef PHONON_VLC_MEDIAPLAYER_H +#define PHONON_VLC_MEDIAPLAYER_H + +#include +#include +#include + +#include +#include + +class QImage; +class QString; + +namespace Phonon { +namespace VLC { + +class Media; + +template +class Descriptions +{ +public: + typedef void (*ReleaseFunction) (VLCArray**, unsigned int) ; + + Descriptions(VLCArray **data, + unsigned int size, + ReleaseFunction release) + : m_release(release) + , m_data(data) + , m_size(size) + { + } + + ~Descriptions() + { + m_release(m_data, m_size); + } + + unsigned int size() const { return m_size; } + +private: + ReleaseFunction m_release; + + VLCArray **m_data; + unsigned int m_size; +}; + +#if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(3, 0, 0, 0)) +typedef Descriptions TitleDescriptions; +typedef QSharedPointer SharedTitleDescriptions; + +typedef Descriptions ChapterDescriptions; +typedef QSharedPointer SharedChapterDescriptions; +#endif + +class MediaPlayer : public QObject +{ + Q_OBJECT +public: + enum State { + NoState = 0, + OpeningState, + BufferingState, + PlayingState, + PausedState, + StoppedState, + EndedState, + ErrorState + }; + + explicit MediaPlayer(QObject *parent = 0); + ~MediaPlayer(); + + inline libvlc_media_player_t *libvlc_media_player() const { return m_player; } + inline operator libvlc_media_player_t *() const { return m_player; } + + void setMedia(Media *media); + + void setVideoCallbacks(); + void setVideoFormatCallbacks(); + + void setNsObject(void *drawable) { libvlc_media_player_set_nsobject(m_player, drawable); } + void setXWindow(quint32 drawable) { libvlc_media_player_set_xwindow(m_player, drawable); } + void setHwnd(void *drawable) { libvlc_media_player_set_hwnd(m_player, drawable); } + + // Playback + bool play(); + void pause(); + void pausedPlay(); + void resume(); + void togglePause(); + Q_INVOKABLE void stop(); + + qint64 length() const; + qint64 time() const; + void setTime(qint64 newTime); + + bool isSeekable() const; + + // Video + QSize videoSize() const + { + unsigned int width; + unsigned int height; + libvlc_video_get_size(m_player, 0, &width, &height); + return QSize(width, height); + } + + bool hasVideoOutput() const; + + /// Set new video aspect ratio. + /// \param aspect new video aspect-ratio or empty to reset to default + void setVideoAspectRatio(const QByteArray &aspect) + { libvlc_video_set_aspect_ratio(m_player, aspect.isEmpty() ? 0 : aspect.data()); } + + void setVideoAdjust(libvlc_video_adjust_option_t adjust, int value) + { libvlc_video_set_adjust_int(m_player, adjust, value); } + + void setVideoAdjust(libvlc_video_adjust_option_t adjust, float value) + { libvlc_video_set_adjust_float(m_player, adjust, value); } + + int subtitle() const + { return libvlc_video_get_spu(m_player); } + + libvlc_track_description_t *videoSubtitleDescription() const + { return libvlc_video_get_spu_description(m_player); } + + bool setSubtitle(int subtitle); + bool setSubtitle(const QString &file); + + int title() const + { return libvlc_media_player_get_title(m_player); } + + int titleCount() const + { return libvlc_media_player_get_title_count(m_player); } + +#if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(3, 0, 0, 0)) + SharedTitleDescriptions titleDescription() const + { + libvlc_title_description_t **data; + unsigned int size = + libvlc_media_player_get_full_title_descriptions(m_player, &data); + return SharedTitleDescriptions( + new TitleDescriptions( + data, size, + &libvlc_title_descriptions_release) + ); + } +#else // deprecated + libvlc_track_description_t *titleDescription() const + { return libvlc_video_get_title_description(m_player); } +#endif + + void setTitle(int title); + + int videoChapterCount() const + { return libvlc_media_player_get_chapter_count(m_player); } + +#if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(3, 0, 0, 0)) + SharedChapterDescriptions videoChapterDescription(int title) const + { + libvlc_chapter_description_t **data; + unsigned int size = + libvlc_media_player_get_full_chapter_descriptions(m_player, title, &data); + return SharedChapterDescriptions( + new ChapterDescriptions( + data, size, + &libvlc_chapter_descriptions_release) + ); + } +#else // deprecated + libvlc_track_description_t *videoChapterDescription(int title) const + { return libvlc_video_get_chapter_description(m_player, title); } +#endif + + void setChapter(int chapter); + + /** Reentrant, through libvlc */ + QImage snapshot() const; + + // Audio + /// Get current audio volume. + /// \return the software volume in percents (0 = mute, 100 = nominal / 0dB) + int audioVolume() const { return m_volume; } + + /// Set new audio volume. + /// \param volume new volume + void setAudioVolume(int volume); + + /// \return mutness + bool mute() const; + + /** + * Mutes + * @param mute whether to mute or unmute + */ + void setMute(bool mute); + + /// Set the fade percentage, between 0 (muted) and 1.0 (no fade) + void setAudioFade(qreal fade); + + /// \param name name of the output to set + /// \returns \c true when setting was successful, \c false otherwise + bool setAudioOutput(const QByteArray &name) + { return libvlc_audio_output_set(m_player, name.data()) == 0; } + + /** + * Set audio output device by name. + * \param outputName the aout name (pulse, alsa, oss, etc.) + * \param deviceName the output name (aout dependent) + */ + void setAudioOutputDevice(const QByteArray &outputName, const QByteArray &deviceName) + { libvlc_audio_output_device_set(m_player, outputName.data(), deviceName.data()); } + + int audioTrack() const + { return libvlc_audio_get_track(m_player); } + + libvlc_track_description_t * audioTrackDescription() const + { return libvlc_audio_get_track_description(m_player); } + + bool setAudioTrack(int track); + + void setCdTrack(int track); + +#if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(2, 2, 0, 0)) + void setEqualizer(libvlc_equalizer_t *equalizer); +#endif + +signals: + void lengthChanged(qint64 length); + void seekableChanged(bool seekable); + void stateChanged(MediaPlayer::State state); + void timeChanged(qint64 time); + void bufferChanged(int percent); + + /** Emitted when the vout availability has changed */ + void hasVideoChanged(bool hasVideo); + + void mutedChanged(bool mute); + void volumeChanged(float volume); + +private: + static void event_cb(const libvlc_event_t *event, void *opaque); + void setVolumeInternal(); + + Media *m_media; + + libvlc_media_player_t *m_player; + + bool m_doingPausedPlay; + int m_volume; + qreal m_fadeAmount; +}; + +QDebug operator<<(QDebug dbg, const MediaPlayer::State &s); + +} // namespace VLC +} // namespace Phonon + +#endif // PHONON_VLC_MEDIAPLAYER_H diff --git a/src/phonon-vlc.json.in b/src/phonon-vlc.json.in new file mode 100644 index 0000000..fd16e31 --- /dev/null +++ b/src/phonon-vlc.json.in @@ -0,0 +1,9 @@ +{ + "Name": "Phonon VLC", + "Icon": "vlc", + "Version": "@PHONON_VLC_VERSION@", + "Website": "http://www.videolan.org", + + "InterfaceVersion": 0, + "InitialPreference": 20 +} diff --git a/src/sinknode.cpp b/src/sinknode.cpp new file mode 100644 index 0000000..551e4b1 --- /dev/null +++ b/src/sinknode.cpp @@ -0,0 +1,81 @@ +/* + Copyright (C) 2013 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "sinknode.h" + +#include "utils/debug.h" +#include "mediaobject.h" +#include "mediaplayer.h" + +namespace Phonon { +namespace VLC { + +SinkNode::SinkNode() + : m_mediaObject(0) + , m_player(0) +{ +} + +SinkNode::~SinkNode() +{ + if (m_mediaObject) { + disconnectFromMediaObject(m_mediaObject); + } +} + +void SinkNode::connectToMediaObject(MediaObject *mediaObject) +{ + if (m_mediaObject) { + error() << Q_FUNC_INFO << "m_mediaObject already connected"; + } + + m_mediaObject = mediaObject; + m_player = mediaObject->m_player; + m_mediaObject->addSink(this); + + // ---> Global handling goes here! Above the derivee handle! <--- // + + handleConnectToMediaObject(mediaObject); +} + +void SinkNode::disconnectFromMediaObject(MediaObject *mediaObject) +{ + handleDisconnectFromMediaObject(mediaObject); + + // ---> Global handling goes here! Below the derivee handle! <--- // + + if (m_mediaObject != mediaObject) { + error() << Q_FUNC_INFO << "SinkNode was not connected to mediaObject"; + } + + if (m_mediaObject) { + m_mediaObject->removeSink(this); + } + + m_mediaObject = 0; + m_player = 0; +} + +void SinkNode::addToMedia(Media *media) +{ + // ---> Global handling goes here! Above the derivee handle! <--- // + + handleAddToMedia(media); +} + +} // namespace VLC +} // namespace Phonon diff --git a/src/sinknode.h b/src/sinknode.h new file mode 100644 index 0000000..ed20fc9 --- /dev/null +++ b/src/sinknode.h @@ -0,0 +1,102 @@ +/* + Copyright (C) 2013 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef PHONON_VLC_SINKNODE_H +#define PHONON_VLC_SINKNODE_H + +#include + +namespace Phonon { +namespace VLC { + +class Media; +class MediaObject; +class MediaPlayer; + +/** \brief The sink node is essentialy an output for a media object + * + * This class handles connections for the sink to a media object. It remembers + * the media object and the libVLC media player associated with it. + * + * \see MediaObject + */ +class SinkNode +{ +public: + SinkNode(); + virtual ~SinkNode(); + + /** + * Associates the sink node to the provided media object. The m_mediaObject and m_vlcPlayer + * attributes are set, and the sink is added to the media object's sinks. + * + * \param mediaObject The media object to connect to. + * + * \see disconnectFromMediaObject() + */ + void connectToMediaObject(MediaObject *mediaObject); + + /** + * Removes this sink from the specified media object's sinks. + * + * \param mediaObject The media object to disconnect from + * + * \see connectToMediaObject() + */ + void disconnectFromMediaObject(MediaObject *mediaObject); + + /** + * Does nothing. To be reimplemented in child classes. + */ + void addToMedia(Media *media); + +protected: + /** + * Handling function for derived classes. + * \note This handle is executed *after* the global handle. + * Meaning the SinkNode base will be done handling the connect. + * \see connectToMediaObject + */ + virtual void handleConnectToMediaObject(MediaObject *mediaObject) { Q_UNUSED(mediaObject); } + + /** + * Handling function for derived classes. + * \note This handle is executed *before* the global handle. + * Meaning the SinkNode base will continue handling the disconnect. + * \see disconnectFromMediaObject + */ + virtual void handleDisconnectFromMediaObject(MediaObject *mediaObject) { Q_UNUSED(mediaObject); } + + /** + * Handling function for derived classes. + * \note This handle is executed *after* the global handle. + * Meaning the SinkNode base will be done handling the connect. + * \see addToMedia + */ + virtual void handleAddToMedia(Media *media) { Q_UNUSED(media); } + + /** Available while connected to a MediaObject (until disconnected) */ + QPointer m_mediaObject; + + /** Available while connected to a MediaObject (until disconnected) */ + MediaPlayer *m_player; +}; + +} // namespace VLC +} // namespace Phonon + +#endif // PHONON_VLC_SINKNODE_H diff --git a/src/streamreader.cpp b/src/streamreader.cpp new file mode 100644 index 0000000..03a3f42 --- /dev/null +++ b/src/streamreader.cpp @@ -0,0 +1,242 @@ +/* + Copyright (C) 2007-2008 Tanguy Krotoff + Copyright (C) 2008 Lukas Durfina + Copyright (C) 2009 Fathi Boudra + Copyright (C) 2009-2011 vlc-phonon AUTHORS + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "streamreader.h" + +#include + +#include + +#include "utils/debug.h" +#include "media.h" +#include "mediaobject.h" +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + +namespace Phonon { +namespace VLC { + +#define BLOCKSIZE 32768 + +StreamReader::StreamReader(MediaObject *parent) + : QObject(parent) + , m_pos(0) + , m_size(0) + , m_eos(false) + , m_seekable(false) + , m_unlocked(false) + , m_mediaObject(parent) +{ +} + +StreamReader::~StreamReader() +{ +} + +void StreamReader::addToMedia(Media *media) +{ + lock(); // Make sure we can lock in read(). + + media->addOption(QLatin1String("imem-cat=4")); + media->addOption(QLatin1String("imem-data="), INTPTR_PTR(this)); + media->addOption(QLatin1String("imem-get="), INTPTR_FUNC(readCallback)); + media->addOption(QLatin1String("imem-release="), INTPTR_FUNC(readDoneCallback)); + media->addOption(QLatin1String("imem-seek="), INTPTR_FUNC(seekCallback)); + + // if stream has known size, we may pass it + // imem module will use it and pass it to demux + if (streamSize() > 0) { + media->addOption(QString("imem-size=%1").arg(streamSize())); + } +} + +void StreamReader::lock() +{ + QMutexLocker lock(&m_mutex); + DEBUG_BLOCK; + m_unlocked = false; +} + +void StreamReader::unlock() +{ + QMutexLocker lock(&m_mutex); + DEBUG_BLOCK; + m_unlocked = true; + m_waitingForData.wakeAll(); +} + +int StreamReader::readCallback(void *data, const char *cookie, + int64_t *dts, int64_t *pts, unsigned *flags, // krazy:exclude=typedefs + size_t *bufferSize, void **buffer) +{ + Q_UNUSED(cookie); + Q_UNUSED(dts); + Q_UNUSED(pts); + Q_UNUSED(flags); + + StreamReader *that = static_cast(data); + size_t length = BLOCKSIZE; + + *buffer = new char[length]; + + int size = length; + bool ret = that->read(that->currentPos(), &size, static_cast(*buffer)); + + *bufferSize = static_cast(size); + + return ret ? 0 : -1; +} + +int StreamReader::readDoneCallback(void *data, const char *cookie, + size_t bufferSize, void *buffer) +{ + Q_UNUSED(data); + Q_UNUSED(cookie); + Q_UNUSED(bufferSize); + delete[] static_cast(buffer); + return 0; +} + +int StreamReader::seekCallback(void *data, const uint64_t pos) +{ + StreamReader *that = static_cast(data); + if (static_cast(pos) > that->streamSize()) { // krazy:exclude=typedefs + // attempt to seek past the end of our data. + return -1; + } + + that->setCurrentPos(pos); + // this should return a true/false, but it doesn't, so assume success. + + return 0; +} + +quint64 StreamReader::currentBufferSize() const +{ + return m_buffer.size(); +} + +bool StreamReader::read(quint64 pos, int *length, char *buffer) +{ + QMutexLocker lock(&m_mutex); + DEBUG_BLOCK; + bool ret = true; + + if (m_unlocked) { + return ret; + } + + if (currentPos() != pos) { + if (!streamSeekable()) { + return false; + } + setCurrentPos(pos); + } + + if (m_buffer.capacity() < *length) { + m_buffer.reserve(*length); + } + + while (currentBufferSize() < static_cast(*length)) { + quint64 oldSize = currentBufferSize(); + needData(); + + m_waitingForData.wait(&m_mutex); + + if (oldSize == currentBufferSize()) { + if (m_eos && m_buffer.isEmpty()) { + return false; + } + // We didn't get any more data + *length = static_cast(oldSize); + // If we have some data to return, why tell to reader that we failed? + // Remember that length argument is more like maxSize not requiredSize + ret = true; + } + } + + if (m_mediaObject->state() != Phonon::BufferingState && + m_mediaObject->state() != Phonon::LoadingState) { + enoughData(); + } + + memcpy(buffer, m_buffer.data(), *length); + m_pos += *length; + // trim the buffer by the amount read + m_buffer = m_buffer.mid(*length); + + return ret; +} + +void StreamReader::endOfData() +{ + m_eos = true; + m_waitingForData.wakeAll(); +} + +void StreamReader::writeData(const QByteArray &data) +{ + QMutexLocker lock(&m_mutex); + DEBUG_BLOCK; + m_buffer.append(data); + m_waitingForData.wakeAll(); +} + +quint64 StreamReader::currentPos() const +{ + return m_pos; +} + +void StreamReader::setCurrentPos(qint64 pos) +{ + QMutexLocker lock(&m_mutex); + m_pos = pos; + m_buffer.clear(); // Not optimal, but meh + + // Do not touch m_size here, it reflects the size of the stream not the size of the buffer, + // and generally seeking does not change the size! + + seekStream(pos); +} + +void StreamReader::setStreamSize(qint64 newSize) +{ + m_size = newSize; +} + +qint64 StreamReader::streamSize() const +{ + return m_size; +} + +void StreamReader::setStreamSeekable(bool seekable) +{ + m_seekable = seekable; + emit streamSeekableChanged(seekable); +} + +bool StreamReader::streamSeekable() const +{ + return m_seekable; +} + +} // namespace VLC +} // namespace Phonon + +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM diff --git a/src/streamreader.h b/src/streamreader.h new file mode 100644 index 0000000..f8a565a --- /dev/null +++ b/src/streamreader.h @@ -0,0 +1,121 @@ +/* + Copyright (C) 2007-2008 Tanguy Krotoff + Copyright (C) 2008 Lukas Durfina + Copyright (C) 2009 Fathi Boudra + Copyright (C) 2009-2011 vlc-phonon AUTHORS + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef PHONON_STREAMREADER_H +#define PHONON_STREAMREADER_H + +#include +#include + +#include + +#include +#include + +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + +namespace Phonon +{ + +class MediaSource; + +namespace VLC +{ + +class Media; +class MediaObject; + +/** \brief Class for supporting custom data streams to the backend + * + * This class receives data from a Phonon MediaSource that is a stream. + * When data is requested, it fetches it from the media source and passes it further. + * MediaObject uses this class to pass stream data to libVLC. + * + * It implements Phonon::StreamInterface, necessary for the connection with an + * Phonon::AbstractMediaStream owned by the Phonon::MediaSource. See the Phonon + * documentation for details. + * + * There are callbacks implemented in streamhooks.cpp, for libVLC. + */ +class StreamReader : public QObject, public Phonon::StreamInterface +{ + Q_OBJECT + Q_INTERFACES(Phonon::StreamInterface) +public: + explicit StreamReader(MediaObject *parent); + ~StreamReader(); + + void addToMedia(Media *media); + + void lock(); + void unlock(); + + static int readCallback(void *data, const char *cookie, + int64_t *dts, int64_t *pts, unsigned *flags, // krazy:exclude=typedefs + size_t *bufferSize, void **buffer); + + static int readDoneCallback(void *data, const char *cookie, + size_t bufferSize, void *buffer); + + static int seekCallback(void *data, const uint64_t pos); + + quint64 currentBufferSize() const; + void writeData(const QByteArray &data); + quint64 currentPos() const; + void setCurrentPos(qint64 pos); + + /** + * Requests data from this stream. The stream requests data from the + * Phonon::MediaSource's abstract media stream with the needData() signal. + * If the requested data is available, it is copied into the buffer. + * + * \param pos Position in the stream + * \param length Length of the data requested + * \param buffer A buffer to put the data + */ + bool read(quint64 offset, int *length, char *buffer); + + void endOfData(); + void setStreamSize(qint64 newSize); + qint64 streamSize() const; + void setStreamSeekable(bool seekable); + bool streamSeekable() const; + +signals: + void streamSeekableChanged(bool seekable); + +protected: + QByteArray m_buffer; + quint64 m_pos; + quint64 m_size; + bool m_eos; + bool m_seekable; + bool m_unlocked; + QMutex m_mutex; + QWaitCondition m_waitingForData; + MediaObject *m_mediaObject; +}; + +} +} + +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + +#endif // PHONON_STREAMREADER_H diff --git a/src/utils/debug.cpp b/src/utils/debug.cpp new file mode 100644 index 0000000..5c672e4 --- /dev/null +++ b/src/utils/debug.cpp @@ -0,0 +1,232 @@ +/* + Copyright (c) 2003-2005 Max Howell + Copyright (c) 2007-2009 Mark Kretschmann + Copyright (c) 2010 Kevin Funk + Copyright (c) 2011 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "debug.h" +#include "debug_p.h" + +#include +#include +#include + +#ifdef Q_OS_UNIX +# include +#endif + +// Define Application wide prefix +#ifndef APP_PREFIX +#define APP_PREFIX QLatin1String( "PHONON-VLC" ) +#endif + +#define DEBUG_INDENT_OBJECTNAME QLatin1String("Debug_Indent_object") + +QMutex Debug::mutex( QMutex::Recursive ); + +using namespace Debug; + +static bool s_debugColorsEnabled = true; +static DebugLevel s_debugLevel = DEBUG_NONE; + +IndentPrivate::IndentPrivate(QObject* parent) + : QObject(parent) +{ + setObjectName( DEBUG_INDENT_OBJECTNAME ); +} + +/** + * We can't use a statically instantiated QString for the indent, because + * static namespaces are unique to each dlopened library. So we piggy back + * the QString on the KApplication instance + */ +IndentPrivate* IndentPrivate::instance() +{ + QObject* qOApp = reinterpret_cast(qApp); + QObject* obj = qOApp ? qOApp->findChild( DEBUG_INDENT_OBJECTNAME ) : 0; + return (obj ? static_cast( obj ) : new IndentPrivate( qApp )); +} + +/* + Text color codes (use last digit here) + 30=black 31=red 32=green 33=yellow 34=blue 35=magenta 36=cyan 37=white +*/ +static int s_colors[] = { 1, 2, 4, 5, 6 }; // no yellow and white for sanity +static int s_colorIndex = 0; + +static QString toString( DebugLevel level ) +{ + switch( level ) + { + case DEBUG_WARN: + return "[WARNING]"; + case DEBUG_ERROR: + return "[ERROR__]"; + case DEBUG_FATAL: + return "[FATAL__]"; + default: + return QString(); + } +} + +static int toColor( DebugLevel level ) +{ + switch( level ) { + case DEBUG_WARN: + return 3; // red + case DEBUG_ERROR: + case DEBUG_FATAL: + return 1; // yellow + default: + return 0; // default: black + } +} + +static QString colorize( const QString &text, int color = s_colorIndex ) +{ + if( !debugColorEnabled() ) + return text; + + return QString( "\x1b[00;3%1m%2\x1b[00;39m" ).arg( QString::number(s_colors[color]), text ); +} + +static QString reverseColorize( const QString &text, int color ) +{ + if( !debugColorEnabled() ) + return text; + + return QString( "\x1b[07;3%1m%2\x1b[00;39m" ).arg( QString::number(color), text ); +} + +QString Debug::indent() +{ + return IndentPrivate::instance()->m_string; +} + +bool Debug::debugEnabled() +{ + return s_debugLevel < DEBUG_NONE; +} + +bool Debug::debugColorEnabled() +{ + return s_debugColorsEnabled; +} + +DebugLevel Debug::minimumDebugLevel() +{ + return s_debugLevel; +} + +void Debug::setColoredDebug( bool enable ) +{ + s_debugColorsEnabled = enable; +} + +void Debug::setMinimumDebugLevel(DebugLevel level) +{ + s_debugLevel = level; +} + +QDebug Debug::dbgstream( DebugLevel level ) +{ + if ( level < s_debugLevel ) + return nullDebug(); + + mutex.lock(); + const QString currentIndent = indent(); + mutex.unlock(); + + QString text = QString("%1%2").arg( APP_PREFIX ).arg( currentIndent ); + if ( level > DEBUG_INFO ) + text.append( ' ' + reverseColorize( toString(level), toColor( level ) ) ); + + return QDebug( QtDebugMsg ) << qPrintable( text ); +} + +void Debug::perfLog( const QString &message, const QString &func ) +{ +#ifdef Q_OS_UNIX + if( !debugEnabled() ) + return; + + QString str = QString( "MARK: %1: %2 %3" ).arg( qApp->applicationName(), func, message ); + access( str.toLocal8Bit().data(), F_OK ); +#endif +} + +Block::Block( const char *label ) + : m_label( label ) + , m_color( s_colorIndex ) +{ + if( !debugEnabled() || DEBUG_INFO < s_debugLevel) + return; + +#if QT_VERSION >= 0x040700 + m_startTime.start(); +#else + m_startTime = QTime::currentTime(); +#endif + + mutex.lock(); + s_colorIndex = (s_colorIndex + 1) % 5; + dbgstream() + << qPrintable( colorize( QLatin1String( "BEGIN:" ), m_color ) ) + << m_label; + IndentPrivate::instance()->m_string += QLatin1String(" "); + mutex.unlock(); +} + +Block::~Block() +{ + if( !debugEnabled() || DEBUG_INFO < s_debugLevel) + return; + +#if QT_VERSION >= 0x040700 + const double duration = m_startTime.elapsed() / 1000.0; +#else + const double duration = (double)m_startTime.msecsTo( QTime::currentTime() ) / 1000.0; +#endif + + mutex.lock(); + IndentPrivate::instance()->m_string.truncate( Debug::indent().length() - 2 ); + mutex.unlock(); + + // Print timing information, and a special message (DELAY) if the method took longer than 5s + if( duration < 5.0 ) + { + dbgstream() + << qPrintable( colorize( QLatin1String( "END__:" ), m_color ) ) + << m_label + << qPrintable( colorize( QString( "[Took: %3s]") + .arg( QString::number(duration, 'g', 2) ), m_color ) ); + } + else + { + dbgstream() + << qPrintable( colorize( QString( "END__:" ), m_color ) ) + << m_label + << qPrintable( reverseColorize( QString( "[DELAY Took (quite long) %3s]") + .arg( QString::number(duration, 'g', 2) ), toColor( DEBUG_WARN ) ) ); + } +} + +void Debug::stamp() +{ + static int n = 0; + debug() << "| Stamp: " << ++n << endl; +} diff --git a/src/utils/debug.h b/src/utils/debug.h new file mode 100644 index 0000000..365251d --- /dev/null +++ b/src/utils/debug.h @@ -0,0 +1,200 @@ +/* + Copyright (c) 2003-2005 Max Howell + Copyright (c) 2007-2009 Mark Kretschmann + Copyright (c) 2010 Kevin Funk + Copyright (c) 2011 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef PHONON_DEBUG_H +#define PHONON_DEBUG_H + +// We always want debug output available at runtime +#undef QT_NO_DEBUG_OUTPUT +#undef KDE_NO_DEBUG_OUTPUT + +#include +#include + +#if QT_VERSION >= 0x040700 +# include +#else +# include +#endif + +// Platform specific macros +#ifdef _WIN32 // krazy:exclude=cpp we really want to check a compiler feature here :P +#define __PRETTY_FUNCTION__ __FUNCTION__ +#endif +#ifdef __SUNPRO_CC +#define __PRETTY_FUNCTION__ __FILE__ +#endif + +/** + * @namespace Debug + * @short kdebug with indentation functionality and convenience macros + * @author Max Howell + * + * Usage: + * + * #define DEBUG_PREFIX "Blah" + * #include "debug.h" + * + * void function() + * { + * Debug::Block myBlock( __PRETTY_FUNCTION__ ); + * + * debug() << "output1" << endl; + * debug() << "output2" << endl; + * } + * + * Will output: + * + * app: BEGIN: void function() + * app: [Blah] output1 + * app: [Blah] output2 + * app: END: void function(): Took 0.1s + * + * @see Block + * @see CrashHelper + * @see ListStream + */ +namespace Debug +{ + extern QMutex mutex; + + enum DebugLevel { + DEBUG_INFO = 0, + DEBUG_WARN = 1, + DEBUG_ERROR = 2, + DEBUG_FATAL = 3, + DEBUG_NONE = 4 + }; + + QDebug dbgstream( DebugLevel level = DEBUG_INFO ); + bool debugEnabled(); + bool debugColorEnabled(); + DebugLevel minimumDebugLevel(); + void setColoredDebug( bool enable ); + void setMinimumDebugLevel( DebugLevel level ); + QString indent(); + + static inline QDebug dbgstreamwrapper( DebugLevel level ) { return dbgstream( level ); } + + static inline QDebug debug() { return dbgstreamwrapper( DEBUG_INFO ); } + static inline QDebug warning() { return dbgstreamwrapper( DEBUG_WARN ); } + static inline QDebug error() { return dbgstreamwrapper( DEBUG_ERROR ); } + static inline QDebug fatal() { return dbgstreamwrapper( DEBUG_FATAL ); } + + void perfLog( const QString &message, const QString &func ); +} + +using Debug::debug; +using Debug::warning; +using Debug::error; +using Debug::fatal; + +/// Standard function announcer +#define DEBUG_FUNC_INFO { Debug::mutex.lock(); qDebug() << Debug::indent() ; Debug::mutex.unlock(); } + +/// Announce a line +#define DEBUG_LINE_INFO { Debug::mutex.lock(); qDebug() << Debug::indent() << "Line: " << __LINE__; Debug::mutex.unlock(); } + +/// Convenience macro for making a standard Debug::Block +#define DEBUG_BLOCK Debug::Block uniquelyNamedStackAllocatedStandardBlock( __PRETTY_FUNCTION__ ); + +/// Performance logging +#define PERF_LOG( msg ) { Debug::perfLog( msg, __PRETTY_FUNCTION__ ); } + +class BlockPrivate; + +namespace Debug +{ + /** + * @class Debug::Block + * @short Use this to label sections of your code + * + * Usage: + * + * void function() + * { + * Debug::Block myBlock( "section" ); + * + * debug() << "output1" << endl; + * debug() << "output2" << endl; + * } + * + * Will output: + * + * app: BEGIN: section + * app: [prefix] output1 + * app: [prefix] output2 + * app: END: section - Took 0.1s + * + */ + class Block + { + public: + explicit Block( const char *name ); + ~Block(); + + private: +#if QT_VERSION >= 0x040700 + QElapsedTimer m_startTime; +#else + QTime m_startTime; +#endif + const char *m_label; + int m_color; + }; + + /** + * @name Debug::stamp() + * @short To facilitate crash/freeze bugs, by making it easy to mark code that has been processed + * + * Usage: + * + * { + * Debug::stamp(); + * function1(); + * Debug::stamp(); + * function2(); + * Debug::stamp(); + * } + * + * Will output (assuming the crash occurs in function2() + * + * app: Stamp: 1 + * app: Stamp: 2 + * + */ + void stamp(); +} + +#include + +namespace Debug +{ + /** + * @class Debug::List + * @short You can pass anything to this and it will output it as a list + * + * debug() << (Debug::List() << anInt << aString << aQStringList << aDouble) << endl; + */ + + typedef QList List; +} + +#endif diff --git a/src/utils/debug_p.h b/src/utils/debug_p.h new file mode 100644 index 0000000..b27da23 --- /dev/null +++ b/src/utils/debug_p.h @@ -0,0 +1,57 @@ +/* + Copyright (c) 2010 Kevin Funk + Copyright (c) 2011 Casian Andrei + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef DEBUGPRIVATE_H +#define DEBUGPRIVATE_H + +#include "debug.h" + +#include + +class IndentPrivate + : public QObject +{ +private: + explicit IndentPrivate(QObject* parent = 0); + +public: + static IndentPrivate* instance(); + + QString m_string; +}; + +/* + * From kdelibs/kdecore/io + */ +class NoDebugStream: public QIODevice +{ + // Q_OBJECT +public: + NoDebugStream() { open(WriteOnly); } + bool isSequential() const { return true; } + qint64 readData(char *, qint64) { return 0; /* eof */ } + qint64 readLineData(char *, qint64) { return 0; /* eof */ } + qint64 writeData(const char *, qint64 len) { return len; } +} devnull; + +QDebug nullDebug() +{ + return QDebug(&devnull); +} + +#endif // DEBUGPRIVATE_H diff --git a/src/utils/libvlc.cpp b/src/utils/libvlc.cpp new file mode 100644 index 0000000..3a33d9a --- /dev/null +++ b/src/utils/libvlc.cpp @@ -0,0 +1,128 @@ +/* + Copyright (C) 2011-2012 vlc-phonon AUTHORS + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "libvlc.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "debug.h" + +LibVLC *LibVLC::self; + +LibVLC::LibVLC() + : m_vlcInstance(0) +{ +} + +LibVLC::~LibVLC() +{ + if (m_vlcInstance) + libvlc_release(m_vlcInstance); + self = 0; +} + +bool LibVLC::init() +{ + Q_ASSERT_X(!self, "LibVLC", "there should be only one LibVLC object"); + LibVLC::self = new LibVLC; + + QList args; + + // Ends up as something like $HOME/.config/Phonon/vlc.conf + const QString configFileName = QSettings("Phonon", "vlc").fileName(); + if (QFile::exists(configFileName)) { + args << QByteArray("--config=").append(QFile::encodeName(configFileName)); + args << "--no-ignore-config"; + } + + int debugLevel = qgetenv("PHONON_SUBSYSTEM_DEBUG").toInt(); + if (debugLevel > 0) { + args << QByteArray("--verbose=").append(QByteArray::number(debugLevel)); + args << QByteArray("--extraintf=logger"); +#ifdef Q_WS_WIN + QDir logFilePath(QString(qgetenv("APPDATA")).append("/vlc")); +#else + QDir logFilePath(QDir::homePath().append("/.vlc")); +#endif //Q_WS_WIN + logFilePath.mkdir("log"); + const QString logFile = logFilePath.path() + .append("/log/vlc-log-") + .append(QString::number(qApp->applicationPid())) + .append(".txt"); + args << QByteArray("--logfile=").append(QFile::encodeName(QDir::toNativeSeparators(logFile))); + } + + args << "--no-media-library"; + args << "--no-osd"; + args << "--no-stats"; + // By default VLC will put a picture-in-picture when making a snapshot. + // This is unexpected behaviour for us, so we force it off. + args << "--no-snapshot-preview"; + // Do not load xlib dependent modules as we cannot ensure proper init + // order as expected by xlib thus leading to crashes. + // KDE BUG: 240001 + args << "--no-xlib"; + // Do not preload services discovery modules, we don't use them. + args << "--services-discovery=''"; + // The application is meant to manage this. Also, using the builtin + // inhibitor may cause problems on shutdown if VLC tries to uninhibit too + // late in the application lifecycle. + args << "--no-disable-screensaver"; + // Allow multiple starts (one gets to wonder whether that makes a difference). +#if !defined(Q_OS_MAC) && (defined(Q_OS_WIN) || !defined(PHONON_NO_DBUS)) + args << "--no-one-instance"; +#endif + args << "--no-audio"; + args << "--no-video"; + // 6 seconds disk read buffer (up from vlc 2.1 default of 300ms) when using alsa, prevents most buffer underruns + // when the disk is very busy. We expect the pulse buffer after decoding to solve the same problem. + Phonon::PulseSupport *pulse = Phonon::PulseSupport::getInstance(); + if (!pulse || !pulse->isActive()) { + args << "--file-caching=6000"; + } + + // Build const char* array + QVarLengthArray vlcArgs(args.size()); + for (int i = 0; i < args.size(); ++i) { + vlcArgs[i] = args.at(i).constData(); + } + + // Create and initialize a libvlc instance (it should be done only once) + self->m_vlcInstance = libvlc_new(vlcArgs.size(), vlcArgs.constData()); + if (!self->m_vlcInstance) { + fatal() << "libVLC: could not initialize"; + return false; + } + return true; +} + +const char *LibVLC::errorMessage() +{ + return libvlc_errmsg(); +} diff --git a/src/utils/libvlc.h b/src/utils/libvlc.h new file mode 100644 index 0000000..a034afd --- /dev/null +++ b/src/utils/libvlc.h @@ -0,0 +1,140 @@ +/* + Copyright (C) 2011 vlc-phonon AUTHORS + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef LIBVLC_H +#define LIBVLC_H + +#include +#include + +#include + +struct libvlc_instance_t; + +/** + * Convenience macro accessing the vlc_instance_t via LibVLC::self. + * Please note that init() must have been called whenever using this, as no + * checking of self is conducted (i.e. can be null). + */ +#define pvlc_libvlc LibVLC::self->vlc() + +/** + * Foreach loop macro for VLC descriptions. + * + * For this macro to work the type name must be of the form: + * \verbatim libvlc_FOO_t \endverbatim + * * + * \param type the type identifier of VLC (without libvlc and _t) + * \param variable the variable name you want to use + * \param getter the getter from which to get the iterator + * \param releaser, function name to release the list + */ +#define VLC_FOREACH(type, variable, getter, releaser) \ + for (libvlc_##type##_t *__libvlc_first_element = getter, *variable = __libvlc_first_element; \ + variable; \ + variable = variable->p_next, !variable ? releaser(__libvlc_first_element) : (void)0) + +// This foreach expects only a type and variable because getter and releaser are generic. +// Also the type is in short form i.e. libvlc_foo_t would be foo. +#define VLC_FOREACH_LIST(type, variable) VLC_FOREACH(type, variable, libvlc_##type##_list_get(pvlc_libvlc), libvlc_##type##_list_release) + +// These foreach expect no type because the type is generic, they do however +// expect a getter to allow usage with our wrapper classes and since the getter +// will most likely not be generic. +// For instance libvlc_audio_get_track_description returns a generic +// libvlc_track_description_t pointer. So the specific audio_track function +// relates to the generic track description type. +#define VLC_FOREACH_TRACK(variable, getter) VLC_FOREACH(track_description, variable, getter, libvlc_track_description_list_release) +#define VLC_FOREACH_MODULE(variable, getter) VLC_FOREACH(module_description, variable, getter, libvlc_module_description_list_release) + +/** + * \brief Singleton class containing a libvlc instance. + * + * This class is a convenience class implementing the singleton pattern to hold + * an instance of libvlc. This instance is necessary to call various libvlc + * functions (such as creating a new mediaplayer instance). + * + * To initialize the object call init(), this will create the actualy LibVLC + * instance and then try to initialize the libvlc instance itself. + * init() returns false in case the libvlc instance could not be created. + * + * For convenience reasons there is also a libvlc macro which gets the LibVLC + * instance and then the libvlc_instance_t form that. Note that this macro + * does not check whether LibVLC actually got initialized, so it should only + * be used when you can be absolutely sure that init() was already called + * + * \code + * LibVLC::init(0); // init LibVLC + * if (!LibVLC::self) { + * exit(1); // error if self is null + * } + * libvlc_media_player_new(libvlc); // use libvlc macro + * \endcode + * + * \author Harald Sitter org> + */ +class LibVLC +{ +public: + /** + * The singleton itself. Beware that this returns 0 unless init was called. + * + * \returns LibVLC instance or 0 if there is none. + * + * \see init + */ + static LibVLC *self; + + /** + * \returns the contained libvlc instance. + */ + libvlc_instance_t *vlc() + { + return m_vlcInstance; + } + + /** + * Construct singleton and initialize and launch the VLC library. + * + * \return VLC initialization result + */ + static bool init(); + + /** + * \returns the most recent error message of libvlc + */ + static const char *errorMessage(); + + /** + * Destruct the LibVLC singleton and release the contained libvlc instance. + */ + ~LibVLC(); + +private: + Q_DISABLE_COPY(LibVLC) + + /** + * Private default constructor, to create LibVLC call init instead. + * + * \see init + */ + LibVLC(); + + libvlc_instance_t *m_vlcInstance; +}; + +#endif // LIBVLC_H diff --git a/src/utils/mime.h.cmake b/src/utils/mime.h.cmake new file mode 100644 index 0000000..fcec78c --- /dev/null +++ b/src/utils/mime.h.cmake @@ -0,0 +1,41 @@ +/* + Copyright (C) 2012 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef PHONON_VLC_MIME_H +#define PHONON_VLC_MIME_H + +#include + +namespace Phonon { +namespace VLC { + +static QStringList mimeTypeList() +{ + // In CMake we null-terminate this list for ease of iteration. + const char *c_strings[] = @PHONON_VLC_MIME_TYPES_C_ARRAY@; + + QStringList list; + int i = 0; + while (c_strings[i]) + list.append(QLatin1String(c_strings[i++])); + return list; +} + +} // namespace VLC +} // namespace Phonon + +#endif // PHONON_VLC_MIME_H diff --git a/src/utils/vstring.h b/src/utils/vstring.h new file mode 100644 index 0000000..e9de06d --- /dev/null +++ b/src/utils/vstring.h @@ -0,0 +1,59 @@ +/* + Copyright (C) 2012 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef PHONON_VLC_VSTRING_H +#define PHONON_VLC_VSTRING_H + +#include +#include +#include + +/** + * @brief The VString class wraps around a char* returned from libvlc functions. + * Directly from libvlc functions returned cstrings are unique in two ways. + * For one they are to be freed by the caller, and for another they are always + * UTF8 (as VLC internally only uses UTF8). + * + * Particularly the first point is where VString comes in, it will on destruction + * call libvlc_free to free the string, thus avoiding memleaks. + * Additionally it conveniently converts to QString using either toQString + * or implicit cast to QString which makes it completely transparent to other + * functions. It also prevents you from carrying the cstring out of scope and + * render implicit copies of it invalid once free is called somewhere. + * Both functions use QString::fromUtf8 and are therefore the best way to + * process the string. + */ +class VString +{ +public: + explicit VString(char *vlcString) : m_vlcString(vlcString) {} + ~VString() + { + libvlc_free(m_vlcString); + } + + // VLC internally only uses UTF8! + QString toQString() { return QString::fromUtf8(m_vlcString); } + operator QString() { return toQString(); } + +private: + VString() {} + + char *m_vlcString; +}; + +#endif // PHONON_VLC_VSTRING_H diff --git a/src/video/mac/nsvideoview.h b/src/video/mac/nsvideoview.h new file mode 100644 index 0000000..9485283 --- /dev/null +++ b/src/video/mac/nsvideoview.h @@ -0,0 +1,29 @@ +/* + Copyright (C) 2010 Benoit Calvez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef NSVIDEOVIEW_H +#define NSVIDEOVIEW_H + +#import + +@interface VideoView : NSView +- (void)addVoutSubview: (NSView *)view; +- (void)removeVoutSubview: (NSView *)view; +- (BOOL)stretchesVideo; +@end + +#endif // NSVIDEOVIEW_H diff --git a/src/video/mac/nsvideoview.mm b/src/video/mac/nsvideoview.mm new file mode 100644 index 0000000..ef97b62 --- /dev/null +++ b/src/video/mac/nsvideoview.mm @@ -0,0 +1,37 @@ +/* + Copyright (C) 2010 Benoit Calvez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "nsvideoview.h" + +@implementation VideoView +- (void)addVoutSubview:(NSView *)view +{ + [view setFrame:[self bounds]]; + [self addSubview:view]; + [view setAutoresizingMask: NSViewHeightSizable |NSViewWidthSizable]; +} +- (void)removeVoutSubview:(NSView *)view +{ + [view removeFromSuperview]; +} +- (BOOL)stretchesVideo +{ + //Whatever you would want + return NO; +} + +@end diff --git a/src/video/mac/vlcmacwidget.h b/src/video/mac/vlcmacwidget.h new file mode 100644 index 0000000..55b7c02 --- /dev/null +++ b/src/video/mac/vlcmacwidget.h @@ -0,0 +1,32 @@ +/* + Copyright (C) 2010 Benoit Calvez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef VLCMACWIDGET_H +#define VLCMACWIDGET_H + +#include + +#import + +class VlcMacWidget : public QMacCocoaViewContainer +{ +public: + explicit VlcMacWidget(QWidget *parent = 0); +}; + + +#endif // VLCMACWIDGET_H diff --git a/src/video/mac/vlcmacwidget.mm b/src/video/mac/vlcmacwidget.mm new file mode 100644 index 0000000..fc801c2 --- /dev/null +++ b/src/video/mac/vlcmacwidget.mm @@ -0,0 +1,39 @@ +/* + Copyright (C) 2010 Benoit Calvez + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "vlcmacwidget.h" +#import +#import "nsvideoview.h" + +#include + +VlcMacWidget::VlcMacWidget(QWidget *parent) : QMacCocoaViewContainer(0, parent) +{ + // Many Cocoa objects create temporary autorelease objects, + // so create a pool to catch them. + @autoreleasepool { + VideoView *videoView = [[VideoView alloc] init]; + + this->setCocoaView(videoView); + + // Release our reference, since our super class takes ownership and we + // don't need it anymore (except with Qt 5.8.0 because of a regression). + if (strcmp(qVersion(), "5.8.0")) { + [videoView release]; + } + } +} diff --git a/src/video/videodataoutput.cpp b/src/video/videodataoutput.cpp new file mode 100644 index 0000000..12a95e5 --- /dev/null +++ b/src/video/videodataoutput.cpp @@ -0,0 +1,200 @@ +/* + Copyright (C) 2010-2012 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) version 3, or any + later version accepted by the membership of KDE e.V. (or its + successor approved by the membership of KDE e.V.), Nokia Corporation + (or its successors, if any) and the KDE Free Qt Foundation, which shall + act as a proxy defined in Section 6 of version 3 of the license. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "videodataoutput.h" + +#include +#include +#include + +#include + +#include "utils/debug.h" +#include "media.h" +#include "mediaobject.h" + +using namespace Phonon::Experimental; + +namespace Phonon +{ +namespace VLC +{ + +VideoDataOutput::VideoDataOutput(QObject *parent) + : QObject(parent) + , m_frontend(0) +{ +} + +VideoDataOutput::~VideoDataOutput() +{ +} + +void VideoDataOutput::handleConnectToMediaObject(MediaObject *mediaObject) +{ + Q_UNUSED(mediaObject); + setCallbacks(m_player); +} + +void VideoDataOutput::handleDisconnectFromMediaObject(MediaObject *mediaObject) +{ + Q_UNUSED(mediaObject); + unsetCallbacks(m_player); +} + +void VideoDataOutput::handleAddToMedia(Media *media) +{ + media->addOption(":video"); +} + +Experimental::AbstractVideoDataOutput *VideoDataOutput::frontendObject() const +{ + return m_frontend; +} + +void VideoDataOutput::setFrontendObject(Experimental::AbstractVideoDataOutput *frontend) +{ + m_frontend = frontend; +} + +void *VideoDataOutput::lockCallback(void **planes) +{ + m_mutex.lock(); + DEBUG_BLOCK; + planes[0] = reinterpret_cast(m_frame.data0.data()); + planes[1] = reinterpret_cast(m_frame.data1.data()); + planes[2] = reinterpret_cast(m_frame.data2.data()); + return 0; +} + +void VideoDataOutput::unlockCallback(void *picture, void *const*planes) +{ + Q_UNUSED(picture); + Q_UNUSED(planes); + DEBUG_BLOCK; + + // For some reason VLC yields BGR24, so we swap it to RGB + if (m_frame.format == Experimental::VideoFrame2::Format_RGB888) { + uchar *data = (uchar *) m_frame.data0.data(); + uchar tmp; + for (int i = 0; i < m_frame.data0.size(); i += 3) { + tmp = data[i]; + data[i] = data[i+2]; + data[i+2] = tmp; + } + } + + if (m_frontend) + m_frontend->frameReady(m_frame); + + m_mutex.unlock(); +} + +void VideoDataOutput::displayCallback(void *picture) +{ + Q_UNUSED(picture); + DEBUG_BLOCK; + // We send the frame while unlocking as we could loose syncing otherwise. + // With VDO the consumer is expected to ensure syncness while not blocking + // unlock for long periods of time. Good luck with that... -.- +} + +static VideoFrame2::Format fourccToFormat(const char *fourcc) +{ + if (qstrcmp(fourcc, "RV24")) + return VideoFrame2::Format_RGB888; + else if (qstrcmp(fourcc, "RV32")) + return VideoFrame2::Format_RGB32; + else if (qstrcmp(fourcc, "YV12")) + return VideoFrame2::Format_YV12; + else if (qstrcmp(fourcc, "YUY2")) + return VideoFrame2::Format_YUY2; + else + return VideoFrame2::Format_Invalid; +} + +static const vlc_chroma_description_t *setFormat(VideoFrame2::Format format, char **chroma) +{ + switch (format) { + case VideoFrame2::Format_Invalid: + *chroma = 0; + return 0; + case VideoFrame2::Format_RGB32: + qstrcpy(*chroma, "RV32"); + return vlc_fourcc_GetChromaDescription(VLC_CODEC_RGB32); + case VideoFrame2::Format_RGB888: + qstrcpy(*chroma, "RV24"); + return vlc_fourcc_GetChromaDescription(VLC_CODEC_RGB24); + case VideoFrame2::Format_YV12: + qstrcpy(*chroma, "YV12"); + return vlc_fourcc_GetChromaDescription(VLC_CODEC_YV12); + case VideoFrame2::Format_YUY2: + qstrcpy(*chroma, "YUY2"); + return vlc_fourcc_GetChromaDescription(VLC_CODEC_YUYV); + } + return 0; +} + +unsigned VideoDataOutput::formatCallback(char *chroma, + unsigned *width, unsigned *height, + unsigned *pitches, unsigned *lines) +{ + DEBUG_BLOCK; + + m_frame.width = *width; + m_frame.height = *height; + + const vlc_chroma_description_t *chromaDesc = 0; + + QSet allowedFormats = m_frontend->allowedFormats(); + VideoFrame2::Format suggestedFormat = fourccToFormat(chroma); + if (suggestedFormat != VideoFrame2::Format_Invalid + && allowedFormats.contains(suggestedFormat)) { // Use suggested + chromaDesc = setFormat(suggestedFormat, &chroma); + m_frame.format = suggestedFormat; + } else { // Pick first and use that + foreach (const VideoFrame2::Format &format, allowedFormats) { + chromaDesc = setFormat(format, &chroma); + if (chroma) { + m_frame.format = format; + break; + } + } + } + + Q_ASSERT(chromaDesc); + + unsigned int bufferSize = setPitchAndLines(chromaDesc, *width, *height, pitches, lines); + + m_frame.data0.resize(pitches[0] * lines[0]); + m_frame.data1.resize(pitches[1] * lines[1]); + m_frame.data2.resize(pitches[2] * lines[0]); + + return bufferSize; +} + +void VideoDataOutput::formatCleanUpCallback() +{ + DEBUG_BLOCK; +} + +} // namespace VLC +} // namespace Phonon diff --git a/src/video/videodataoutput.h b/src/video/videodataoutput.h new file mode 100644 index 0000000..422e06d --- /dev/null +++ b/src/video/videodataoutput.h @@ -0,0 +1,74 @@ +/* + Copyright (C) 2012 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef Phonon_VLC_VIDEODATAOUTPUT_H +#define Phonon_VLC_VIDEODATAOUTPUT_H + +#include +#include + +#include +#include + +#include "sinknode.h" +#include "videomemorystream.h" + +namespace Phonon +{ +namespace VLC +{ + +/** + * @author Harald Sitter + */ +class VideoDataOutput : public QObject, public SinkNode, + public Experimental::VideoDataOutputInterface, private VideoMemoryStream +{ + Q_OBJECT + Q_INTERFACES(Phonon::Experimental::VideoDataOutputInterface) +public: + explicit VideoDataOutput(QObject *parent); + ~VideoDataOutput(); + + void handleConnectToMediaObject(MediaObject *mediaObject); + void handleDisconnectFromMediaObject(MediaObject *mediaObject); + void handleAddToMedia(Media *media); + + Experimental::AbstractVideoDataOutput *frontendObject() const; + void setFrontendObject(Experimental::AbstractVideoDataOutput *frontend); + + virtual void *lockCallback(void **planes); + virtual void unlockCallback(void *picture,void *const *planes); + virtual void displayCallback(void *picture); + + virtual unsigned formatCallback(char *chroma, + unsigned *width, unsigned *height, + unsigned *pitches, + unsigned *lines); + virtual void formatCleanUpCallback(); + +private: + Experimental::AbstractVideoDataOutput *m_frontend; + Experimental::VideoFrame2 m_frame; + QByteArray m_buffer; + QMutex m_mutex; +}; + +} // namespace VLC +} // namespace Phonon + +#endif // PHONON_VLC_VIDEODATAOUTPUT_H diff --git a/src/video/videomemorystream.cpp b/src/video/videomemorystream.cpp new file mode 100644 index 0000000..6886bcf --- /dev/null +++ b/src/video/videomemorystream.cpp @@ -0,0 +1,147 @@ +/* + Copyright (C) 2012 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "videomemorystream.h" + +#include "mediaplayer.h" + +namespace Phonon { +namespace VLC { + +static inline VideoMemoryStream *p_this(void *opaque) { return static_cast(opaque); } +static inline VideoMemoryStream *p_this(void **opaque) { return static_cast(*opaque); } +#define P_THIS p_this(opaque) + +VideoMemoryStream::VideoMemoryStream() +{ +} + +VideoMemoryStream::~VideoMemoryStream() +{ +} + +static inline qint64 gcd(qint64 a, qint64 b) +{ + while( b ) + { + qint64 c = a % b; + a = b; + b = c; + } + return a; +} + +static int lcm(int a, int b) +{ + return a * b / GCD( a, b ); +} + +unsigned VideoMemoryStream::setPitchAndLines(const vlc_chroma_description_t *desc, + unsigned width, unsigned height, + unsigned *pitches, unsigned *lines, + unsigned *visiblePitches, unsigned *visibleLines) +{ + // Mostly taken from vlc/src/misc/picture.c + // Simple alignment would be an option but I trust the VLC guys they know what they are doing. + int i_modulo_w = 1; + int i_modulo_h = 1; + unsigned int i_ratio_h = 1; + for( unsigned i = 0; i < desc->plane_count; i++ ) + { + i_modulo_w = lcm( i_modulo_w, 8 * desc->p[i].w.den ); + i_modulo_h = lcm( i_modulo_h, 8 * desc->p[i].h.den ); + if( i_ratio_h < desc->p[i].h.den ) + i_ratio_h = desc->p[i].h.den; + + } + i_modulo_h = lcm( i_modulo_h, 32 ); + + const int i_width_aligned = ( width + i_modulo_w - 1 ) / i_modulo_w * i_modulo_w; + const int i_height_aligned = ( height + i_modulo_h - 1 ) / i_modulo_h * i_modulo_h; + const int i_height_extra = 2 * i_ratio_h; /* This one is a hack for some ASM functions */ + + unsigned int bufferSize = 0; + for(unsigned i = 0; i < desc->plane_count; ++i) + { + pitches[i] = i_width_aligned * desc->p[i].w.num / desc->p[i].w.den * desc->pixel_size; + if (visiblePitches) + visiblePitches[i] = width * desc->p[i].w.num / desc->p[i].w.den * desc->pixel_size; + + lines[i] = (i_height_aligned + i_height_extra ) * desc->p[i].h.num / desc->p[i].h.den; + if (visibleLines) + visibleLines[i] = height * desc->p[i].h.num / desc->p[i].h.den; + + bufferSize += pitches[i] * lines[i]; + } + + return bufferSize; +} + +void VideoMemoryStream::setCallbacks(MediaPlayer *player) +{ + libvlc_video_set_callbacks(player->libvlc_media_player(), + lockCallbackInternal, + unlockCallbackInternal, + displayCallbackInternal, + this); + libvlc_video_set_format_callbacks(player->libvlc_media_player(), + formatCallbackInternal, + formatCleanUpCallbackInternal); +} + +void VideoMemoryStream::unsetCallbacks(MediaPlayer *player) +{ + libvlc_video_set_callbacks(player->libvlc_media_player(), + 0, + 0, + 0, + 0); + libvlc_video_set_format_callbacks(player->libvlc_media_player(), + 0, + 0); +} + + +void *VideoMemoryStream::lockCallbackInternal(void *opaque, void **planes) +{ + return P_THIS->lockCallback(planes); +} + +void VideoMemoryStream::unlockCallbackInternal(void *opaque, void *picture, void *const*planes) +{ + P_THIS->unlockCallback(picture, planes); +} + +void VideoMemoryStream::displayCallbackInternal(void *opaque, void *picture) +{ + P_THIS->displayCallback(picture); +} + +unsigned VideoMemoryStream::formatCallbackInternal(void **opaque, char *chroma, + unsigned *width, unsigned *height, + unsigned *pitches, unsigned *lines) +{ + return P_THIS->formatCallback(chroma, width, height, pitches, lines); +} + +void VideoMemoryStream::formatCleanUpCallbackInternal(void *opaque) +{ + P_THIS->formatCleanUpCallback(); +} + +} // namespace VLC +} // namespace Phonon diff --git a/src/video/videomemorystream.h b/src/video/videomemorystream.h new file mode 100644 index 0000000..7127e52 --- /dev/null +++ b/src/video/videomemorystream.h @@ -0,0 +1,83 @@ +/* + Copyright (C) 2012 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef PHONON_VLC_VIDEOMEMORYSTREAM_H +#define PHONON_VLC_VIDEOMEMORYSTREAM_H + +// VLC 3.0 uses the restrict keyword. restrict is not a thing in C++, so +// depending on the compiler you use an extension keyword or drop it entirely. +#if defined(Q_CC_GNU) +#define restrict __restrict__ +#elif defined(Q_CC_MSVC) +#define restrict __restrict +#else +#define restrict +#endif + +#include +#include + +namespace Phonon { +namespace VLC { + +class MediaPlayer; + +class VideoMemoryStream +{ +public: + explicit VideoMemoryStream(); + virtual ~VideoMemoryStream(); + + /** + * @returns overall buffersize needed + */ + static unsigned setPitchAndLines(const vlc_chroma_description_t *chromaDescription, + unsigned width, unsigned height, + unsigned *pitches, unsigned *lines, + unsigned *visiblePitches = 0, unsigned *visibleLines = 0); + + void setCallbacks(Phonon::VLC::MediaPlayer *player); + void unsetCallbacks(Phonon::VLC::MediaPlayer *player); + +protected: + virtual void *lockCallback(void **planes) = 0; + virtual void unlockCallback(void *picture,void *const *planes) = 0; + virtual void displayCallback(void *picture) = 0; + + virtual unsigned formatCallback(char *chroma, + unsigned *width, unsigned *height, + unsigned *pitches, + unsigned *lines) = 0; + virtual void formatCleanUpCallback() = 0; + +private: + static void *lockCallbackInternal(void *opaque, void **planes); + static void unlockCallbackInternal(void *opaque, void *picture, void *const *planes); + static void displayCallbackInternal(void *opaque, void *picture); + + static unsigned formatCallbackInternal(void **opaque, char *chroma, + unsigned *width, unsigned *height, + unsigned *pitches, + unsigned *lines); + static void formatCleanUpCallbackInternal(void *opaque); + +}; + +} // namespace VLC +} // namespace Phonon + +#endif // PHONON_VLC_VIDEOMEMORYSTREAM_H diff --git a/src/video/videowidget.cpp b/src/video/videowidget.cpp new file mode 100644 index 0000000..35d9025 --- /dev/null +++ b/src/video/videowidget.cpp @@ -0,0 +1,545 @@ +/* + Copyright (C) 2007-2008 Tanguy Krotoff + Copyright (C) 2008 Lukas Durfina + Copyright (C) 2009 Fathi Boudra + Copyright (C) 2009-2011 vlc-phonon AUTHORS + Copyright (C) 2011-2021 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "videowidget.h" + +#include +#include +#include + +#include + +#include "utils/debug.h" +#include "mediaobject.h" +#include "media.h" + +#include "video/videomemorystream.h" + +namespace Phonon { +namespace VLC { + +#define DEFAULT_QSIZE QSize(320, 240) + +class SurfacePainter : public VideoMemoryStream +{ +public: + void handlePaint(QPaintEvent *event) + { + // Mind that locking here is still faster than making this lockfree by + // dispatching QEvents. + // Plus VLC can actually skip frames as necessary. + QMutexLocker lock(&m_mutex); + Q_UNUSED(event); + + if (m_frame.isNull()) { + return; + } + + QPainter painter(widget); + // When using OpenGL for the QPaintEngine drawing the same QImage twice + // does not actually result in a texture change for one reason or another. + // So we simply create new images for every event. This is plenty cheap + // as the QImage only points to the plane data (it can't even make it + // properly shared as it does not know that the data belongs to a QBA). + // TODO: investigate if this is still necessary. This was added for gwenview, but with Qt 5.15 the problem + // can't be produced. + painter.drawImage(drawFrameRect(), QImage(m_frame)); + event->accept(); + } + + VideoWidget *widget; + +private: + virtual void *lockCallback(void **planes) + { + m_mutex.lock(); + planes[0] = (void *) m_frame.bits(); + return 0; + } + + virtual void unlockCallback(void *picture,void *const *planes) + { + Q_UNUSED(picture); + Q_UNUSED(planes); + m_mutex.unlock(); + } + + virtual void displayCallback(void *picture) + { + Q_UNUSED(picture); + if (widget) + widget->update(); + } + + virtual unsigned formatCallback(char *chroma, + unsigned *width, unsigned *height, + unsigned *pitches, + unsigned *lines) + { + QMutexLocker lock(&m_mutex); + // Surface rendering is a fallback system used when no efficient rendering implementation is available. + // As such we only support RGB32 for simplicity reasons and this will almost always mean software scaling. + // And since scaling is unavoidable anyway we take the canonical frame size and then scale it on our end via + // QPainter, again, greater simplicity at likely no real extra cost since this is all super inefficient anyway. + // Also, since aspect ratio can be change mid-playback by the user, doing the scaling on our end means we + // don't need to restart the entire player to retrigger format calculation. + // With all that in mind we simply use the canonical size and feed VLC the QImage's pitch and lines as + // effectively the VLC vout is the QImage so its constraints matter. + + // per https://wiki.videolan.org/Hacker_Guide/Video_Filters/#Pitch.2C_visible_pitch.2C_planes_et_al. + // it would seem that we can use either real or visible pitches and lines as VLC generally will iterate the + // smallest value when moving data between two entities. i.e. since QImage will at most paint NxM anyway, + // we may just go with its values as calculating the real pitch/line of the VLC picture_t for RV32 wouldn't + // change the maximum pitch/lines we can paint on the output side. + + qstrcpy(chroma, "RV32"); + m_frame = QImage(*width, *height, QImage::Format_RGB32); + Q_ASSERT(!m_frame.isNull()); // ctor may construct null if allocation fails + m_frame.fill(0); + pitches[0] = m_frame.bytesPerLine(); + lines[0] = m_frame.sizeInBytes() / m_frame.bytesPerLine(); + + return m_frame.sizeInBytes(); + } + + virtual void formatCleanUpCallback() + { + // Lazy delete the object to avoid callbacks from VLC after deletion. + if (!widget) { + // The widget member is set to null by the widget destructor, so when this condition is true the + // widget had already been destroyed and we can't possibly receive a paint event anymore, meaning + // we need no lock here. If it were any other way we'd have trouble with synchronizing deletion + // without deleting a locked mutex. + delete this; + } + } + + QRect scaleToAspect(QRect srcRect, int w, int h) const + { + float width = srcRect.width(); + float height = srcRect.width() * (float(h) / float(w)); + if (height > srcRect.height()) { + height = srcRect.height(); + width = srcRect.height() * (float(w) / float(h)); + } + return QRect(0, 0, (int)width, (int)height); + } + + QRect drawFrameRect() const + { + QRect widgetRect = widget->rect(); + QRect drawFrameRect; + switch (widget->aspectRatio()) { + case Phonon::VideoWidget::AspectRatioWidget: + drawFrameRect = widgetRect; + // No more calculations needed. + return drawFrameRect; + case Phonon::VideoWidget::AspectRatio4_3: + drawFrameRect = scaleToAspect(widgetRect, 4, 3); + break; + case Phonon::VideoWidget::AspectRatio16_9: + drawFrameRect = scaleToAspect(widgetRect, 16, 9); + break; + case Phonon::VideoWidget::AspectRatioAuto: + drawFrameRect = QRect(0, 0, m_frame.width(), m_frame.height()); + break; + } + + // Scale m_drawFrameRect to fill the widget + // without breaking aspect: + float widgetWidth = widgetRect.width(); + float widgetHeight = widgetRect.height(); + float frameWidth = widgetWidth; + float frameHeight = drawFrameRect.height() * float(widgetWidth) / float(drawFrameRect.width()); + + switch (widget->scaleMode()) { + case Phonon::VideoWidget::ScaleAndCrop: + if (frameHeight < widgetHeight) { + frameWidth *= float(widgetHeight) / float(frameHeight); + frameHeight = widgetHeight; + } + break; + case Phonon::VideoWidget::FitInView: + if (frameHeight > widgetHeight) { + frameWidth *= float(widgetHeight) / float(frameHeight); + frameHeight = widgetHeight; + } + break; + } + drawFrameRect.setSize(QSize(int(frameWidth), int(frameHeight))); + drawFrameRect.moveTo(int((widgetWidth - frameWidth) / 2.0f), + int((widgetHeight - frameHeight) / 2.0f)); + return drawFrameRect; + } + + // Could ReadWriteLock two frames so VLC can write while we paint. + QImage m_frame; + QMutex m_mutex; +}; + +VideoWidget::VideoWidget(QWidget *parent) : + BaseWidget(parent), + SinkNode(), + m_videoSize(DEFAULT_QSIZE), + m_aspectRatio(Phonon::VideoWidget::AspectRatioAuto), + m_scaleMode(Phonon::VideoWidget::FitInView), + m_filterAdjustActivated(false), + m_brightness(0.0), + m_contrast(0.0), + m_hue(0.0), + m_saturation(0.0), + m_surfacePainter(0) +{ + // We want background painting so Qt autofills with black. + setAttribute(Qt::WA_NoSystemBackground, false); + + // Required for dvdnav +#ifdef __GNUC__ +#warning dragonplayer munches on our mouse events, so clicking in a DVD menu does not work - vlc 1.2 where are thu? +#endif // __GNUC__ + setMouseTracking(true); + + // setBackgroundColor + QPalette p = palette(); + p.setColor(backgroundRole(), Qt::black); + setPalette(p); + setAutoFillBackground(true); +} + +VideoWidget::~VideoWidget() +{ + if (m_surfacePainter) + m_surfacePainter->widget = 0; // Lazy delete +} + +void VideoWidget::handleConnectToMediaObject(MediaObject *mediaObject) +{ + connect(mediaObject, SIGNAL(hasVideoChanged(bool)), + SLOT(updateVideoSize(bool))); + connect(mediaObject, SIGNAL(hasVideoChanged(bool)), + SLOT(processPendingAdjusts(bool))); + connect(mediaObject, SIGNAL(currentSourceChanged(MediaSource)), + SLOT(clearPendingAdjusts())); + + clearPendingAdjusts(); +} + +void VideoWidget::handleDisconnectFromMediaObject(MediaObject *mediaObject) +{ + // Undo all connections or path creation->destruction->creation can cause + // duplicated connections or getting singals from two different MediaObjects. + disconnect(mediaObject, 0, this, 0); +} + +void VideoWidget::handleAddToMedia(Media *media) +{ + media->addOption(":video"); + + if (!m_surfacePainter) { +#if defined(Q_OS_MAC) + m_player->setNsObject(cocoaView()); +#elif defined(Q_OS_UNIX) + if (QGuiApplication::platformName().contains(QStringLiteral("xcb"), Qt::CaseInsensitive)) { + m_player->setXWindow(winId()); + } else { + enableSurfacePainter(); + } +#elif defined(Q_OS_WIN) + m_player->setHwnd((HWND)winId()); +#endif + } +} + +Phonon::VideoWidget::AspectRatio VideoWidget::aspectRatio() const +{ + return m_aspectRatio; +} + +void VideoWidget::setAspectRatio(Phonon::VideoWidget::AspectRatio aspect) +{ + DEBUG_BLOCK; + if (!m_player) + return; + + m_aspectRatio = aspect; + + switch (m_aspectRatio) { + // FIXME: find a way to implement aspectratiowidget, it is meant to scale + // and stretch (i.e. scale to window without retaining aspect ratio). + case Phonon::VideoWidget::AspectRatioAuto: + m_player->setVideoAspectRatio(QByteArray()); + return; + case Phonon::VideoWidget::AspectRatio4_3: + m_player->setVideoAspectRatio("4:3"); + return; + case Phonon::VideoWidget::AspectRatio16_9: + m_player->setVideoAspectRatio("16:9"); + return; + } + warning() << "The aspect ratio" << aspect << "is not supported by Phonon VLC."; +} + +Phonon::VideoWidget::ScaleMode VideoWidget::scaleMode() const +{ + return m_scaleMode; +} + +void VideoWidget::setScaleMode(Phonon::VideoWidget::ScaleMode scale) +{ +#ifdef __GNUC__ +#warning OMG WTF +#endif + m_scaleMode = scale; + switch (m_scaleMode) { + } + warning() << "The scale mode" << scale << "is not supported by Phonon VLC."; +} + +qreal VideoWidget::brightness() const +{ + return m_brightness; +} + +void VideoWidget::setBrightness(qreal brightness) +{ + DEBUG_BLOCK; + if (!m_player) { + return; + } + if (!enableFilterAdjust()) { + // Add to pending adjusts + m_pendingAdjusts.insert(QByteArray("setBrightness"), brightness); + return; + } + + // VLC operates within a 0.0 to 2.0 range for brightness. + m_brightness = brightness; + m_player->setVideoAdjust(libvlc_adjust_Brightness, + phononRangeToVlcRange(m_brightness, 2.0)); +} + +qreal VideoWidget::contrast() const +{ + return m_contrast; +} + +void VideoWidget::setContrast(qreal contrast) +{ + DEBUG_BLOCK; + if (!m_player) { + return; + } + if (!enableFilterAdjust()) { + // Add to pending adjusts + m_pendingAdjusts.insert(QByteArray("setContrast"), contrast); + return; + } + + // VLC operates within a 0.0 to 2.0 range for contrast. + m_contrast = contrast; + m_player->setVideoAdjust(libvlc_adjust_Contrast, phononRangeToVlcRange(m_contrast, 2.0)); +} + +qreal VideoWidget::hue() const +{ + return m_hue; +} + +void VideoWidget::setHue(qreal hue) +{ + DEBUG_BLOCK; + if (!m_player) { + return; + } + if (!enableFilterAdjust()) { + // Add to pending adjusts + m_pendingAdjusts.insert(QByteArray("setHue"), hue); + return; + } + + // VLC operates within a 0 to 360 range for hue. + // Phonon operates on -1.0 to 1.0, so we need to consider 0 to 180 as + // 0 to 1.0 and 180 to 360 as -1 to 0.0. + // 360/0 (0) + // ___ + // / \ + // 270 (-.25) | | 90 (.25) + // \___/ + // 180 (1/-1) + // (-.25 is 360 minus 90 (vlcValue of .25). + m_hue = hue; + const int vlcValue = static_cast(phononRangeToVlcRange(qAbs(hue), 180.0, false)); + int value = 0; + if (hue >= 0) + value = vlcValue; + else + value = 360.0 - vlcValue; + m_player->setVideoAdjust(libvlc_adjust_Hue, value); +} + +qreal VideoWidget::saturation() const +{ + return m_saturation; +} + +void VideoWidget::setSaturation(qreal saturation) +{ + DEBUG_BLOCK; + if (!m_player) { + return; + } + if (!enableFilterAdjust()) { + // Add to pending adjusts + m_pendingAdjusts.insert(QByteArray("setSaturation"), saturation); + return; + } + + // VLC operates within a 0.0 to 3.0 range for saturation. + m_saturation = saturation; + m_player->setVideoAdjust(libvlc_adjust_Saturation, + phononRangeToVlcRange(m_saturation, 3.0)); +} + +QWidget *VideoWidget::widget() +{ + return this; +} + +QSize VideoWidget::sizeHint() const +{ + return m_videoSize; +} + +void VideoWidget::updateVideoSize(bool hasVideo) +{ + if (hasVideo) { + m_videoSize = m_player->videoSize(); + updateGeometry(); + update(); + } else + m_videoSize = DEFAULT_QSIZE; +} + +void VideoWidget::setVisible(bool visible) +{ + if (window() && window()->testAttribute(Qt::WA_DontShowOnScreen) && !m_surfacePainter) { + enableSurfacePainter(); + } + QWidget::setVisible(visible); +} + +void VideoWidget::processPendingAdjusts(bool videoAvailable) +{ + if (!videoAvailable || !m_mediaObject || !m_mediaObject->hasVideo()) { + return; + } + + QHashIterator it(m_pendingAdjusts); + while (it.hasNext()) { + it.next(); + QMetaObject::invokeMethod(this, it.key().constData(), Q_ARG(qreal, it.value())); + } + m_pendingAdjusts.clear(); +} + +void VideoWidget::clearPendingAdjusts() +{ + m_pendingAdjusts.clear(); +} + +void VideoWidget::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + if (m_surfacePainter) + m_surfacePainter->handlePaint(event); +} + +bool VideoWidget::enableFilterAdjust(bool adjust) +{ + DEBUG_BLOCK; + // Need to check for MO here, because we can get called before a VOut is actually + // around in which case we just ignore this. + if (!m_mediaObject || !m_mediaObject->hasVideo()) { + debug() << "no mo or no video!!!"; + return false; + } + if ((!m_filterAdjustActivated && adjust) || + (m_filterAdjustActivated && !adjust)) { + debug() << "adjust: " << adjust; + m_player->setVideoAdjust(libvlc_adjust_Enable, static_cast(adjust)); + m_filterAdjustActivated = adjust; + } + return true; +} + +float VideoWidget::phononRangeToVlcRange(qreal phononValue, float upperBoundary, + bool shift) +{ + // VLC operates on different ranges than Phonon. Phonon always uses a range of + // -1:1 with 0 as the default value. + // It is therefore necessary to convert between the two schemes using sophisticated magic. + // First the incoming range is locked between -1..1, then depending on shift + // either normalized to 0..2 or 0..1 and finally a new value is calculated + // depending on the upperBoundary and the normalized range. + float value = static_cast(phononValue); + float range = 2.0; // The default normalized range will be 0..2 = 2 + + // Ensure valid range + if (value < -1.0) + value = -1.0; + else if (value > 1.0) + value = 1.0; + + if (shift) + value += 1.0; // Shift into 0..2 range + else { + // Chop negative value; normalize to 0..1 = range 1 + if (value < 0.0) + value = 0.0; + range = 1.0; + } + + return (value * (upperBoundary/range)); +} + +QImage VideoWidget::snapshot() const +{ + DEBUG_BLOCK; + if (m_player) + return m_player->snapshot(); + else + return QImage(); +} + +void VideoWidget::enableSurfacePainter() +{ + if (m_surfacePainter) { + return; + } + + debug() << "ENABLING SURFACE PAINTING"; + m_surfacePainter = new SurfacePainter; + m_surfacePainter->widget = this; + m_surfacePainter->setCallbacks(m_player); +} + +} // namespace VLC +} // namespace Phonon diff --git a/src/video/videowidget.h b/src/video/videowidget.h new file mode 100644 index 0000000..95cd463 --- /dev/null +++ b/src/video/videowidget.h @@ -0,0 +1,253 @@ +/* + Copyright (C) 2007-2008 Tanguy Krotoff + Copyright (C) 2008 Lukas Durfina + Copyright (C) 2009 Fathi Boudra + Copyright (C) 2009-2011 vlc-phonon AUTHORS + Copyright (C) 2011-2019 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef PHONON_VLC_VIDEOWIDGET_H +#define PHONON_VLC_VIDEOWIDGET_H + +#include + +#include + +#ifdef Q_OS_MAC +#include "video/mac/vlcmacwidget.h" +typedef VlcMacWidget BaseWidget; +#else +typedef QWidget BaseWidget; +#endif + +#include "sinknode.h" + +namespace Phonon { +namespace VLC { + +class SurfacePainter; + +/** \brief Implements the Phonon VideoWidget MediaNode, responsible for displaying video + * + * Phonon video is displayed using this widget. It implements the VideoWidgetInterface. + * It is connected to a media object that provides the video source. Methods to control + * video settings such as brightness or contrast are provided. + */ +class VideoWidget : public BaseWidget, public SinkNode, public VideoWidgetInterface44 +{ + Q_OBJECT + Q_INTERFACES(Phonon::VideoWidgetInterface44) +public: + /** + * Constructs a new VideoWidget with the given parent. The video settings members + * are set to their default values. + */ + explicit VideoWidget(QWidget *parent); + + /** + * Death to the VideoWidget! + */ + ~VideoWidget(); + + /** + * Connects the VideoWidget to a media object by setting the video widget + * window system identifier of the media object to that of the owned private + * video widget. It also connects the signal from the mediaObject regarding + * a resize of the video. + * + * If the mediaObject was connected to another VideoWidget, the connection is + * lost. + * + * \see MediaObject + * \param mediaObject What media object to connect to + * \reimp + */ + void handleConnectToMediaObject(MediaObject *mediaObject); + /** \reimp */ + void handleDisconnectFromMediaObject(MediaObject *mediaObject); + /** \reimp */ + void handleAddToMedia(Media *media); + + /** + * \return The aspect ratio previously set for the video widget + */ + Phonon::VideoWidget::AspectRatio aspectRatio() const; + + /** + * Set the aspect ratio of the video. + * VLC accepted formats are x:y (4:3, 16:9, etc...) expressing the global image aspect. + */ + void setAspectRatio(Phonon::VideoWidget::AspectRatio aspect); + + /** + * \return The scale mode previously set for the video widget + */ + Phonon::VideoWidget::ScaleMode scaleMode() const; + + /** + * Set how the video is scaled, keeping the aspect ratio into account when the video is resized. + * + * The ScaleMode enumeration describes how to treat aspect ratio during resizing of video. + * \li Phonon::VideoWidget::FitInView - the video will be fitted to fill the view keeping aspect ratio + * \li Phonon::VideoWidget::ScaleAndCrop - the video is scaled + */ + void setScaleMode(Phonon::VideoWidget::ScaleMode scale); + + /** + * \return The brightness previously set for the video widget + */ + qreal brightness() const; + + /** + * Set the brightness of the video + */ + Q_INVOKABLE void setBrightness(qreal brightness); + + /** + * \return The contrast previously set for the video widget + */ + qreal contrast() const; + + /** + * Set the contrast of the video + */ + Q_INVOKABLE void setContrast(qreal contrast); + + /** + * \return The hue previously set for the video widget + */ + qreal hue() const; + + /** + * Set the hue of the video + */ + Q_INVOKABLE void setHue(qreal hue); + + /** + * \return The saturation previously set for the video widget + */ + qreal saturation() const; + + /** + * Set the saturation of the video + */ + Q_INVOKABLE void setSaturation(qreal saturation); + + /** + * \return The owned widget that is used for the actual draw. + */ + QWidget *widget(); + + /// \reimp + QSize sizeHint() const; + + void setVisible(bool visible); + +private slots: + /// Updates the sizeHint to match the native size of the video. + /// \param hasVideo \c true when there is a video, \c false otherwise + void updateVideoSize(bool hasVideo); + + /** + * Sets all pending video adjusts (hue, brightness etc.) that the application + * wanted to set before the vidoe became available. + * + * \param videoAvailable whether or not video is available at the time of calling + */ + void processPendingAdjusts(bool videoAvailable); + + /** + * Clears all pending video adjusts (hue, brightness etc.). + */ + void clearPendingAdjusts(); + +protected: + /// \reimp + void paintEvent(QPaintEvent *event); + +private: + /** + * Sets whether filter adjust is active or not. + * + * \param adjust true if adjust is supposed to be activated, false if not + * + * \returns whether the adjust request was accepted, if not the callee should + * add the request to m_pendingAdjusts for later processing once a video + * became available. Adjusts get accepted always except when + * MediaObject::hasVideo() is false, so it is not related to the + * actual execution of the request. + */ + bool enableFilterAdjust(bool adjust = true); + + /** + * Converts a Phonon range to a VLC value range. + * + * A Phonon range is always a qreal between -1.0 and 1.0, a VLC range however + * can be any between 0 and 360. This functon maps the Phonon value to an + * appropriate value within a specified target range. + * + * \param phononValue the incoming Phonon specific value, should be -1.0:1.0 + * should it however not be within that range will it be + * manually locked (i.e. exceeding values become either -1.0 or 1.0) + * \param upperBoundary the upper boundary for the target range. The lower + * boundary is currently always assumed to be 0 + * \param shift whether or not to shift the Phonon range to positive values + * before mapping to VLC values (useful when our 0 must be a VLC 0). + * Please note that if you do not shift the range will be reduced to + * 0:1, phononValue < 0 will be set to 0. + * + * \returns float usable to VLC + */ + static float phononRangeToVlcRange(qreal phononValue, float upperBoundary, + bool shift = true); + + /** + * \return The snapshot of the current video frame. + */ + QImage snapshot() const; + + /** + * Enables the mighty surface painter (qpaints frames). + */ + void enableSurfacePainter(); + + /** + * Pending video adjusts the application tried to set before we actually + * had a video to set them on. + */ + QHash m_pendingAdjusts; + + /** + * Original size of the video, needed for sizeHint(). + */ + QSize m_videoSize; + + Phonon::VideoWidget::AspectRatio m_aspectRatio; + Phonon::VideoWidget::ScaleMode m_scaleMode; + + bool m_filterAdjustActivated; + qreal m_brightness; + qreal m_contrast; + qreal m_hue; + qreal m_saturation; + + SurfacePainter *m_surfacePainter; +}; + +} // namespace VLC +} // namespace Phonon + +#endif // PHONON_VLC_VIDEOWIDGET_H diff --git a/warning-check.rb b/warning-check.rb new file mode 100755 index 0000000..9f90ed1 --- /dev/null +++ b/warning-check.rb @@ -0,0 +1,40 @@ +#!/usr/bin/env ruby +# Copyright (C) 2011 Harald Sitter +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see . + +for file in Dir.glob("src/**") + line_count = 1 + has_open_ifdef = false + + next if File.directory?(file) + + File.open(file, 'r').each_line do | line | + if line =~ /#\s*ifdef\s*__GNUC__\s*/ + has_open_ifdef = true + end + + if line =~ /#\s*endif\s*/ + has_open_ifdef = false + end + + if line =~ /#\s*warning\s*/ and not has_open_ifdef + raise("unprotected warning in (#{file}:#{line_count}), please add #ifdef __GNUC__") + end + + line_count += 1 + end +end + +puts ("All good :)")