diff options
Diffstat (limited to 'src/3rdparty/phonon')
305 files changed, 49051 insertions, 0 deletions
diff --git a/src/3rdparty/phonon/CMakeLists.txt b/src/3rdparty/phonon/CMakeLists.txt new file mode 100644 index 0000000..c18c3e7 --- /dev/null +++ b/src/3rdparty/phonon/CMakeLists.txt @@ -0,0 +1,272 @@ +project(Phonon) + +cmake_minimum_required(VERSION 2.6.2 FATAL_ERROR) + +# CMP0002: we have multiple targets with the same name for the unit tests +cmake_policy(SET CMP0002 OLD) + +# enable unit tests +option(PHONON_BUILD_TESTS "Build the tests") +option(PHONON_BUILD_EXAMPLES "Build the examples") + +if (PHONON_BUILD_TESTS) + enable_testing() +endif (PHONON_BUILD_TESTS) + +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +include(PhononMacros) + +include(MacroLogFeature) +include(MacroOptionalFindPackage) + +set(QT_MIN_VERSION 4.4.0) +find_package(Qt4 REQUIRED) +if (NOT QT_QTDBUS_FOUND) + message(STATUS "Warning: Phonon won't be compiled with DBus support.") +endif(NOT QT_QTDBUS_FOUND) + +find_package(Automoc4 REQUIRED) +include (CheckCXXCompilerFlag) +include (MacroEnsureVersion) + +if (NOT AUTOMOC4_VERSION) + set(AUTOMOC4_VERSION "0.9.83") +endif (NOT AUTOMOC4_VERSION) +macro_ensure_version("0.9.86" "${AUTOMOC4_VERSION}" _automoc4_version_ok) +if (NOT _automoc4_version_ok) + message(FATAL_ERROR "Your version of automoc4 is too old. You have ${AUTOMOC4_VERSION}, you need at least 0.9.86") +endif (NOT _automoc4_version_ok) + +if (CMAKE_COMPILER_IS_GNUCXX) + set (KDE4_ENABLE_EXCEPTIONS -fexceptions) + # Select flags. + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g") + set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG -DQT_NO_DEBUG") + set(CMAKE_CXX_FLAGS_DEBUG "-g -O2 -fno-reorder-blocks -fno-schedule-insns -fno-inline") + set(CMAKE_CXX_FLAGS_DEBUGFULL "-g3 -fno-inline") + set(CMAKE_CXX_FLAGS_PROFILE "-g3 -fno-inline -ftest-coverage -fprofile-arcs") + set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g") + set(CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG -DQT_NO_DEBUG") + set(CMAKE_C_FLAGS_DEBUG "-g -O2 -fno-reorder-blocks -fno-schedule-insns -fno-inline") + set(CMAKE_C_FLAGS_DEBUGFULL "-g3 -fno-inline") + set(CMAKE_C_FLAGS_PROFILE "-g3 -fno-inline -ftest-coverage -fprofile-arcs") + + if (CMAKE_SYSTEM_NAME MATCHES Linux) + set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-long-long -std=iso9899:1990 -Wundef -Wcast-align -Werror-implicit-function-declaration -Wchar-subscripts -Wall -W -Wpointer-arith -Wwrite-strings -Wformat-security -Wmissing-format-attribute -fno-common") + set ( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wnon-virtual-dtor -Wno-long-long -ansi -Wundef -Wcast-align -Wchar-subscripts -Wall -W -Wpointer-arith -Wformat-security -fno-exceptions -fno-check-new -fno-common") + add_definitions (-D_BSD_SOURCE) + endif (CMAKE_SYSTEM_NAME MATCHES Linux) + + # gcc under Windows + if (MINGW) + set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--export-all-symbols -Wl,--disable-auto-import") + set (CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--export-all-symbols -Wl,--disable-auto-import") + + # we always link against the release version of QT with mingw + # (even for debug builds). So we need to define QT_NO_DEBUG + # or else QPluginLoader rejects plugins because it thinks + # they're built against the wrong QT. + add_definitions(-DQT_NO_DEBUG) + endif (MINGW) + + check_cxx_compiler_flag(-fPIE HAVE_FPIE_SUPPORT) + if(KDE4_ENABLE_FPIE) + if(HAVE_FPIE_SUPPORT) + set (KDE4_CXX_FPIE_FLAGS "-fPIE") + set (KDE4_PIE_LDFLAGS "-pie") + else(HAVE_FPIE_SUPPORT) + message(STATUS "Your compiler doesn't support PIE flag") + endif(HAVE_FPIE_SUPPORT) + endif(KDE4_ENABLE_FPIE) + + check_cxx_compiler_flag(-Woverloaded-virtual __KDE_HAVE_W_OVERLOADED_VIRTUAL) + if(__KDE_HAVE_W_OVERLOADED_VIRTUAL) + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Woverloaded-virtual") + endif(__KDE_HAVE_W_OVERLOADED_VIRTUAL) + + # visibility support + check_cxx_compiler_flag(-fvisibility=hidden __KDE_HAVE_GCC_VISIBILITY) + set( __KDE_HAVE_GCC_VISIBILITY ${__KDE_HAVE_GCC_VISIBILITY} CACHE BOOL "GCC support for hidden visibility") + + # get the gcc version + exec_program(${CMAKE_C_COMPILER} ARGS --version OUTPUT_VARIABLE _gcc_version_info) + + string (REGEX MATCH "[345]\\.[0-9]\\.[0-9]" _gcc_version "${_gcc_version_info}") + # gcc on mac just reports: "gcc (GCC) 3.3 20030304 ..." without the patch level, handle this here: + if (NOT _gcc_version) + string (REGEX REPLACE ".*\\(GCC\\).* ([34]\\.[0-9]) .*" "\\1.0" _gcc_version "${_gcc_version_info}") + endif (NOT _gcc_version) + + macro_ensure_version("4.1.0" "${_gcc_version}" GCC_IS_NEWER_THAN_4_1) + macro_ensure_version("4.2.0" "${_gcc_version}" GCC_IS_NEWER_THAN_4_2) + macro_ensure_version("4.3.0" "${_gcc_version}" GCC_IS_NEWER_THAN_4_3) + + # save a little by making local statics not threadsafe + # ### do not enable it for older compilers, see + # ### http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31806 + if (GCC_IS_NEWER_THAN_4_3) + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-threadsafe-statics") + endif (GCC_IS_NEWER_THAN_4_3) + + set(_GCC_COMPILED_WITH_BAD_ALLOCATOR FALSE) + if (GCC_IS_NEWER_THAN_4_1) + exec_program(${CMAKE_C_COMPILER} ARGS -v OUTPUT_VARIABLE _gcc_alloc_info) + string(REGEX MATCH "(--enable-libstdcxx-allocator=mt)" _GCC_COMPILED_WITH_BAD_ALLOCATOR "${_gcc_alloc_info}") + endif (GCC_IS_NEWER_THAN_4_1) + + if (__KDE_HAVE_GCC_VISIBILITY AND GCC_IS_NEWER_THAN_4_1 AND NOT _GCC_COMPILED_WITH_BAD_ALLOCATOR) + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") + set (KDE4_C_FLAGS "-fvisibility=hidden") + # check that Qt defines Q_DECL_EXPORT as __attribute__ ((visibility("default"))) + # if it doesn't and KDE compiles with hidden default visibiltiy plugins will break + set(_source "#include <QtCore/QtGlobal>\n int main()\n {\n #ifdef QT_VISIBILITY_AVAILABLE \n return 0;\n #else \n return 1; \n #endif \n }\n") + set(_source_file ${CMAKE_BINARY_DIR}/CMakeTmp/check_qt_visibility.cpp) + file(WRITE "${_source_file}" "${_source}") + set(_include_dirs "-DINCLUDE_DIRECTORIES:STRING=${QT_INCLUDES}") + + try_run(_run_result _compile_result ${CMAKE_BINARY_DIR} ${_source_file} CMAKE_FLAGS "${_include_dirs}") + + if(NOT _compile_result) + message(FATAL_ERROR "Could not compile simple test program:\n ${_source}") + endif(NOT _compile_result) + if(_run_result) + message(FATAL_ERROR "Qt compiled without support for -fvisibility=hidden. This will break plugins and linking of some applications. Please fix your Qt installation.") + endif(_run_result) + + if (GCC_IS_NEWER_THAN_4_2) + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden") + endif (GCC_IS_NEWER_THAN_4_2) + else (__KDE_HAVE_GCC_VISIBILITY AND GCC_IS_NEWER_THAN_4_1 AND NOT _GCC_COMPILED_WITH_BAD_ALLOCATOR) + set (__KDE_HAVE_GCC_VISIBILITY 0) + endif (__KDE_HAVE_GCC_VISIBILITY AND GCC_IS_NEWER_THAN_4_1 AND NOT _GCC_COMPILED_WITH_BAD_ALLOCATOR) + +endif (CMAKE_COMPILER_IS_GNUCXX) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON) +set(CMAKE_COLOR_MAKEFILE ON) + +set(PHONON_LIB_MAJOR_VERSION "4") +set(PHONON_LIB_MINOR_VERSION "3") +set(PHONON_LIB_PATCH_VERSION "1") +set(PHONON_LIB_VERSION "${PHONON_LIB_MAJOR_VERSION}.3.1") +set(PHONON_LIB_SOVERSION ${PHONON_LIB_MAJOR_VERSION}) + +add_definitions(${QT_DEFINITIONS}) +remove_definitions(-DQT3_SUPPORT_WARNINGS -DQT3_SUPPORT) +if(MSVC) + add_definitions( -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS ) +endif(MSVC) + +# for including config.h and for includes like <kparts/foo.h> +include_directories(${QT_INCLUDES} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/includes ${CMAKE_CURRENT_SOURCE_DIR}/phonon ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/phonon) + +macro(_SET_FANCY _var _value _comment) + if (KDESupport_SOURCE_DIR) + # when building inside kdesupport other subprojects write crap into our variables + set(${_var} "${_value}") + else (KDESupport_SOURCE_DIR) + if (NOT DEFINED ${_var}) + set(${_var} "${_value}") + else (NOT DEFINED ${_var}) + set(${_var} "${${_var}}" CACHE PATH "${_comment}") + endif (NOT DEFINED ${_var}) + endif (KDESupport_SOURCE_DIR) +endmacro(_SET_FANCY) + +set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)" ) + +_set_fancy(EXEC_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" "Base directory for executables and libraries") +_set_fancy(SHARE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/share" "Base directory for files which go to share/") +_set_fancy(BIN_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/bin" "The install dir for executables (default ${EXEC_INSTALL_PREFIX}/bin)") +_set_fancy(LIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX}" "The subdirectory relative to the install prefix where libraries will be installed (default is ${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX})") +_set_fancy(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" "The subdirectory to the header prefix") +_set_fancy(PLUGIN_INSTALL_DIR "${LIB_INSTALL_DIR}/kde4" "The subdirectory relative to the install prefix where plugins will be installed (default is ${LIB_INSTALL_DIR}/kde4)") +_set_fancy(ICON_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/icons" "The icon install dir (default ${SHARE_INSTALL_PREFIX}/share/icons/)") +_set_fancy(SERVICES_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/kde4/services" "The install dir for service (desktop, protocol, ...) files") +_set_fancy(DBUS_INTERFACES_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/dbus-1/interfaces" "The dbus interfaces install dir (default: ${SHARE_INSTALL_PREFIX}/dbus-1/interfaces)") +_set_fancy(DBUS_SERVICES_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/dbus-1/services" "The dbus services install dir (default: ${SHARE_INSTALL_PREFIX}/dbus-1/services)") + +set(INSTALL_TARGETS_DEFAULT_ARGS RUNTIME DESTINATION "${BIN_INSTALL_DIR}" + LIBRARY DESTINATION "${LIB_INSTALL_DIR}" + ARCHIVE DESTINATION "${LIB_INSTALL_DIR}" COMPONENT Devel ) + +# on the Mac support an extra install directory for application bundles +if(APPLE) + set(INSTALL_TARGETS_DEFAULT_ARGS ${INSTALL_TARGETS_DEFAULT_ARGS} + BUNDLE DESTINATION "${BUNDLE_INSTALL_DIR}" ) +endif(APPLE) + +if (CMAKE_SYSTEM_NAME MATCHES Linux) + if (CMAKE_COMPILER_IS_GNUCXX) + set ( CMAKE_SHARED_LINKER_FLAGS "-Wl,--fatal-warnings -Wl,--no-undefined -lc ${CMAKE_SHARED_LINKER_FLAGS}") + set ( CMAKE_MODULE_LINKER_FLAGS "-Wl,--fatal-warnings -Wl,--no-undefined -lc ${CMAKE_MODULE_LINKER_FLAGS}") + + set ( CMAKE_SHARED_LINKER_FLAGS "-Wl,--enable-new-dtags ${CMAKE_SHARED_LINKER_FLAGS}") + set ( CMAKE_MODULE_LINKER_FLAGS "-Wl,--enable-new-dtags ${CMAKE_MODULE_LINKER_FLAGS}") + set ( CMAKE_EXE_LINKER_FLAGS "-Wl,--enable-new-dtags ${CMAKE_EXE_LINKER_FLAGS}") + + # we profile... + if(CMAKE_BUILD_TYPE_TOLOWER MATCHES profile) + set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fprofile-arcs -ftest-coverage") + set (CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage") + endif(CMAKE_BUILD_TYPE_TOLOWER MATCHES profile) + endif (CMAKE_COMPILER_IS_GNUCXX) + if (CMAKE_C_COMPILER MATCHES "icc") + set ( CMAKE_SHARED_LINKER_FLAGS "-Wl,--fatal-warnings -Wl,--no-undefined -lc ${CMAKE_SHARED_LINKER_FLAGS}") + set ( CMAKE_MODULE_LINKER_FLAGS "-Wl,--fatal-warnings -Wl,--no-undefined -lc ${CMAKE_MODULE_LINKER_FLAGS}") + endif (CMAKE_C_COMPILER MATCHES "icc") +endif (CMAKE_SYSTEM_NAME MATCHES Linux) + +set(PHONON_LIBS phonon ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY}) +if(QT_QTDBUS_FOUND) + list(APPEND PHONON_LIBS phonon ${QT_QTDBUS_LIBRARY}) +endif(QT_QTDBUS_FOUND) + +set(EXECUTABLE_OUTPUT_PATH ${Phonon_BINARY_DIR}/bin) +if (WIN32) + set(LIBRARY_OUTPUT_PATH ${EXECUTABLE_OUTPUT_PATH}) +else (WIN32) + set(LIBRARY_OUTPUT_PATH ${Phonon_BINARY_DIR}/lib) +endif (WIN32) + +if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_C_COMPILER MATCHES "icc") + set ( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wnon-virtual-dtor -Wno-long-long -ansi -Wundef -Wcast-align -Wchar-subscripts -Wall -W -Wpointer-arith -Wformat-security -fno-check-new -fno-common") +endif (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_C_COMPILER MATCHES "icc") + +# Set up RPATH handling, so the libs are found if they are installed to a non-standard location. +# By default cmake builds the targets with full RPATH to everything in the build directory, +# but then removes the RPATH when installing. +# These two options below make it set the RPATH of the installed targets to all +# RPATH directories outside the current CMAKE_BINARY_DIR and also the library +# install directory. Alex +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) +set(CMAKE_INSTALL_RPATH "${LIB_INSTALL_DIR}" ) + +if(APPLE) + set(CMAKE_INSTALL_NAME_DIR ${LIB_INSTALL_DIR}) +endif(APPLE) + + +add_subdirectory(cmake) +add_subdirectory(phonon) +add_subdirectory(includes) +if (Q_WS_MAC AND BUILD_PHONON_QT7) + add_subdirectory(qt7) +endif (Q_WS_MAC AND BUILD_PHONON_QT7) +if (Q_WS_WIN) + add_subdirectory(ds9) +endif (Q_WS_WIN) + +if (Q_WS_X11) + add_subdirectory(gstreamer) + add_subdirectory(xine) +endif (Q_WS_X11) + +if(NOT WIN32) # pkgconfig file + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/phonon.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/phonon.pc @ONLY) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/phonon.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig) +endif(NOT WIN32) + +macro_display_feature_log() diff --git a/src/3rdparty/phonon/COPYING.LIB b/src/3rdparty/phonon/COPYING.LIB new file mode 100644 index 0000000..2d2d780 --- /dev/null +++ b/src/3rdparty/phonon/COPYING.LIB @@ -0,0 +1,510 @@ + + 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 + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/src/3rdparty/phonon/ds9/CMakeLists.txt b/src/3rdparty/phonon/ds9/CMakeLists.txt new file mode 100644 index 0000000..1bb6f6f --- /dev/null +++ b/src/3rdparty/phonon/ds9/CMakeLists.txt @@ -0,0 +1,53 @@ +# Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +# +# 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 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 <http://www.gnu.org/licenses/>. +project(phonon-ds9) +include(ConfigureChecks.cmake) + +if (BUILD_PHONON_DS9) + find_package(OPENGL REQUIRED) + + include_directories($ENV{DXDSDK_DIR}/include $ENV{DXSDK_DIR}) + set(phonon_ds9_SRCS + abstractvideorenderer.cpp + audiooutput.cpp + backend.cpp + backendnode.cpp + effect.cpp + fakesource.cpp + iodevicereader.cpp + mediagraph.cpp + mediaobject.cpp + videowidget.cpp + videorenderer_vmr9.cpp + videorenderer_soft.cpp + volumeeffect.cpp + qbasefilter.cpp + qpin.cpp + qasyncreader.cpp + qaudiocdreader.cpp + qmeminputpin.cpp + ) + + add_definitions(-DPHONON_MAKE_QT_ONLY_BACKEND -DUNICODE) + automoc4_add_library(phonon_ds9 SHARED ${phonon_ds9_SRCS}) + set_target_properties(phonon_ds9 PROPERTIES PREFIX "") + target_link_libraries(phonon_ds9 + ${PHONON_LIBS} ${QT_QTOPENGL_LIBRARY} ${OPENGL_gl_LIBRARY} + dxguid strmiids dmoguids msdmo ole32 oleaut32 uuid gdi32) + install(TARGETS phonon_ds9 + RUNTIME DESTINATION ${BIN_INSTALL_DIR}/phonon_backend + LIBRARY DESTINATION ${LIB_INSTALL_DIR} + ARCHIVE DESTINATION ${LIB_INSTALL_DIR}) + install(FILES ds9.desktop DESTINATION ${SERVICES_INSTALL_DIR}/phononbackends) +endif (BUILD_PHONON_DS9) diff --git a/src/3rdparty/phonon/ds9/ConfigureChecks.cmake b/src/3rdparty/phonon/ds9/ConfigureChecks.cmake new file mode 100644 index 0000000..c13056f --- /dev/null +++ b/src/3rdparty/phonon/ds9/ConfigureChecks.cmake @@ -0,0 +1,44 @@ +# Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +# +# 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 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 <http://www.gnu.org/licenses/>. + +# We must find: +# $DXSDK_DIR/include/d3d9.h +# $DXDSK_DIR/$LIB/dxguid.lib +# vmr9.h +# dshow.h +# strmiids.lib +# dmoguids.lib +# msdmo.lib +include(CheckCXXSourceCompiles) + +macro_push_required_vars() + +set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${CMAKE_INCLUDE_PATH} $ENV{DXSDK_DIR} $ENV{DXSDK_DIR}/include) +set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} dxguid strmiids dmoguids msdmo) + +CHECK_CXX_SOURCE_COMPILES( +"#include <d3d9.h> +#include <dshow.h> +#include <strmif.h> +#include <vmr9.h> + +int main() { }" BUILD_PHONON_DS9) + +macro_pop_required_vars() + +if (BUILD_PHONON_DS9) + message(STATUS "Found DirectShow 9 support: $ENV{DXSDK_DIR}") +else (BUILD_PHONON_DS9) + message(STATUS "DirectShow 9 support NOT found") +endif (BUILD_PHONON_DS9) diff --git a/src/3rdparty/phonon/ds9/abstractvideorenderer.cpp b/src/3rdparty/phonon/ds9/abstractvideorenderer.cpp new file mode 100644 index 0000000..e932e70 --- /dev/null +++ b/src/3rdparty/phonon/ds9/abstractvideorenderer.cpp @@ -0,0 +1,118 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#include "abstractvideorenderer.h" + +#include <QtGui/QApplication> +#include <QtGui/QDesktopWidget> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_VIDEO + +namespace Phonon +{ + namespace DS9 + { + + AbstractVideoRenderer::AbstractVideoRenderer() : + m_dstX(0), m_dstY(0), m_dstWidth(0), m_dstHeight(0), + m_active(false) + { + } + + AbstractVideoRenderer::~AbstractVideoRenderer() + { + } + + Filter AbstractVideoRenderer::getFilter() const + { + return m_filter; + } + + QSize AbstractVideoRenderer::sizeHint() const + { + QSize s = videoSize(); + if (s.isNull()) { + s = QSize(640, 480).boundedTo( QApplication::desktop()->availableGeometry().size() ); + } + return s; + } + + void AbstractVideoRenderer::setActive(bool b) + { + m_active = b; + } + + bool AbstractVideoRenderer::isActive() const + { + return m_active; + } + + void AbstractVideoRenderer::internalNotifyResize(const QSize &size, const QSize &videoSize, + Phonon::VideoWidget::AspectRatio aspectRatio, Phonon::VideoWidget::ScaleMode scaleMode) + { + //update the video rects + qreal ratio = -1.; + const int videoWidth = videoSize.width(), + videoHeight = videoSize.height(); + + switch(aspectRatio) + { + case Phonon::VideoWidget::AspectRatioAuto: + { + //preserve the aspect ratio of the video + ratio = qreal(videoWidth) / qreal(videoHeight); + } + break; + case Phonon::VideoWidget::AspectRatio4_3: + ratio = qreal(4. / 3.); + break; + case Phonon::VideoWidget::AspectRatio16_9: + ratio = qreal(16. / 9.); + break; + case Phonon::VideoWidget::AspectRatioWidget: + default: + break; + } + + const qreal realWidth = size.width(), + realHeight = size.height(); + + //reinitialization + m_dstWidth = size.width(); + m_dstHeight = size.height(); + m_dstX = m_dstY = 0; + + if (ratio > 0) { + if (realWidth / realHeight > ratio && scaleMode == Phonon::VideoWidget::FitInView + || realWidth / realHeight < ratio && scaleMode == Phonon::VideoWidget::ScaleAndCrop) { + //the height is correct, let's change the width + m_dstWidth = qRound(realHeight * ratio); + m_dstX = qRound((realWidth - realHeight * ratio) / 2.); + } else { + m_dstHeight = qRound(realWidth / ratio); + m_dstY = qRound((realHeight - realWidth / ratio) / 2.); + } + } + } + } +} + +#endif //QT_NO_PHONON_EFFECT + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/ds9/abstractvideorenderer.h b/src/3rdparty/phonon/ds9/abstractvideorenderer.h new file mode 100644 index 0000000..25c660f --- /dev/null +++ b/src/3rdparty/phonon/ds9/abstractvideorenderer.h @@ -0,0 +1,73 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ +#ifndef PHONON_ABSTRACTVIDEORENDERER_H +#define PHONON_ABSTRACTVIDEORENDERER_H + +#include "backendnode.h" + +#include <phonon/videowidget.h> + +QT_BEGIN_NAMESPACE + +class QImage; + +#ifndef QT_NO_PHONON_VIDEO + +namespace Phonon +{ + namespace DS9 + { + //this is the interface used by the videorenderer from the VideoWidget class + class AbstractVideoRenderer + { + public: + virtual ~AbstractVideoRenderer(); + + virtual void repaintCurrentFrame(QWidget *target, const QRect &rect) = 0; + virtual void notifyResize(const QSize&, Phonon::VideoWidget::AspectRatio, Phonon::VideoWidget::ScaleMode) = 0; + virtual void applyMixerSettings(qreal brightness, qreal contrast, qreal m_hue, qreal saturation) = 0; + + void setActive(bool); + bool isActive() const; + + virtual bool isNative() const = 0; + virtual QImage snapshot() const = 0; + + Filter getFilter() const; + QSize sizeHint() const; + + protected: + virtual QSize videoSize() const = 0; + + AbstractVideoRenderer(); + void internalNotifyResize(const QSize &size, const QSize &videoSize, + Phonon::VideoWidget::AspectRatio aspectRatio, Phonon::VideoWidget::ScaleMode scaleMode); + + + Filter m_filter; + int m_dstX, m_dstY, m_dstWidth, m_dstHeight; + bool m_active; + }; + } +} + +#endif //QT_NO_PHONON_VIDEO + + +QT_END_NAMESPACE + +#endif diff --git a/src/3rdparty/phonon/ds9/audiooutput.cpp b/src/3rdparty/phonon/ds9/audiooutput.cpp new file mode 100644 index 0000000..fcc062c --- /dev/null +++ b/src/3rdparty/phonon/ds9/audiooutput.cpp @@ -0,0 +1,111 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#include "audiooutput.h" +#include "mediaobject.h" + +#include <QtCore/qmath.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace DS9 + { + AudioOutput::AudioOutput(Backend *back, QObject *parent) + : BackendNode(parent), m_currentIndex(0), m_crossfadeProgress(1.), + m_device(-1), m_backend(back), m_volume(0.) + { + } + + AudioOutput::~AudioOutput() + { + } + + int AudioOutput::outputDevice() const + { + return m_device; + } + + static const qreal log10over20 = qreal(0.1151292546497022842); // ln(10) / 20 + + void AudioOutput::setVolume(qreal newVolume) + { + for(int i = 0; i < FILTER_COUNT; ++i) { + ComPointer<IBasicAudio> audio(m_filters[i], IID_IBasicAudio); + if (audio) { + const qreal currentVolume = newVolume * (m_currentIndex == i ? m_crossfadeProgress : 1-m_crossfadeProgress); + const qreal newDbVolume = (qMax(0., 1.-::log(::pow(currentVolume, -log10over20)))-1.) * 10000; + audio->put_Volume(qRound(newDbVolume)); + } + } + + if (m_volume != newVolume) { + m_volume = newVolume; + emit volumeChanged(newVolume); + } + } + + void AudioOutput::setCrossFadingProgress(short currentIndex, qreal progress) + { + m_crossfadeProgress = progress; + m_currentIndex = currentIndex; + setVolume(m_volume); + } + + bool AudioOutput::setOutputDevice(const AudioOutputDevice & newDevice) + { + //stub implementation + return setOutputDevice(newDevice.index()); + } + + qreal AudioOutput::volume() const + { + return m_volume; + } + + bool AudioOutput::setOutputDevice(int newDevice) + { + if (newDevice == m_device) { + return true; + } + + //free the previous one if it was already set + for(int i = 0; i < FILTER_COUNT; ++i) { + const Filter &oldFilter = m_filters[i]; + + Filter newFilter = m_backend->getAudioOutputFilter(newDevice); + + if (m_mediaObject && oldFilter && newFilter) { + m_mediaObject->switchFilters(i, oldFilter, newFilter); + } + + m_filters[i] = newFilter; + + + } + + m_device = newDevice; + setVolume(m_volume); + return true; + } + } +} + +QT_END_NAMESPACE + +#include "moc_audiooutput.cpp" diff --git a/src/3rdparty/phonon/ds9/audiooutput.h b/src/3rdparty/phonon/ds9/audiooutput.h new file mode 100644 index 0000000..c3bfc8b --- /dev/null +++ b/src/3rdparty/phonon/ds9/audiooutput.h @@ -0,0 +1,68 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_AUDIOOUTPUT_H +#define PHONON_AUDIOOUTPUT_H + +#include "backendnode.h" +#include <phonon/audiooutputinterface.h> + +#include "backend.h" + +struct IBaseFilter; +struct IBasicAudio; + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace DS9 + { + class AudioOutput : public BackendNode, public Phonon::AudioOutputInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::AudioOutputInterface) + public: + AudioOutput(Backend *back, QObject *parent); + ~AudioOutput(); + + // Attributes Getters: + qreal volume() const; + int outputDevice() const; + void setVolume(qreal newVolume); + bool setOutputDevice(int newDevice); + bool setOutputDevice(const AudioOutputDevice & newDevice); + void setCrossFadingProgress(short currentIndex, qreal progress); + + Q_SIGNALS: + void volumeChanged(qreal newVolume); + void audioDeviceFailed(); + + private: + short m_currentIndex; + qreal m_crossfadeProgress; + + int m_device; + Backend *m_backend; + qreal m_volume; + }; + } +} + +QT_END_NAMESPACE + +#endif // PHONON_AUDIOOUTPUT_H diff --git a/src/3rdparty/phonon/ds9/backend.cpp b/src/3rdparty/phonon/ds9/backend.cpp new file mode 100644 index 0000000..245749a --- /dev/null +++ b/src/3rdparty/phonon/ds9/backend.cpp @@ -0,0 +1,343 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#include "backend.h" +#include "backendnode.h" + +#include "audiooutput.h" +#include "effect.h" +#include "mediaobject.h" +#include "videowidget.h" +#include "volumeeffect.h" + +//windows specific (DirectX Media Object) +#include <dmo.h> + +#include <QtCore/QSettings> +#include <QtCore/QSet> +#include <QtCore/QVariant> + +#include <QtCore/QtPlugin> + +QT_BEGIN_NAMESPACE + +Q_EXPORT_PLUGIN2(phonon_ds9, Phonon::DS9::Backend); + +namespace Phonon +{ + namespace DS9 + { + bool Backend::AudioMoniker::operator==(const AudioMoniker &other) + { + return other->IsEqual(*this) == S_OK; + } + + + Backend::Backend(QObject *parent, const QVariantList &) + : QObject(parent) + { + ::CoInitialize(0); + + //registering meta types + qRegisterMetaType<HRESULT>("HRESULT"); + qRegisterMetaType<Graph>("Graph"); + } + + Backend::~Backend() + { + m_audioOutputs.clear(); + m_audioEffects.clear(); + ::CoUninitialize(); + } + + QObject *Backend::createObject(BackendInterface::Class c, QObject *parent, const QList<QVariant> &args) + { + switch (c) + { + case MediaObjectClass: + return new MediaObject(parent); + case AudioOutputClass: + return new AudioOutput(this, parent); +#ifndef QT_NO_PHONON_EFFECT + case EffectClass: + return new Effect(m_audioEffects[ args[0].toInt() ], parent); +#endif //QT_NO_PHONON_EFFECT +#ifndef QT_NO_PHONON_VIDEO + case VideoWidgetClass: + return new VideoWidget(qobject_cast<QWidget *>(parent)); +#endif //QT_NO_PHONON_VIDEO +#ifndef QT_NO_PHONON_VOLUMEFADEREFFECT + case VolumeFaderEffectClass: + return new VolumeEffect(parent); +#endif //QT_NO_PHONON_VOLUMEFADEREFFECT + default: + return 0; + } + } + + bool Backend::supportsVideo() const + { +#ifndef QT_NO_PHONON_VIDEO + return true; +#else + return false; +#endif //QT_NO_PHONON_VIDEO + } + + QStringList Backend::availableMimeTypes() const + { + QStringList ret; + { + QSettings settings(QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Multimedia\\mplayer2\\mime types"), QSettings::NativeFormat); + ret += settings.childGroups(); + } + { + QSettings settings(QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Multimedia\\wmplayer\\mime types"), QSettings::NativeFormat); + ret += settings.childGroups(); + } + + ret.removeDuplicates(); + ret.replaceInStrings("\\", "/"); + qSort(ret); + return ret; + } + + Filter Backend::getAudioOutputFilter(int index) const + { + Filter ret; + if (index >= 0 && index < m_audioOutputs.count()) { + m_audioOutputs.at(index)->BindToObject(0, 0, IID_IBaseFilter, reinterpret_cast<void**>(&ret)); + } else { + //just return the default audio renderer (not directsound) + ret = Filter(CLSID_AudioRender, IID_IBaseFilter); + } + return ret; + } + + + QList<int> Backend::objectDescriptionIndexes(Phonon::ObjectDescriptionType type) const + { + QList<int> ret; + + switch(type) + { + case Phonon::AudioOutputDeviceType: + { +#ifdef Q_OS_WINCE + ret << 0; // only one audio device with index 0 +#else + ComPointer<ICreateDevEnum> devEnum(CLSID_SystemDeviceEnum, IID_ICreateDevEnum); + if (!devEnum) { + return ret; //it is impossible to enumerate the devices + } + ComPointer<IEnumMoniker> enumMon; + HRESULT hr = devEnum->CreateClassEnumerator(CLSID_AudioRendererCategory, enumMon.pparam(), 0); + if (FAILED(hr)) { + break; + } + AudioMoniker mon; + + //let's reorder the devices so that directshound appears first + int nbds = 0; //number of directsound devices + + while (S_OK == enumMon->Next(1, mon.pparam(), 0)) { + LPOLESTR str = 0; + mon->GetDisplayName(0,0,&str); + const QString name = QString::fromUtf16((unsigned short*)str); + ComPointer<IMalloc> alloc; + ::CoGetMalloc(1, alloc.pparam()); + alloc->Free(str); + + int insert_pos = 0; + if (!m_audioOutputs.contains(mon)) { + insert_pos = m_audioOutputs.count(); + m_audioOutputs.append(mon); + } else { + insert_pos = m_audioOutputs.indexOf(mon); + } + + if (name.contains(QLatin1String("DirectSound"))) { + ret.insert(nbds++, insert_pos); + } else { + ret.append(insert_pos); + } + } +#endif + break; + } +#ifndef QT_NO_PHONON_EFFECT + case Phonon::EffectType: + { + m_audioEffects.clear(); + ComPointer<IEnumDMO> enumDMO; + HRESULT hr = ::DMOEnum(DMOCATEGORY_AUDIO_EFFECT, DMO_ENUMF_INCLUDE_KEYED, 0, 0, 0, 0, enumDMO.pparam()); + if (SUCCEEDED(hr)) { + CLSID clsid; + while (S_OK == enumDMO->Next(1, &clsid, 0, 0)) { + ret += m_audioEffects.count(); + m_audioEffects.append(clsid); + } + } + break; + } + break; +#endif //QT_NO_PHONON_EFFECT + default: + break; + } + return ret; + } + + QHash<QByteArray, QVariant> Backend::objectDescriptionProperties(Phonon::ObjectDescriptionType type, int index) const + { + QHash<QByteArray, QVariant> ret; + switch (type) + { + case Phonon::AudioOutputDeviceType: + { +#ifdef Q_OS_WINCE + ret["name"] = QLatin1String("default audio device"); +#else + const AudioMoniker &mon = m_audioOutputs[index]; + LPOLESTR str = 0; + HRESULT hr = mon->GetDisplayName(0,0, &str); + if (SUCCEEDED(hr)) { + QString name = QString::fromUtf16((unsigned short*)str); + ComPointer<IMalloc> alloc; + ::CoGetMalloc(1, alloc.pparam()); + alloc->Free(str); + ret["name"] = name.mid(name.indexOf('\\') + 1); + } +#endif + } + break; +#ifndef QT_NO_PHONON_EFFECT + case Phonon::EffectType: + { + WCHAR name[80]; // 80 is clearly stated in the MSDN doc + HRESULT hr = ::DMOGetName(m_audioEffects[index], name); + if (SUCCEEDED(hr)) { + ret["name"] = QString::fromUtf16((unsigned short*)name); + } + } + break; +#endif //QT_NO_PHONON_EFFECT + default: + break; + } + return ret; + } + + bool Backend::endConnectionChange(QSet<QObject *> objects) + { + //end of a transaction + for(QSet<QObject *>::const_iterator it = objects.begin(); it != objects.end(); ++it) { + if (BackendNode *node = qobject_cast<BackendNode*>(*it)) { + MediaObject *mo = node->mediaObject(); + if (mo) { + switch(mo->transactionState) + { + case Phonon::ErrorState: + case Phonon::StoppedState: + case Phonon::LoadingState: + //nothing to do + break; + case Phonon::PausedState: + mo->transactionState = Phonon::StoppedState; + mo->pause(); + break; + default: + mo->transactionState = Phonon::StoppedState; + mo->play(); + break; + } + + if (mo->state() == Phonon::ErrorState) + return false; + } + } + } + + return true; + } + + + bool Backend::startConnectionChange(QSet<QObject *> objects) + { + //let's save the state of the graph (before we stop it) + for(QSet<QObject *>::const_iterator it = objects.begin(); it != objects.end(); ++it) { + if (BackendNode *node = qobject_cast<BackendNode*>(*it)) { + if (MediaObject *mo = node->mediaObject()) { + if (mo->state() != Phonon::StoppedState) { + mo->transactionState = mo->state(); + mo->ensureStopped(); //we have to stop the graph.. + if (mo->state() == Phonon::ErrorState) + return false; + } + } + } + } + + return true; + } + + bool Backend::connectNodes(QObject *_source, QObject *_sink) + { + BackendNode *source = qobject_cast<BackendNode*>(_source); + if (!source) { + return false; + } + BackendNode *sink = qobject_cast<BackendNode*>(_sink); + if (!sink) { + return false; + } + + //setting the graph if needed + if (source->mediaObject() == 0 && sink->mediaObject() == 0) { + //error: no graph selected + return false; + } else if (source->mediaObject() && source->mediaObject() != sink->mediaObject()) { + //this' graph becomes the common one + source->mediaObject()->grabNode(sink); + } else if (source->mediaObject() == 0) { + //sink's graph becomes the common one + sink->mediaObject()->grabNode(source); + } + + return source->mediaObject()->connectNodes(source, sink); + } + + bool Backend::disconnectNodes(QObject *_source, QObject *_sink) + { + BackendNode *source = qobject_cast<BackendNode*>(_source); + if (!source) { + return false; + } + BackendNode *sink = qobject_cast<BackendNode*>(_sink); + if (!sink) { + return false; + } + + return source->mediaObject() == 0 || + source->mediaObject()->disconnectNodes(source, sink); + } + } +} + +QT_END_NAMESPACE + +#include "moc_backend.cpp" diff --git a/src/3rdparty/phonon/ds9/backend.h b/src/3rdparty/phonon/ds9/backend.h new file mode 100644 index 0000000..ad638f2 --- /dev/null +++ b/src/3rdparty/phonon/ds9/backend.h @@ -0,0 +1,83 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_BACKEND_H +#define PHONON_BACKEND_H + +#include "phononds9_namespace.h" +#include <phonon/backendinterface.h> +#include <phonon/phononnamespace.h> + +#include <QtCore/QList> + +#include "compointer.h" +#include "backendnode.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace DS9 + { + class AudioOutput; + class MediaObject; + + typedef Phonon::ObjectDescriptionType ObjectDescriptionType; + + class Backend : public QObject, public Phonon::BackendInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::BackendInterface) + public: + Backend(QObject *parent = 0, const QVariantList & = QVariantList()); + virtual ~Backend(); + + QObject *createObject(Phonon::BackendInterface::Class, QObject *parent, const QList<QVariant> &args); + + bool supportsVideo() const; + QStringList availableMimeTypes() const; + + QList<int> objectDescriptionIndexes(Phonon::ObjectDescriptionType type) const; + QHash<QByteArray, QVariant> objectDescriptionProperties(Phonon::ObjectDescriptionType type, int index) const; + + bool connectNodes(QObject *, QObject *); + bool disconnectNodes(QObject *, QObject *); + + //transaction management + bool startConnectionChange(QSet<QObject *>); + bool endConnectionChange(QSet<QObject *>); + + Filter getAudioOutputFilter(int index) const; + + Q_SIGNALS: + void objectDescriptionChanged(ObjectDescriptionType); + + private: + class AudioMoniker : public ComPointer<IMoniker> + { + public: + bool operator==(const AudioMoniker &other); + }; + mutable QVector<AudioMoniker> m_audioOutputs; + mutable QVector<CLSID> m_audioEffects; + }; + } +} + +QT_END_NAMESPACE + +#endif // PHONON_BACKEND_H diff --git a/src/3rdparty/phonon/ds9/backendnode.cpp b/src/3rdparty/phonon/ds9/backendnode.cpp new file mode 100644 index 0000000..7e0b3cd --- /dev/null +++ b/src/3rdparty/phonon/ds9/backendnode.cpp @@ -0,0 +1,115 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#include "backendnode.h" +#include "mediaobject.h" + +#include <ocidl.h> // ISpecifyPropertyPages +#include <olectl.h> // OleCreatePropertyFrame + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace DS9 + { + // Displays a property dialog for a filter (experimental but should be put into main + /*static void showPropertyDialog(const Filter &filter) + { + ComPointer<ISpecifyPropertyPages> prop(filter, IID_ISpecifyPropertyPages); + if (prop != 0) { + IUnknown *iunk[] = {filter}; + // Show the page. + CAUUID caGUID; + prop->GetPages(&caGUID); + OleCreatePropertyFrame( + 0, // Parent window + 0, 0, // (Reserved) + 0, // Caption for the dialog box + 1, // Number of objects (just the filter) + iunk, // Array of object pointers. + caGUID.cElems, // Number of property pages + caGUID.pElems, // Array of property page CLSIDs + 0, // Locale identifier + 0, 0 // Reserved + ); + } + }*/ + + //for now we have 2 graphs that do the same + BackendNode::BackendNode(QObject *parent) : QObject(parent), m_mediaObject(0) + { + } + + BackendNode::~BackendNode() + { + } + + void BackendNode::setMediaObject(MediaObject *mo) + { + if (m_mediaObject) { + disconnect(m_mediaObject, SIGNAL(destroyed()), this, SLOT(mediaObjectDestroyed())); + } + m_mediaObject = mo; + connect(mo, SIGNAL(destroyed()), SLOT(mediaObjectDestroyed())); + } + + void BackendNode::mediaObjectDestroyed() + { + //remove the filter from its graph + FILTER_INFO info; + for(int i = 0; i < FILTER_COUNT; ++i) { + const Filter &filter = m_filters[i]; + if (!filter) + continue; + filter->QueryFilterInfo(&info); + if (info.pGraph) { + HRESULT hr = info.pGraph->RemoveFilter(filter); + Q_ASSERT(SUCCEEDED(hr)); + Q_UNUSED(hr); + info.pGraph->Release(); + } + } + m_mediaObject = 0; + } + + QList<InputPin> BackendNode::pins(const Filter &filter, PIN_DIRECTION wantedDirection) + { + QList<InputPin> ret; + if (filter) { + ComPointer<IEnumPins> enumPin; + HRESULT hr = filter->EnumPins(enumPin.pparam()); + Q_UNUSED(hr); + Q_ASSERT( SUCCEEDED(hr)); + InputPin pin; + while (enumPin->Next(1, pin.pparam(), 0) == S_OK) { + PIN_DIRECTION dir; + hr = pin->QueryDirection(&dir); + Q_ASSERT( SUCCEEDED(hr)); + if (dir == wantedDirection) { + ret.append(pin); + } + } + } + return ret; + } + } +} + +QT_END_NAMESPACE + +#include "moc_backendnode.cpp" diff --git a/src/3rdparty/phonon/ds9/backendnode.h b/src/3rdparty/phonon/ds9/backendnode.h new file mode 100644 index 0000000..17bd3fb --- /dev/null +++ b/src/3rdparty/phonon/ds9/backendnode.h @@ -0,0 +1,73 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_BACKENDNODE_H +#define PHONON_BACKENDNODE_H + +#include "phononds9_namespace.h" + +#include <QtCore/QObject> +#include <QtCore/QVector> + +#include "compointer.h" + +QT_BEGIN_NAMESPACE + + + +namespace Phonon +{ + namespace DS9 + { + class MediaObject; + typedef ComPointer<IPin> InputPin; + typedef ComPointer<IPin> OutputPin; + typedef ComPointer<IBaseFilter> Filter; + typedef ComPointer<IGraphBuilder> Graph; + + class BackendNode : public QObject + { + Q_OBJECT + + public: + BackendNode(QObject *parent); + virtual ~BackendNode(); + + MediaObject *mediaObject() const {return m_mediaObject;} + + static QList<InputPin> pins(const Filter &, PIN_DIRECTION); + + Filter filter(int index) const { return m_filters[index]; } + //add a pointer to the base Media Object (giving access to the graph and error management) + void setMediaObject(MediaObject *mo); + + //called by the connections to tell the node that it's been connection to anothe one through its 'inpin' input port + virtual void connected(BackendNode *, const InputPin& inpin) {} + + private Q_SLOTS: + void mediaObjectDestroyed(); + + protected: + Filter m_filters[FILTER_COUNT]; + MediaObject *m_mediaObject; + }; + } +} + +QT_END_NAMESPACE + +#endif diff --git a/src/3rdparty/phonon/ds9/compointer.h b/src/3rdparty/phonon/ds9/compointer.h new file mode 100644 index 0000000..180febf --- /dev/null +++ b/src/3rdparty/phonon/ds9/compointer.h @@ -0,0 +1,114 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_COMPOINTER_H +#define PHONON_COMPOINTER_H + +#include <windows.h> +#include <dshow.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace DS9 + { + template<class T> class ComPointer + { + public: + explicit ComPointer(T *t = 0) : m_t(t) + { + } + + explicit ComPointer( const IID &clsid, const IID &iid) : m_t(0) + { + ::CoCreateInstance(clsid, 0, CLSCTX_INPROC_SERVER, iid, + reinterpret_cast<void**>(&m_t)); + } + + explicit ComPointer(IUnknown *_unk, const GUID &guid) : m_t(0) + { + if (_unk) { + _unk->QueryInterface(guid, reinterpret_cast<void**>(&m_t)); + } + } + + ComPointer(const ComPointer<T> &other) : m_t(other.m_t) + { + if (m_t) { + m_t->AddRef(); + } + } + + ComPointer<T> &operator=(const ComPointer<T> &other) + { + if (other.m_t) { + other.m_t->AddRef(); + } + if (m_t) { + m_t->Release(); + } + m_t = other.m_t; + return *this; + } + + T *operator->() const + { + return m_t; + } + + operator T*() const + { + return m_t; + } + + //the following method first reinitialize their value to avoid mem leaks + T ** pparam() + { + if (m_t) { + m_t->Release(); + m_t = 0; + } + return &m_t; + } + + bool operator==(const ComPointer<T> &other) const + { + return m_t == other.m_t; + } + + bool operator!=(const ComPointer<T> &other) const + { + return m_t != other.m_t; + } + + ~ComPointer() + { + if (m_t) { + m_t->Release(); + } + } + + private: + T *m_t; + }; + } +} + +QT_END_NAMESPACE + +#endif diff --git a/src/3rdparty/phonon/ds9/ds9.desktop b/src/3rdparty/phonon/ds9/ds9.desktop new file mode 100644 index 0000000..370011e --- /dev/null +++ b/src/3rdparty/phonon/ds9/ds9.desktop @@ -0,0 +1,51 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=PhononBackend +MimeType=application/x-annodex;video/quicktime;video/x-quicktime;audio/x-m4a;application/x-quicktimeplayer;video/mkv;video/msvideo;video/x-msvideo;video/x-flic;audio/x-aiff;audio/aiff;audio/x-pn-aiff;audio/x-realaudio;audio/basic;audio/x-basic;audio/x-pn-au;audio/x-8svx;audio/8svx;audio/x-16sv;audio/168sv;image/x-ilbm;image/ilbm;video/x-anim;video/anim;image/png;image/x-png;video/mng;video/x-mng;audio/x-ogg;audio/x-speex+ogg;application/ogg;application/ogg;audio/vnd.rn-realaudio;audio/x-pn-realaudio-plugin;audio/x-real-audio;application/vnd.rn-realmedia;video/mpeg;video/x-mpeg;audio/x-wav;audio/wav;audio/x-pn-wav;audio/x-pn-windows-acm;audio/mpeg2;audio/x-mpeg2;audio/mpeg3;audio/x-mpeg3;audio/mpeg;audio/x-mpeg;x-mpegurl;audio/x-mpegurl;audio/mp3;audio/mpeg; +X-KDE-Library=phonon_ds9 +X-KDE-PhononBackendInfo-InterfaceVersion=1 +X-KDE-PhononBackendInfo-Version=0.1 +X-KDE-PhononBackendInfo-Website=http://www.trolltech.com/ +InitialPreference=15 + +Name=DirectShow9 +Name[pa]=ਡਾਇਰੈਕਸ਼ੋ9 +Name[sk]=DirectShow 9 +Name[sl]=DirectShow 9 +Name[sr]=Директшоу‑9 +Name[sr@latin]=DirectShow‑9 +Name[sv]=Directshow 9 +Name[x-test]=xxDirectShow9xx + +Comment=Phonon DirectShow9 backend +Comment[bg]=Phonon DirectShow9 +Comment[ca]=Dorsal DirectShow9 del Phonon +Comment[da]=DirectShow9-backend til Phonon +Comment[de]=Phonon-Treiber für DirectShow9 +Comment[el]=Σύστημα υποστήριξης DirectShow9 του Phonon +Comment[es]=Motor DirectShow9 para Phonon +Comment[et]=Phononi DirectShow9 taustaprogramm +Comment[fr]=Système de gestion DirectShow9 pour Phonon +Comment[ga]=Inneall DirectShow9 le haghaidh Phonon +Comment[gl]=Infraestrutura de DirectShow9 para Phonon +Comment[it]=Motore DirectShow9 di Phonon +Comment[ja]=Phonon DirectShow9 バックエンド +Comment[ko]=Phonon DirectShow9 백엔드 +Comment[lv]=Phonon DirectShow9 aizmugure +Comment[nds]=Phonon-Hülpprogrmm DirectShow9 +Comment[nl]=DirectShow9-backend (Phonon) +Comment[nn]=Phonon-motor for DirectShow9 +Comment[pa]=ਫੋਨੋਨ ਡਾਇਰੈਕਟਸ਼ੋ9 ਬੈਕਐਂਡ +Comment[pl]=Obsługa DirectShow9 przez Phonon +Comment[pt]=Infra-estrutura do DirectShow9 para o Phonon +Comment[pt_BR]=Infraestrutura Phonon DirectShow9 +Comment[sk]=Phonon DirectShow 9 podsystém +Comment[sl]=Phononova Hrbtenica DirectShow 9 +Comment[sr]=Директшоу‑9 као позадина Фонона +Comment[sr@latin]=DirectShow‑9 kao pozadina Phonona +Comment[sv]=Phonon Directshow 9-gränssnitt +Comment[tr]=Phonon DirectShow9 arka ucu +Comment[uk]=Сервер DirectShow9 для Phonon +Comment[x-test]=xxPhonon DirectShow9 backendxx +Comment[zh_CN]=Phonon DirectShow9 后端 +Comment[zh_TW]=Phonon DirectShow9 後端介面 diff --git a/src/3rdparty/phonon/ds9/effect.cpp b/src/3rdparty/phonon/ds9/effect.cpp new file mode 100644 index 0000000..dc4ac3d --- /dev/null +++ b/src/3rdparty/phonon/ds9/effect.cpp @@ -0,0 +1,153 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#include "effect.h" +#include <phonon/effectparameter.h> + +#include <medparam.h> +#include <dmo.h> +#include <dmodshow.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_EFFECT + +namespace Phonon +{ + namespace DS9 + { + Effect::Effect(CLSID effectClass, QObject *parent) + : BackendNode(parent) + { + //creation of the filter + for(int i = 0; i < FILTER_COUNT; ++i) { + Filter &filter = m_filters[i]; + filter = Filter(CLSID_DMOWrapperFilter, IID_IBaseFilter); + Q_ASSERT(filter); + ComPointer<IDMOWrapperFilter> wrapper(filter, IID_IDMOWrapperFilter); + Q_ASSERT(wrapper); + wrapper->Init(effectClass, DMOCATEGORY_AUDIO_EFFECT); + } + } + + Effect::Effect(QObject *parent) : BackendNode(parent) + { + //at this point the QVector of Filter should be filled + } + + Effect::~Effect() + { + } + + QList<Phonon::EffectParameter> Effect::parameters() const + { + QList<Phonon::EffectParameter> ret; + ComPointer<IMediaParamInfo> paramInfo(m_filters[0], IID_IMediaParamInfo); + if (!paramInfo) { + return ret; + } + DWORD paramCount = 0; + paramInfo->GetParamCount( ¶mCount); + + for(quint32 i = 0; i < paramCount; i++) { + MP_PARAMINFO info; + HRESULT hr = paramInfo->GetParamInfo(i, &info); + Q_ASSERT(SUCCEEDED(hr)); + WCHAR *name = 0; + hr = paramInfo->GetParamText(i, &name); + Q_ASSERT(SUCCEEDED(hr)); + QVariant def, min, max; + + QVariantList values; + + switch(info.mpType) + { + case MPT_ENUM: + { + WCHAR *current = name; + current += wcslen(current) + 1; //skip the name + current += wcslen(current) + 1; //skip the unit + for(; *current; current += wcslen(current) + 1) { + values.append( QString::fromUtf16((unsigned short*)current) ); + } + } + //FALLTHROUGH + case MPT_INT: + def = int(info.mpdNeutralValue); + min = int(info.mpdMinValue); + max = int(info.mpdMaxValue); + break; + case MPT_FLOAT: + def = info.mpdNeutralValue; + min = info.mpdMinValue; + max = info.mpdMaxValue; + break; + case MPT_BOOL: + def = bool(info.mpdNeutralValue); + break; + case MPT_MAX: + //Reserved ms-help://MS.PSDKSVR2003R2.1033/directshow/htm/mp_typeenumeration.htm + break; + } + + Phonon::EffectParameter::Hints hint = info.mopCaps == MP_CAPS_CURVE_INVSQUARE ? + Phonon::EffectParameter::LogarithmicHint : Phonon::EffectParameter::Hints(0); + + const QString n = QString::fromUtf16((unsigned short*)name); + ret.append(Phonon::EffectParameter(i, n, hint, def, min, max, values)); + ::CoTaskMemFree(name); //let's free the memory + } + return ret; + } + + QVariant Effect::parameterValue(const Phonon::EffectParameter &p) const + { + QVariant ret; + ComPointer<IMediaParams> params(m_filters[0], IID_IMediaParams); + Q_ASSERT(params); + MP_DATA data; + HRESULT hr = params->GetParam(p.id(), &data); + if(SUCCEEDED(hr)) + return data; + else + return QVariant(); + } + + void Effect::setParameterValue(const Phonon::EffectParameter &p, const QVariant &v) + { + if (v.isNull()) { + return; + } + + for(int i=0; i < FILTER_COUNT ; ++i) { + const Filter &filter = m_filters[i]; + ComPointer<IMediaParams> params(filter, IID_IMediaParams); + Q_ASSERT(params); + + MP_DATA data = float(v.toDouble()); + params->SetParam(p.id(), data); + } + } + + } +} + +#endif //QT_NO_PHONON_EFFECT + +QT_END_NAMESPACE + +#include "moc_effect.cpp" diff --git a/src/3rdparty/phonon/ds9/effect.h b/src/3rdparty/phonon/ds9/effect.h new file mode 100644 index 0000000..50f3ea2 --- /dev/null +++ b/src/3rdparty/phonon/ds9/effect.h @@ -0,0 +1,59 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_AUDIOEFFECT_H +#define PHONON_AUDIOEFFECT_H + +#include <QtCore/QObject> +#include <phonon/effectinterface.h> +#include "backendnode.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_EFFECT + +namespace Phonon +{ + namespace DS9 + { + class EffectInterface; + + class Effect : public BackendNode, public Phonon::EffectInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::EffectInterface) + public: + Effect(CLSID effectClass, QObject *parent); + ~Effect(); + + QList<Phonon::EffectParameter> parameters() const; + QVariant parameterValue(const Phonon::EffectParameter &) const; + void setParameterValue(const Phonon::EffectParameter &, const QVariant &); + + + protected: + //this is the constructor called by the explicit subclasses of effect + Effect(QObject *parent); + }; + } +} + +#endif //QT_NO_PHONON_EFFECT + +QT_END_NAMESPACE + +#endif // PHONON_AUDIOEFFECT_H diff --git a/src/3rdparty/phonon/ds9/fakesource.cpp b/src/3rdparty/phonon/ds9/fakesource.cpp new file mode 100644 index 0000000..9a61a2e --- /dev/null +++ b/src/3rdparty/phonon/ds9/fakesource.cpp @@ -0,0 +1,166 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#include "fakesource.h" +#include "qpin.h" + +#include <dshow.h> +#include <initguid.h> +#include <dvdmedia.h> // VIDEOINFOHEADER2 + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace DS9 + { + static WAVEFORMATEX g_defaultWaveFormat = {WAVE_FORMAT_PCM, 2, 44100, 176400, 4, 16, 0}; + static BITMAPINFOHEADER g_defautBitmapHeader = { sizeof(BITMAPINFOHEADER), 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}; + static VIDEOINFOHEADER2 g_defaultVideoInfo = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + class FakePin : public QPin + { + public: + FakePin(FakeSource *source, const AM_MEDIA_TYPE &mt) : + QPin(source, PINDIR_OUTPUT, QVector<AM_MEDIA_TYPE>() << mt), m_source(source) + { + setAvailable(true); + } + + ~FakePin() + { + } + + + STDMETHODIMP Disconnect() + { + HRESULT hr = QPin::Disconnect(); + if (SUCCEEDED(hr)) { + setAvailable(true); + } + return hr; + } + + + STDMETHODIMP Connect(IPin *pin, const AM_MEDIA_TYPE *type) + { + HRESULT hr = QPin::Connect(pin, type); + if (SUCCEEDED(hr)) { + setAvailable(false); + } + return hr; + } + + private: + void setAvailable(bool avail) + { + if (mediaTypes().first().majortype == MEDIATYPE_Audio) { + if (avail) { + m_source->addAvailableAudioPin(this); + } else { + m_source->removeAvailableAudioPin(this); + } + } else { + if (avail) { + m_source->addAvailableVideoPin(this); + } else { + m_source->removeAvailableVideoPin(this); + } + } + } + + FakeSource *m_source; + + + }; + + FakeSource::FakeSource() : QBaseFilter(CLSID_NULL) + { + createFakeAudioPin(); + createFakeVideoPin(); + } + + FakeSource::~FakeSource() + { + } + + void FakeSource::addAvailableAudioPin(FakePin *pin) + { + availableAudioPins += pin; + } + + void FakeSource::addAvailableVideoPin(FakePin *pin) + { + availableVideoPins += pin; + } + + void FakeSource::removeAvailableAudioPin(FakePin *pin) + { + availableAudioPins -= pin; + + if (availableAudioPins.isEmpty()) { + createFakeAudioPin(); + } + } + + void FakeSource::removeAvailableVideoPin(FakePin *pin) + { + availableVideoPins -= pin; + + if (availableVideoPins.isEmpty()) { + createFakeVideoPin(); + } + } + + void FakeSource::createFakeAudioPin() + { + AM_MEDIA_TYPE mt; + qMemSet(&mt, 0, sizeof(AM_MEDIA_TYPE)); + mt.majortype = MEDIATYPE_Audio; + mt.subtype = MEDIASUBTYPE_PCM; + mt.formattype = FORMAT_WaveFormatEx; + mt.lSampleSize = 2; + + //fake the format (stereo 44.1 khz stereo 16 bits) + mt.cbFormat = sizeof(WAVEFORMATEX); + mt.pbFormat = reinterpret_cast<BYTE*>(&g_defaultWaveFormat); + + new FakePin(this, mt); + } + + void FakeSource::createFakeVideoPin() + { + AM_MEDIA_TYPE mt; + qMemSet(&mt, 0, sizeof(AM_MEDIA_TYPE)); + mt.majortype = MEDIATYPE_Video; + mt.subtype = MEDIASUBTYPE_RGB32; + mt.formattype = FORMAT_VideoInfo2; + mt.bFixedSizeSamples = 1; + + g_defaultVideoInfo.bmiHeader = g_defautBitmapHeader; + + //fake the format + mt.cbFormat = sizeof(VIDEOINFOHEADER2); + mt.pbFormat = reinterpret_cast<BYTE*>(&g_defaultVideoInfo); + + new FakePin(this, mt); + } + + } +} + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/ds9/fakesource.h b/src/3rdparty/phonon/ds9/fakesource.h new file mode 100644 index 0000000..a32e0c2 --- /dev/null +++ b/src/3rdparty/phonon/ds9/fakesource.h @@ -0,0 +1,54 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_FAKESOURCE_H +#define PHONON_FAKESOURCE_H + +#include <QtCore/QSet> + +#include "qbasefilter.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace DS9 + { + class FakePin; + class FakeSource : public QBaseFilter + { + public: + FakeSource(); + ~FakeSource(); + + void addAvailableAudioPin(FakePin*); + void addAvailableVideoPin(FakePin*); + void removeAvailableAudioPin(FakePin*); + void removeAvailableVideoPin(FakePin*); + private: + void createFakeVideoPin(); + void createFakeAudioPin(); + + QSet<FakePin*> availableAudioPins; + QSet<FakePin*> availableVideoPins; + }; + } +} + +QT_END_NAMESPACE + +#endif diff --git a/src/3rdparty/phonon/ds9/iodevicereader.cpp b/src/3rdparty/phonon/ds9/iodevicereader.cpp new file mode 100644 index 0000000..ec10278 --- /dev/null +++ b/src/3rdparty/phonon/ds9/iodevicereader.cpp @@ -0,0 +1,228 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#include "iodevicereader.h" +#include "qasyncreader.h" + +#include "mediagraph.h" + +#include <phonon/streaminterface.h> + +#include <dshow.h> +#include <initguid.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + +namespace Phonon +{ + namespace DS9 + { + //these mediatypes define a stream, its type will be autodetected by DirectShow + static QVector<AM_MEDIA_TYPE> getMediaTypes() + { + AM_MEDIA_TYPE mt; + mt.majortype = MEDIATYPE_Stream; + mt.bFixedSizeSamples = TRUE; + mt.bTemporalCompression = FALSE; + mt.lSampleSize = 1; + mt.formattype = GUID_NULL; + mt.pUnk = 0; + mt.cbFormat = 0; + mt.pbFormat = 0; + + QVector<AM_MEDIA_TYPE> ret; + //normal auto-detect stream + mt.subtype = MEDIASUBTYPE_NULL; + ret << mt; + //AVI stream + mt.subtype = MEDIASUBTYPE_Avi; + ret << mt; + //WAVE stream + mt.subtype = MEDIASUBTYPE_WAVE; + ret << mt; + return ret; + } + + class StreamReader : public QAsyncReader, public Phonon::StreamInterface + { + public: + StreamReader(QBaseFilter *parent, const Phonon::MediaSource &source, const MediaGraph *mg) : + QAsyncReader(parent, getMediaTypes()), + m_seekable(false), m_pos(0), m_size(-1), m_mediaGraph(mg) + { + connectToSource(source); + } + + //for Phonon::StreamInterface + void writeData(const QByteArray &data) + { + QWriteLocker locker(&m_lock); + m_pos += data.size(); + m_buffer += data; + } + + void endOfData() + { + } + + void setStreamSize(qint64 newSize) + { + QWriteLocker locker(&m_lock); + m_size = newSize; + } + + qint64 streamSize() const + { + QReadLocker locker(&m_lock); + return m_size; + } + + void setStreamSeekable(bool s) + { + QWriteLocker locker(&m_lock); + m_seekable = s; + } + + bool streamSeekable() const + { + QReadLocker locker(&m_lock); + return m_seekable; + } + + void setCurrentPos(qint64 pos) + { + QWriteLocker locker(&m_lock); + m_pos = pos; + seekStream(pos); + m_buffer.clear(); + } + + qint64 currentPos() const + { + QReadLocker locker(&m_lock); + return m_pos; + } + + int currentBufferSize() const + { + QReadLocker locker(&m_lock); + return m_buffer.size(); + } + + //virtual pure members + + //implementation from IAsyncReader + STDMETHODIMP Length(LONGLONG *total, LONGLONG *available) + { + QReadLocker locker(&m_lock); + if (total) { + *total = m_size; + } + + if (available) { + *available = m_size; + } + + return S_OK; + } + + + HRESULT read(LONGLONG pos, LONG length, BYTE *buffer, LONG *actual) + { + QMutexLocker locker(&m_mutexRead); + + if (m_mediaGraph->isStopping()) { + return VFW_E_WRONG_STATE; + } + + if(streamSize() != 1 && pos + length > streamSize()) { + //it tries to read outside of the boundaries + return E_FAIL; + } + + if (currentPos() - currentBufferSize() != pos) { + if (!streamSeekable()) { + return S_FALSE; + } + setCurrentPos(pos); + } + + int oldSize = currentBufferSize(); + while (currentBufferSize() < int(length)) { + needData(); + if (m_mediaGraph->isStopping()) { + return VFW_E_WRONG_STATE; + } + + if (oldSize == currentBufferSize()) { + break; //we didn't get any data + } + oldSize = currentBufferSize(); + } + + DWORD bytesRead = qMin(currentBufferSize(), int(length)); + { + QWriteLocker locker(&m_lock); + qMemCopy(buffer, m_buffer.data(), bytesRead); + //truncate the buffer + m_buffer = m_buffer.mid(bytesRead); + } + + if (actual) { + *actual = bytesRead; //initialization + } + + return bytesRead == length ? S_OK : S_FALSE; + } + + public: + //for Phonon::StreamInterface + QByteArray m_buffer; + bool m_seekable; + qint64 m_pos; + qint64 m_size; + + QMutex m_mutexRead; + const MediaGraph *m_mediaGraph; + }; + + IODeviceReader::IODeviceReader(const Phonon::MediaSource &source, const MediaGraph *mg) : + QBaseFilter(CLSID_NULL) + { + //create the output pin + m_streamReader = new StreamReader(this, source, mg); + } + + IODeviceReader::~IODeviceReader() + { + } + + STDMETHODIMP IODeviceReader::Stop() + { + HRESULT hr = QBaseFilter::Stop(); + m_streamReader->enoughData(); //this asks to cancel any blocked call to needData + return hr; + } + + } +} + +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/ds9/iodevicereader.h b/src/3rdparty/phonon/ds9/iodevicereader.h new file mode 100644 index 0000000..af4b271 --- /dev/null +++ b/src/3rdparty/phonon/ds9/iodevicereader.h @@ -0,0 +1,57 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_IODEVICEREADER_H +#define PHONON_IODEVICEREADER_H + +#include "qbasefilter.h" + +#include <phonon/mediasource.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + +namespace Phonon +{ + class MediaSource; + namespace DS9 + { + class MediaGraph; + class StreamReader; + + //for the streams... + //the filter + class IODeviceReader : public QBaseFilter + { + public: + IODeviceReader(const MediaSource &source, const MediaGraph *); + ~IODeviceReader(); + STDMETHODIMP Stop(); + + private: + StreamReader *m_streamReader; + }; + + } +} + +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + +QT_END_NAMESPACE + +#endif diff --git a/src/3rdparty/phonon/ds9/lgpl-2.1.txt b/src/3rdparty/phonon/ds9/lgpl-2.1.txt new file mode 100644 index 0000000..5ab7695 --- /dev/null +++ b/src/3rdparty/phonon/ds9/lgpl-2.1.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[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 + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/src/3rdparty/phonon/ds9/lgpl-3.txt b/src/3rdparty/phonon/ds9/lgpl-3.txt new file mode 100644 index 0000000..fc8a5de --- /dev/null +++ b/src/3rdparty/phonon/ds9/lgpl-3.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + 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 that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU 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 as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/src/3rdparty/phonon/ds9/mediagraph.cpp b/src/3rdparty/phonon/ds9/mediagraph.cpp new file mode 100644 index 0000000..31a0622 --- /dev/null +++ b/src/3rdparty/phonon/ds9/mediagraph.cpp @@ -0,0 +1,1099 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#include "fakesource.h" +#include "iodevicereader.h" +#include "qaudiocdreader.h" + +#include "mediagraph.h" +#include "mediaobject.h" + + +#include <QtCore/QUrl> +#include <QtCore/QDebug> + +#include <qnetwork.h> + + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace DS9 + { + //description of a connection + struct GraphConnection + { + Filter output; + int outputOffset; + Filter input; + int inputOffset; + }; + + static QList<GraphConnection> getConnections(Filter source) + { + QList<GraphConnection> ret; + int outOffset = 0; + const QList<OutputPin> outputs = BackendNode::pins(source, PINDIR_OUTPUT); + for (int i = 0; i < outputs.count(); ++i) { + InputPin input; + if (outputs.at(i)->ConnectedTo(input.pparam()) == S_OK) { + PIN_INFO info; + input->QueryPinInfo(&info); + Filter current(info.pFilter); + if (current) { + //this is a valid connection + const int inOffset = BackendNode::pins(current, PINDIR_INPUT).indexOf(input); + const GraphConnection connection = {source, outOffset, current, inOffset}; + ret += connection; + ret += getConnections(current); //get subsequent connections + } + } + outOffset++; + } + return ret; + } + + static HRESULT saveToFile(Graph graph, const QString &filepath) + { + const WCHAR wszStreamName[] = L"ActiveMovieGraph"; + HRESULT hr; + ComPointer<IStorage> storage; + + // First, create a document file that will hold the GRF file + hr = StgCreateDocfile((OLECHAR*)filepath.utf16(), + STGM_CREATE | STGM_TRANSACTED | STGM_READWRITE | + STGM_SHARE_EXCLUSIVE, + 0, storage.pparam()); + + if (FAILED(hr)) { + return hr; + } + + // Next, create a stream to store. + ComPointer<IStream> stream; + hr = storage->CreateStream(wszStreamName, + STGM_WRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, + 0, 0, stream.pparam()); + + if (FAILED(hr)) { + return hr; + } + + // The IpersistStream::Save method converts a stream into a persistent object. + ComPointer<IPersistStream> persist(graph, IID_IPersistStream); + hr = persist->Save(stream, TRUE); + if (SUCCEEDED(hr)) { + hr = storage->Commit(STGC_DEFAULT); + } + + return hr; + } + + + MediaGraph::MediaGraph(MediaObject *mo, short index) : + m_graph(CLSID_FilterGraph, IID_IGraphBuilder), + m_fakeSource(new FakeSource()), + m_hasVideo(false), m_hasAudio(false), m_connectionsDirty(false), + m_isStopping(false), m_isSeekable(false), m_result(S_OK), + m_index(index), m_renderId(0), m_seekId(0), + m_currentTime(0), m_totalTime(0), m_mediaObject(mo) + { + m_mediaControl = ComPointer<IMediaControl>(m_graph, IID_IMediaControl); + Q_ASSERT(m_mediaControl); + m_mediaSeeking = ComPointer<IMediaSeeking>(m_graph, IID_IMediaSeeking); + Q_ASSERT(m_mediaSeeking); + + HRESULT hr = m_graph->AddFilter(m_fakeSource, 0); + if (m_mediaObject->catchComError(hr)) { + return; + } + } + + MediaGraph::~MediaGraph() + { + } + + short MediaGraph::index() const + { + return m_index; + } + + void MediaGraph::grabNode(BackendNode *node) + { + grabFilter(node->filter(m_index)); + } + + void MediaGraph::grabFilter(Filter filter) + { + if (filter) { + FILTER_INFO info; + filter->QueryFilterInfo(&info); + if (info.pGraph != m_graph) { + if (info.pGraph) { + m_mediaObject->catchComError(info.pGraph->RemoveFilter(filter)); + } + m_mediaObject->catchComError(m_graph->AddFilter(filter, 0)); + } + if (info.pGraph) { + info.pGraph->Release(); + } + } + } + + void MediaGraph::switchFilters(Filter oldFilter, Filter newFilter) + { + OAFilterState state = syncGetRealState(); + if (state != State_Stopped) { + ensureStopped(); //to do the transaction + } + + + OutputPin connected; + { + InputPin pin = BackendNode::pins(oldFilter, PINDIR_INPUT).first(); + pin->ConnectedTo(connected.pparam()); + } + + m_graph->RemoveFilter(oldFilter); + m_graph->AddFilter(newFilter, 0); + + if (connected) { + InputPin pin = BackendNode::pins(newFilter, PINDIR_INPUT).first(); + //let's reestablish the connections + m_graph->Connect(connected, pin); + } + + switch(state) + { + case State_Running: + play(); + break; + case State_Paused: + pause(); + break; + default: + break; + } + + } + + OAFilterState MediaGraph::syncGetRealState() const + { + OAFilterState state; + m_mediaControl->GetState(INFINITE, &state); + return state; + } + + + + void MediaGraph::ensureSourceDisconnected() + { + for (int i = 0; i < m_sinkConnections.count(); ++i) { + const Filter currentFilter = m_sinkConnections.at(i)->filter(m_index); + const QList<InputPin> inputs = BackendNode::pins(currentFilter, PINDIR_INPUT); + const QList<InputPin> outputs = BackendNode::pins(m_fakeSource, PINDIR_OUTPUT); + + for (int i = 0; i < inputs.count(); ++i) { + for (int o = 0; o < outputs.count(); o++) { + tryDisconnect(outputs.at(o), inputs.at(i)); + } + + for (int d = 0; d < m_decoderPins.count(); ++d) { + tryDisconnect(m_decoderPins.at(d), inputs.at(i)); + } + } + } + } + + void MediaGraph::ensureSourceConnectedTo(bool force) + { + if (m_connectionsDirty == false && force == false) { + return; + } + + m_connectionsDirty = false; + ensureSourceDisconnected(); + + //reconnect the pins + for (int i = 0; i < m_sinkConnections.count(); ++i) { + const Filter currentFilter = m_sinkConnections.at(i)->filter(m_index); + const QList<InputPin> inputs = BackendNode::pins(currentFilter, PINDIR_INPUT); + for(int i = 0; i < inputs.count(); ++i) { + //we ensure the filter belongs to the graph + grabFilter(currentFilter); + + for (int d = 0; d < m_decoderPins.count(); ++d) { + //a decoder has only one output + if (tryConnect(m_decoderPins.at(d), inputs.at(i))) { + break; + } + } + } + } + } + + QList<Filter> MediaGraph::getAllFilters(Graph graph) + { + QList<Filter> ret; + ComPointer<IEnumFilters> enumFilters; + graph->EnumFilters(enumFilters.pparam()); + Filter current; + while( enumFilters && enumFilters->Next(1, current.pparam(), 0) == S_OK) { + ret += current; + } + return ret; + } + + QList<Filter> MediaGraph::getAllFilters() const + { + return getAllFilters(m_graph); + } + + + bool MediaGraph::isSeekable() const + { + return m_isSeekable; + } + + qint64 MediaGraph::absoluteTotalTime() const + { + if (m_seekId) { + return m_totalTime; + } else { + qint64 ret = 0; + if (m_mediaSeeking) { + m_mediaSeeking->GetDuration(&ret); + ret /= 10000; //convert to milliseconds + } + return ret; + } + } + + qint64 MediaGraph::absoluteCurrentTime() const + { + if (m_seekId) { + return m_currentTime; + } else { + qint64 ret = -1; + if (m_mediaSeeking) { + HRESULT hr = m_mediaSeeking->GetCurrentPosition(&ret); + if (FAILED(hr)) { + return ret; + } + ret /= 10000; //convert to milliseconds + } + return ret; + } + } + + Phonon::MediaSource MediaGraph::mediaSource() const + { + return m_mediaSource; + } + + void MediaGraph::play() + { + ensureSourceConnectedTo(); + m_mediaObject->workerThread()->addStateChangeRequest(m_graph, State_Running, m_decoders); + } + + void MediaGraph::pause() + { + ensureSourceConnectedTo(); + m_mediaObject->workerThread()->addStateChangeRequest(m_graph, State_Paused, m_decoders); + } + + HRESULT MediaGraph::renderResult() const + { + return m_result; + } + + bool MediaGraph::isStopping() const + { + return m_isStopping; + } + + Graph MediaGraph::graph() const + { + return m_graph; + } + + void MediaGraph::stop() + { + if (!isLoading()) { + ensureStopped(); + absoluteSeek(0); //resets the clock + } else { + m_mediaObject->workerThread()->abortCurrentRender(m_renderId); + m_renderId = 0; //cancels current loading + } + m_mediaObject->workerThread()->addStateChangeRequest(m_graph, State_Stopped); + } + + void MediaGraph::ensureStopped() + { + m_isStopping = true; + //special case here because we want stopped to be synchronous + m_graph->Abort(); + m_mediaControl->Stop(); + OAFilterState dummy; + //this will wait until the change is effective + m_mediaControl->GetState(INFINITE, &dummy); + m_isStopping = false; + } + + bool MediaGraph::isLoading() const + { + return m_renderId != 0; + } + + void MediaGraph::absoluteSeek(qint64 time) + { + //this just sends a request + if (m_seekId == 0) { + m_currentTime = absoluteCurrentTime(); + m_totalTime = absoluteTotalTime(); + } + m_seekId = m_mediaObject->workerThread()->addSeekRequest(m_graph, time); + } + + HRESULT MediaGraph::removeFilter(const Filter& filter) + { + FILTER_INFO info; + filter->QueryFilterInfo(&info); +#ifdef GRAPH_DEBUG + qDebug() << "removeFilter" << QString::fromUtf16(info.achName); +#endif + if (info.pGraph) { + info.pGraph->Release(); + return m_graph->RemoveFilter(filter); + } + + //already removed + return S_OK; + } + + HRESULT MediaGraph::cleanup() + { + stop(); + + ensureSourceDisconnected(); + + QList<Filter> list = m_decoders; + if (m_demux) { + list << m_demux; + } + if (m_realSource) { + list << m_realSource; + } + list << m_decoders; + + for (int i = 0; i < m_decoders.count(); ++i) { + list += getFilterChain(m_demux, m_decoders.at(i)); + } + + for (int i = 0; i < list.count(); ++i) { + removeFilter(list.at(i)); + } + + //Let's reinitialize the internal lists + m_decoderPins.clear(); + m_decoders.clear(); + m_demux = Filter(); + m_realSource = Filter(); + m_mediaSource = Phonon::MediaSource(); + + absoluteSeek(0); //resets the clock + + return S_OK; + } + + + bool MediaGraph::disconnectNodes(BackendNode *source, BackendNode *sink) + { + const Filter sinkFilter = sink->filter(m_index); + const QList<InputPin> inputs = BackendNode::pins(sinkFilter, PINDIR_INPUT); + + QList<OutputPin> outputs; + if (source == m_mediaObject) { + outputs = BackendNode::pins(m_fakeSource, PINDIR_OUTPUT); + outputs += m_decoderPins; + } else { + outputs = BackendNode::pins(source->filter(m_index), PINDIR_OUTPUT); + } + + + for (int i = 0; i < inputs.count(); ++i) { + for (int o = 0; o < outputs.count(); ++o) { + tryDisconnect(outputs.at(o), inputs.at(i)); + } + } + + if (m_sinkConnections.removeOne(sink)) { + m_connectionsDirty = true; + } + return true; + } + + bool MediaGraph::tryDisconnect(const OutputPin &out, const InputPin &in) + { + bool ret = false; + + OutputPin output; + if (SUCCEEDED(in->ConnectedTo(output.pparam()))) { + + if (output == out) { + //we need a simple disconnection + ret = SUCCEEDED(out->Disconnect()) && SUCCEEDED(in->Disconnect()); + } else { + InputPin in2; + if (SUCCEEDED(out->ConnectedTo(in2.pparam()))) { + PIN_INFO info; + in2->QueryPinInfo(&info); + Filter tee(info.pFilter); + CLSID clsid; + tee->GetClassID(&clsid); + if (clsid == CLSID_InfTee) { + //we have to remove all intermediate filters between the tee and the sink + PIN_INFO info; + in->QueryPinInfo(&info); + Filter sink(info.pFilter); + QList<Filter> list = getFilterChain(tee, sink); + out->QueryPinInfo(&info); + Filter source(info.pFilter); + + if (list.isEmpty()) { + output->QueryPinInfo(&info); + if (Filter(info.pFilter) == tee) { + ret = SUCCEEDED(output->Disconnect()) && SUCCEEDED(in->Disconnect()); + } + } else { + ret = true; + for (int i = 0; i < list.count(); ++i) { + ret = ret && SUCCEEDED(removeFilter(list.at(i))); + } + } + + //Let's try to see if the Tee filter is still useful + if (ret) { + int connections = 0; + const QList<OutputPin> outputs = BackendNode::pins(tee, PINDIR_OUTPUT); + for(int i = 0; i < outputs.count(); ++i) { + InputPin p; + if ( SUCCEEDED(outputs.at(i)->ConnectedTo(p.pparam()))) { + connections++; + } + } + if (connections == 0) { + //this avoids a crash if the filter is destroyed + //by the subsequent call to removeFilter + output = OutputPin(); + removeFilter(tee); //there is no more output for the tee, we remove it + } + } + } + } + } + } + return ret; + } + + bool MediaGraph::tryConnect(const OutputPin &out, const InputPin &newIn) + { + + + ///The management of the creation of the Tees is done here (this is the only place where we call IPin::Connect + InputPin inPin; + if (SUCCEEDED(out->ConnectedTo(inPin.pparam()))) { + + //the fake source has another mechanism for the connection + if (BackendNode::pins(m_fakeSource, PINDIR_OUTPUT).contains(out)) { + return false; + } + + //the output pin is already connected + PIN_INFO info; + inPin->QueryPinInfo(&info); + Filter filter(info.pFilter); //this will ensure the interface is "Release"d + CLSID clsid; + filter->GetClassID(&clsid); + if (clsid == CLSID_InfTee) { + //there is already a Tee (namely 'filter') in use + const QList<OutputPin> outputs = BackendNode::pins(filter, PINDIR_OUTPUT); + for(int i = 0; i < outputs.count(); ++i) { + const OutputPin &pin = outputs.at(i); + if (VFW_E_NOT_CONNECTED == pin->ConnectedTo(inPin.pparam())) { + return SUCCEEDED(pin->Connect(newIn, 0)); + } + } + //we shoud never go here + return false; + } else { + QAMMediaType type; + out->ConnectionMediaType(&type); + + //first we disconnect the current connection (and we save the current media type) + if (!tryDisconnect(out, inPin)) { + return false; + } + + //..then we try to connect the new node + if (SUCCEEDED(out->Connect(newIn, 0))) { + + //we have to insert the Tee + if (!tryDisconnect(out, newIn)) { + return false; + } + + Filter filter(CLSID_InfTee, IID_IBaseFilter); + if (!filter) { + //rollback + m_graph->Connect(out, inPin); + return false; + } + + if (FAILED(m_graph->AddFilter(filter, 0))) { + return false; + } + + + InputPin teeIn = BackendNode::pins(filter, PINDIR_INPUT).first(); //a Tee has always one input + HRESULT hr = out->Connect(teeIn, &type); + if (FAILED(hr)) { + hr = m_graph->Connect(out, teeIn); + } + if (FAILED(hr)) { + m_graph->Connect(out, inPin); + return false; + } + + OutputPin teeOut = BackendNode::pins(filter, PINDIR_OUTPUT).last(); //the last is always the one that's not connected + + //we simply reconnect the pins as they + hr = m_graph->Connect(teeOut, inPin); + if (FAILED(hr)) { + m_graph->Connect(out, inPin); + return false; + } + + teeOut = BackendNode::pins(filter, PINDIR_OUTPUT).last(); //the last is always the one that's not connected + if (FAILED(m_graph->Connect(teeOut, newIn))) { + m_graph->Connect(out, inPin); + return false; + } + + return true; + } else { + //we simply reconnect the pins as they + m_graph->Connect(out, inPin); + return false; + } + } + + } else { + return SUCCEEDED(m_graph->Connect(out, newIn)); + } + } + + bool MediaGraph::connectNodes(BackendNode *source, BackendNode *sink) + { + bool ret = false; + const QList<InputPin> inputs = BackendNode::pins(sink->filter(m_index), PINDIR_INPUT); + QList<OutputPin> outputs = BackendNode::pins(source == m_mediaObject ? m_fakeSource : source->filter(m_index), PINDIR_OUTPUT); + + if (source == m_mediaObject) { + grabFilter(m_fakeSource); + } + +#ifdef GRAPH_DEBUG + qDebug() << Q_FUNC_INFO << source << sink << this; +#endif + + for (int o = 0; o < outputs.count(); o++) { + InputPin p; + for (int i = 0; i < inputs.count(); i++) { + const InputPin &inPin = inputs.at(i); + if (tryConnect(outputs.at(o), inPin)) { + //tell the sink node that it just got a new input + sink->connected(source, inPin); + ret = true; + if (source == m_mediaObject) { + m_connectionsDirty = true; + m_sinkConnections += sink; +#ifdef GRAPH_DEBUG + qDebug() << "found a sink connection" << sink << m_sinkConnections.count(); +#endif + } + break; + } + } + } + + return ret; + } + + + HRESULT MediaGraph::loadSource(const Phonon::MediaSource &source) + { + m_hasVideo = false; + m_hasAudio = false; + m_isSeekable = false; + + + //cleanup of the previous filters + m_result = cleanup(); + if (FAILED(m_result)) { + return m_result; + } + + m_mediaSource = source; + + switch (source.type()) + { + case Phonon::MediaSource::Disc: + if (source.discType() == Phonon::Dvd) { + m_result = E_NOTIMPL; + /*m_realSource = Filter(CLSID_DVDNavigator, IID_IBaseFilter); + if (m_realSource) { + return REGDB_E_CLASSNOTREG; + } + + m_result = m_graph->AddFilter(m_realSource, L"DVD Navigator");*/ + + + #ifndef QT_NO_PHONON_MEDIACONTROLLER + } else if (source.discType() == Phonon::Cd) { + m_realSource = Filter(new QAudioCDPlayer); + m_result = m_graph->AddFilter(m_realSource, 0); + +#endif //QT_NO_PHONON_MEDIACONTROLLER + } else { + m_result = E_NOTIMPL; + } + if (FAILED(m_result)) { + return m_result; + } + m_renderId = m_mediaObject->workerThread()->addFilterToRender(m_realSource); + return m_result; + case Phonon::MediaSource::Invalid: + return m_result; + case Phonon::MediaSource::Url: + case Phonon::MediaSource::LocalFile: + { + QString url; + if (source.type() == Phonon::MediaSource::LocalFile) { + url = source.fileName(); + } else { + url = source.url().toString(); + } + m_renderId = m_mediaObject->workerThread()->addUrlToRender(url); + } + break; +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + case Phonon::MediaSource::Stream: + { + m_realSource = Filter(new IODeviceReader(source, this)); + m_renderId = m_mediaObject->workerThread()->addFilterToRender(m_realSource); + } + break; +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + default: + m_result = E_FAIL; + } + + return m_result; + } + + void MediaGraph::finishSeeking(quint16 workId, qint64 time) + { + if (m_seekId == workId) { + m_currentTime = time; + m_mediaObject->seekingFinished(this); + m_seekId = 0; + } else { + //it's a queue seek command + //we're still seeking + } + } + + void MediaGraph::finishLoading(quint16 workId, HRESULT hr, Graph graph) + { + if (m_renderId == workId) { + m_renderId = 0; + + //let's determine if the graph is seekable + { + ComPointer<IMediaSeeking> mediaSeeking(graph, IID_IMediaSeeking); + DWORD caps = AM_SEEKING_CanSeekAbsolute; + m_isSeekable = mediaSeeking && SUCCEEDED(mediaSeeking->CheckCapabilities(&caps)); + } + + m_result = reallyFinishLoading(hr, graph); + m_mediaObject->loadingFinished(this); + } + } + + + HRESULT MediaGraph::reallyFinishLoading(HRESULT hr, const Graph &graph) + { + if (FAILED(hr)) { + return hr; + } + + const Graph oldGraph = m_graph; + m_graph = graph; + + //we keep the source and all the way down to the decoders + QList<Filter> removedFilters; + + const QList<Filter> allFilters = getAllFilters(graph); + for (int i = 0; i < allFilters.count(); ++i) { + const Filter &filter = allFilters.at(i); + if (isSourceFilter(filter)) { + m_realSource = filter; //save the source filter + if (!m_demux ) { + m_demux = filter; //in the WMV case, the demuxer is the source filter itself + } + } else if (isDemuxerFilter(filter)) { + m_demux = filter; + } else if (isDecoderFilter(filter)) { + m_decoders += filter; + m_decoderPins += BackendNode::pins(filter, PINDIR_OUTPUT).first(); + } else { + removedFilters += filter; + } + } + + for (int i = 0; i < m_decoders.count(); ++i) { + QList<Filter> chain = getFilterChain(m_demux, m_decoders.at(i)); + for (int i = 0; i < chain.count(); ++i) { + //we keep those filters + removedFilters.removeOne(chain.at(i)); + } + } + + for (int i = 0; i < removedFilters.count(); ++i) { + graph->RemoveFilter(removedFilters.at(i)); + } + + m_mediaObject->workerThread()->replaceGraphForEventManagement(graph, oldGraph); + + //let's transfer the nodes from the current graph to the new one + QList<GraphConnection> connections; //we store the connections that need to be restored + + // First get all the sink nodes (nodes with no input connected) + for (int i = 0; i < m_sinkConnections.count(); ++i) { + Filter currentFilter = m_sinkConnections.at(i)->filter(m_index); + connections += getConnections(currentFilter); + grabFilter(currentFilter); + } + + //we need to do something smart to detect if the streams are unencoded + if (m_demux) { + const QList<OutputPin> outputs = BackendNode::pins(m_demux, PINDIR_OUTPUT); + for (int i = 0; i < outputs.count(); ++i) { + const OutputPin &out = outputs.at(i); + InputPin pin; + if (out->ConnectedTo(pin.pparam()) == VFW_E_NOT_CONNECTED) { + m_decoderPins += out; //unconnected outputs can be decoded outputs + } + } + } + + ensureSourceConnectedTo(true); + + //let's reestablish the connections + for (int i = 0; i < connections.count(); ++i) { + const GraphConnection &connection = connections.at(i); + //check if we shoud transfer the sink node + + grabFilter(connection.input); + grabFilter(connection.output); + + const OutputPin output = BackendNode::pins(connection.output, PINDIR_OUTPUT).at(connection.outputOffset); + const InputPin input = BackendNode::pins(connection.input, PINDIR_INPUT).at(connection.inputOffset); + HRESULT hr = output->Connect(input, 0); + Q_UNUSED(hr); + Q_ASSERT( SUCCEEDED(hr)); + } + + //Finally, let's update the interfaces + m_mediaControl = ComPointer<IMediaControl>(graph, IID_IMediaControl); + m_mediaSeeking = ComPointer<IMediaSeeking>(graph, IID_IMediaSeeking); + return hr; + } + + //utility functions + //retrieves the filters between source and sink + QList<Filter> MediaGraph::getFilterChain(const Filter &source, const Filter &sink) + { + QList<Filter> ret; + Filter current = sink; + while (current && BackendNode::pins(current, PINDIR_INPUT).count() == 1 && current != source) { + if (current != source) + ret += current; + InputPin pin = BackendNode::pins(current, PINDIR_INPUT).first(); + current = Filter(); + OutputPin output; + if (pin->ConnectedTo(output.pparam()) == S_OK) { + PIN_INFO info; + if (SUCCEEDED(output->QueryPinInfo(&info)) && info.pFilter) { + current = Filter(info.pFilter); //this will take care of releasing the interface pFilter + } + } + } + if (current != source) { + //the soruce and sink don't seem to be connected + ret.clear(); + } + return ret; + } + + bool MediaGraph::isDecoderFilter(const Filter &filter) + { + if (filter == 0) { + return false; + } +#ifdef GRAPH_DEBUG + { + FILTER_INFO info; + filter->QueryFilterInfo(&info); + qDebug() << Q_FUNC_INFO << QString::fromUtf16(info.achName); + if (info.pGraph) { + info.pGraph->Release(); + } + } +#endif + + + QList<InputPin> inputs = BackendNode::pins(filter, PINDIR_INPUT); + QList<OutputPin> outputs = BackendNode::pins(filter, PINDIR_OUTPUT); + + //TODO: find a better way to detect if a node is a decoder + if (inputs.count() == 0 || outputs.count() ==0) { + return false; + } + + //the input pin must be encoded data + QAMMediaType type; + HRESULT hr = inputs.first()->ConnectionMediaType(&type); + if (FAILED(hr)) { + return false; + } + + + //...and the output must be decoded + QAMMediaType type2; + hr = outputs.first()->ConnectionMediaType(&type2); + if (FAILED(hr)) { + return false; + } + + if (type2.majortype != MEDIATYPE_Video && + type2.majortype != MEDIATYPE_Audio) { + return false; + } + + if (type2.majortype == MEDIATYPE_Video) { + m_hasVideo = true; + } else { + m_hasAudio = true; + } + +#ifdef GRAPH_DEBUG + { + FILTER_INFO info; + filter->QueryFilterInfo(&info); + qDebug() << "found a decoder filter" << QString::fromUtf16(info.achName); + if (info.pGraph) { + info.pGraph->Release(); + } + } +#endif + + return true; + } + + bool MediaGraph::isSourceFilter(const Filter &filter) const + { +#ifdef GRAPH_DEBUG + { + FILTER_INFO info; + filter->QueryFilterInfo(&info); + qDebug() << Q_FUNC_INFO << QString::fromUtf16(info.achName); + if (info.pGraph) { + info.pGraph->Release(); + } + } +#endif + //a source filter is one that has no input + return BackendNode::pins(filter, PINDIR_INPUT).isEmpty(); + } + + bool MediaGraph::isDemuxerFilter(const Filter &filter) const + { + QList<InputPin> inputs = BackendNode::pins(filter, PINDIR_INPUT); + QList<OutputPin> outputs = BackendNode::pins(filter, PINDIR_OUTPUT); + +#ifdef GRAPH_DEBUG + { + FILTER_INFO info; + filter->QueryFilterInfo(&info); + qDebug() << Q_FUNC_INFO << QString::fromUtf16(info.achName); + if (info.pGraph) { + info.pGraph->Release(); + } + } +#endif + + if (inputs.count() != 1 || outputs.count() == 0) { + return false; //a demuxer has only one input + } + + QAMMediaType type; + HRESULT hr = inputs.first()->ConnectionMediaType(&type); + if (FAILED(hr)) { + return false; + } + + if (type.majortype != MEDIATYPE_Stream) { + return false; + } + + for (int i = 0; i < outputs.count(); ++i) { + QAMMediaType type; + //for now we support only video and audio + hr = outputs.at(i)->ConnectionMediaType(&type); + if (SUCCEEDED(hr) && + type.majortype != MEDIATYPE_Video && type.majortype != MEDIATYPE_Audio) { + return false; + } + } +#ifdef GRAPH_DEBUG + { + FILTER_INFO info; + filter->QueryFilterInfo(&info); + qDebug() << "found a demuxer filter" << QString::fromUtf16(info.achName); + if (info.pGraph) { + info.pGraph->Release(); + } + } +#endif + return true; + } + + QMultiMap<QString, QString> MediaGraph::metadata() const + { + QMultiMap<QString, QString> ret; + ComPointer<IAMMediaContent> mediaContent(m_demux, IID_IAMMediaContent); + if (mediaContent) { + //let's get the meta data + BSTR str; + HRESULT hr = mediaContent->get_AuthorName(&str); + if (SUCCEEDED(hr)) { + ret.insert(QLatin1String("ARTIST"), QString::fromUtf16((const unsigned short*)str)); + SysFreeString(str); + } + hr = mediaContent->get_Title(&str); + if (SUCCEEDED(hr)) { + ret.insert(QLatin1String("TITLE"), QString::fromUtf16((const unsigned short*)str)); + SysFreeString(str); + } + hr = mediaContent->get_Description(&str); + if (SUCCEEDED(hr)) { + ret.insert(QLatin1String("DESCRIPTION"), QString::fromUtf16((const unsigned short*)str)); + SysFreeString(str); + } + hr = mediaContent->get_Copyright(&str); + if (SUCCEEDED(hr)) { + ret.insert(QLatin1String("COPYRIGHT"), QString::fromUtf16((const unsigned short*)str)); + SysFreeString(str); + } + hr = mediaContent->get_MoreInfoText(&str); + if (SUCCEEDED(hr)) { + ret.insert(QLatin1String("MOREINFO"), QString::fromUtf16((const unsigned short*)str)); + SysFreeString(str); + } + } + return ret; + } + + Filter MediaGraph::realSource() const + { + return m_realSource; + } + +#ifndef QT_NO_PHONON_MEDIACONTROLLER + void MediaGraph::setStopPosition(qint64 time) + { + qint64 current = 0, + stop = 0; + m_mediaSeeking->GetPositions(¤t, &stop); + + const bool shouldSeek = current == stop; + + if (time == -1) { + HRESULT hr = m_mediaSeeking->GetDuration(&time); + if (FAILED(hr)) { + return; + } + } else { + time *= 10000; + } + + if (time == stop) { + //the stop position is already at the right place + return; + } + + if (shouldSeek) { + m_mediaSeeking->SetPositions(¤t, AM_SEEKING_AbsolutePositioning, + &time, AM_SEEKING_AbsolutePositioning); + } else { + m_mediaSeeking->SetPositions(0, AM_SEEKING_NoPositioning, + &time, AM_SEEKING_AbsolutePositioning); + } + } + + qint64 MediaGraph::stopPosition() const + { + qint64 ret; + m_mediaSeeking->GetStopPosition(&ret); + return ret / 10000; + + } + + QList<qint64> MediaGraph::titles() const + { + //for now we only manage that for the audio cd + ComPointer<ITitleInterface> titleIFace(m_realSource, IID_ITitleInterface); + if (titleIFace) { + return titleIFace->titles(); + } else { + // the default value: only one title that starts at position 0 + return QList<qint64>() << 0; + } + } +#endif //QT_NO_PHONON_MEDIACONTROLLER + + + + } +} + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/ds9/mediagraph.h b/src/3rdparty/phonon/ds9/mediagraph.h new file mode 100644 index 0000000..13e7bcf --- /dev/null +++ b/src/3rdparty/phonon/ds9/mediagraph.h @@ -0,0 +1,148 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_MEDIAGRAPH_H +#define PHONON_MEDIAGRAPH_H + +#include "backendnode.h" +#include <QtCore/QMultiMap> + +#include <phonon/mediasource.h> + +//#define GRAPH_DEBUG + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace DS9 + { + class MediaObject; + + //in the end we should probably have no more inheritance here: everything should be in the interface of the class + //could be nice to then remove all the "*this" in the code of this class + class MediaGraph : public QObject + { + public: + MediaGraph(MediaObject *mo, short index); + ~MediaGraph(); + bool isSeekable() const; + qint64 absoluteTotalTime() const; + qint64 absoluteCurrentTime() const; + void play(); + void stop(); + void pause(); + void absoluteSeek(qint64); + + QMultiMap<QString, QString> metadata() const; + + static QList<Filter> getAllFilters(Graph graph); + QList<Filter> getAllFilters() const; + + HRESULT loadSource(const Phonon::MediaSource &); + + bool hasVideo() const { return m_hasVideo; } + void grabNode(BackendNode *node); + void grabFilter(Filter filter); + + //connections of the nodes + bool connectNodes(BackendNode *source, BackendNode *sink); + bool disconnectNodes(BackendNode *source, BackendNode *sink); + + Phonon::MediaSource mediaSource() const; + + //before loading a source, and after its playback this will be called + HRESULT cleanup(); + void ensureStopped(); + + short index() const; + + Filter realSource() const; + + +#ifndef QT_NO_PHONON_MEDIACONTROLLER + QList<qint64> titles() const; + void setStopPosition(qint64 time); + qint64 stopPosition() const; +#endif //QT_NO_PHONON_MEDIACONTROLLER + + void switchFilters(Filter oldFilter, Filter newFilter); + OAFilterState syncGetRealState() const; + + bool isLoading() const; + bool isStopping() const; + HRESULT renderResult() const; + + Graph graph() const; + + void finishLoading(quint16 workId, HRESULT hr, Graph); + void finishSeeking(quint16 workId, qint64 time); + + private: + bool isSourceFilter(const Filter &filter) const; + bool isDemuxerFilter(const Filter &filter) const; + bool isDecoderFilter(const Filter &filter); + static QList<Filter> getFilterChain(const Filter &source, const Filter &sink); + + HRESULT reallyFinishLoading(HRESULT, const Graph &graph); + + + //utility functions + void ensureSourceConnectedTo(bool force = false); + void ensureSourceDisconnected(); + bool tryConnect(const OutputPin &, const InputPin &); + bool tryDisconnect(const OutputPin &, const InputPin &); + HRESULT removeFilter(const Filter& filter); + + //after loading, removes the decoders that are not linked to a sink + HRESULT removeUselessDecoders(); + + //COM objects + Graph m_graph; + ComPointer<IMediaControl> m_mediaControl; + ComPointer<IMediaSeeking> m_mediaSeeking; + Filter m_fakeSource, m_realSource; + Filter m_demux; + QList<OutputPin> m_decoderPins; + QList<Filter> m_decoders; + + bool m_hasVideo; + bool m_hasAudio; + bool m_connectionsDirty; + bool m_isStopping; + mutable bool m_isSeekable; + HRESULT m_result; + quint16 m_index; + quint16 m_renderId; + quint16 m_seekId; + + //while seeking we need to store the current time + qint64 m_currentTime; + qint64 m_totalTime; + + MediaObject *m_mediaObject; + Phonon::MediaSource m_mediaSource; + QList<BackendNode*> m_sinkConnections; //connections to the source + + Q_DISABLE_COPY(MediaGraph); + }; + } +} + +QT_END_NAMESPACE + +#endif // PHONON_MEDIAGRAPH_H diff --git a/src/3rdparty/phonon/ds9/mediaobject.cpp b/src/3rdparty/phonon/ds9/mediaobject.cpp new file mode 100644 index 0000000..93a19b0 --- /dev/null +++ b/src/3rdparty/phonon/ds9/mediaobject.cpp @@ -0,0 +1,1208 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#include <QtCore/QVector> +#include <QtCore/QTimerEvent> +#include <QtCore/QTimer> +#include <QtCore/QTime> +#include <QtCore/QLibrary> + +#ifndef Q_CC_MSVC +#include <dshow.h> +#endif //Q_CC_MSVC +#include <objbase.h> +#include <initguid.h> +#include <qnetwork.h> +#include <evcode.h> + +#include "mediaobject.h" +#include "videowidget.h" +#include "audiooutput.h" + + +#include <QtCore/QDebug> + +#define TIMER_INTERVAL 16 //... ms for the timer that polls the current state (we use the multimedia timer +#define PRELOAD_TIME 2000 // 2 seconds to load a source + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace DS9 + { + typedef BOOL (WINAPI* LPAMGETERRORTEXT)(HRESULT, WCHAR *, DWORD); + + //first the definition of the WorkerThread class + WorkerThread::WorkerThread() + : QThread(), m_currentRenderId(0), m_finished(false), m_currentWorkId(1) + { + } + + WorkerThread::~WorkerThread() + { + } + + WorkerThread::Work WorkerThread::dequeueWork() + { + QMutexLocker locker(&m_mutex); + if (m_finished) { + return Work(); + } + Work ret = m_queue.dequeue(); + + //we ensure to have the wait condition in the right state + if (m_queue.isEmpty()) { + m_waitCondition.reset(); + } else { + m_waitCondition.set(); + } + + return ret; + } + + void WorkerThread::run() + { + while (m_finished == false) { + HANDLE handles[FILTER_COUNT +1]; + handles[0] = m_waitCondition; + int count = 1; + for(int i = 0; i < FILTER_COUNT; ++i) { + if (m_graphHandle[i].graph) { + handles[count++] = m_graphHandle[i].handle; + } + } + DWORD result = ::WaitForMultipleObjects(count, handles, FALSE, INFINITE); + if (result == WAIT_OBJECT_0) { + if (m_finished) { + //that's the end if the thread execution + return; + } + + handleTask(); + } else { + //this is the event management + const Graph &graph = m_graphHandle[result - WAIT_OBJECT_0 - 1].graph; + long eventCode; + LONG_PTR param1, param2; + + ComPointer<IMediaEvent> mediaEvent(graph, IID_IMediaEvent); + mediaEvent->GetEvent(&eventCode, ¶m1, ¶m2, 0); + emit eventReady(graph, eventCode, param1); + mediaEvent->FreeEventParams(eventCode, param1, param2); + } + } + } + + //wants to know as soon as the state is set + void WorkerThread::addStateChangeRequest(Graph graph, OAFilterState state, QList<Filter> decoders) + { + QMutexLocker locker(&m_mutex); + bool found = false; + //we try to see if there is already an attempt to change the state and we remove it + for(int i = 0; !found && i < m_queue.size(); ++i) { + const Work &w = m_queue.at(i); + if (w.graph == graph && w.task == ChangeState) { + found = true; + m_queue.removeAt(i); + } + } + + //now let's create the new task + Work w; + w.task = ChangeState; + w.id = m_currentWorkId++; + w.graph = graph; + w.state = state; + w.decoders = decoders; + m_queue.enqueue(w); + m_waitCondition.set(); + } + + quint16 WorkerThread::addSeekRequest(Graph graph, qint64 time) + { + QMutexLocker locker(&m_mutex); + bool found = false; + //we try to see if there is already an attempt to seek and we remove it + for(int i = 0; !found && i < m_queue.size(); ++i) { + const Work &w = m_queue.at(i); + if (w.graph == graph && w.task == Seek) { + found = true; + m_queue.removeAt(i); + } + } + + Work w; + w.task = Seek; + //we create a new graph + w.graph = graph; + w.id = m_currentWorkId++; + w.time = time; + m_queue.enqueue(w); + m_waitCondition.set(); + return w.id; + } + + quint16 WorkerThread::addUrlToRender(const QString &url) + { + QMutexLocker locker(&m_mutex); + Work w; + w.task = Render; + //we create a new graph + w.graph = Graph(CLSID_FilterGraph, IID_IGraphBuilder); + w.url = url; + w.url.detach(); + w.id = m_currentWorkId++; + m_queue.enqueue(w); + m_waitCondition.set(); + return w.id; + } + + quint16 WorkerThread::addFilterToRender(const Filter &filter) + { + QMutexLocker locker(&m_mutex); + Work w; + w.task = Render; + //we create a new graph + w.graph = Graph(CLSID_FilterGraph, IID_IGraphBuilder); + w.filter = filter; + w.id = m_currentWorkId++; + m_queue.enqueue(w); + m_waitCondition.set(); + return w.id; + } + + void WorkerThread::replaceGraphForEventManagement(Graph newGraph, Graph oldGraph) + { + QMutexLocker locker(&m_mutex); + Work w; + w.task = ReplaceGraph; + w.graph = newGraph; + w.oldGraph = oldGraph; + m_queue.enqueue(w); + m_waitCondition.set(); + } + + void WorkerThread::handleTask() + { + const Work w = dequeueWork(); + + if (m_finished) { + return; + } + + HRESULT hr = S_OK; + + m_currentRender = w.graph; + m_currentRenderId = w.id; + if (w.task == ReplaceGraph) { + QMutexLocker locker(&m_mutex); + HANDLE h; + + int index = -1; + for(int i = 0; i < FILTER_COUNT; ++i) { + if (m_graphHandle[i].graph == w.oldGraph) { + m_graphHandle[i].graph = Graph(); + index = i; + break; + } else if (index == -1 && m_graphHandle[i].graph == 0) { + //this is the first available slot + index = i; + } + } + + Q_ASSERT(index != -1); + + //add the new graph + if (SUCCEEDED(ComPointer<IMediaEvent>(w.graph, IID_IMediaEvent) + ->GetEventHandle(reinterpret_cast<OAEVENT*>(&h)))) { + m_graphHandle[index].graph = w.graph; + m_graphHandle[index].handle = h; + } + } else if (w.task == Render) { + if (w.filter) { + //let's render pins + w.graph->AddFilter(w.filter, 0); + const QList<OutputPin> outputs = BackendNode::pins(w.filter, PINDIR_OUTPUT); + for (int i = 0; i < outputs.count(); ++i) { + //blocking call + hr = w.graph->Render(outputs.at(i)); + if (FAILED(hr)) { + break; + } + } + } else if (!w.url.isEmpty()) { + //let's render a url (blocking call) + hr = w.graph->RenderFile(reinterpret_cast<const wchar_t *>(w.url.utf16()), 0); + } + if (hr != E_ABORT) { + emit asyncRenderFinished(w.id, hr, w.graph); + } + } else if (w.task == Seek) { + //that's a seekrequest + ComPointer<IMediaSeeking> mediaSeeking(w.graph, IID_IMediaSeeking); + qint64 newtime = w.time * 10000; + hr = mediaSeeking->SetPositions(&newtime, AM_SEEKING_AbsolutePositioning, + 0, AM_SEEKING_NoPositioning); + qint64 currentTime = -1; + if (SUCCEEDED(hr)) { + hr = mediaSeeking->GetCurrentPosition(¤tTime); + if (SUCCEEDED(hr)) { + currentTime /= 10000; //convert to ms + } + } + emit asyncSeekingFinished(w.id, currentTime); + hr = E_ABORT; //to avoid emitting asyncRenderFinished + } else if (w.task == ChangeState) { + + //remove useless decoders + QList<Filter> unused; + for (int i = 0; i < w.decoders.count(); ++i) { + const Filter &filter = w.decoders.at(i); + bool used = false; + const QList<OutputPin> pins = BackendNode::pins(filter, PINDIR_OUTPUT); + for( int i = 0; i < pins.count(); ++i) { + InputPin input; + if (pins.at(i)->ConnectedTo(input.pparam()) == S_OK) { + used = true; + } + } + if (!used) { + unused += filter; + } + } + + //we can get the state + for (int i = 0; i < unused.count(); ++i) { + //we should remove this filter from the graph + w.graph->RemoveFilter(unused.at(i)); + } + + + //we can get the state + ComPointer<IMediaControl> mc(w.graph, IID_IMediaControl); + + //we change the state here + switch(w.state) + { + case State_Stopped: + mc->Stop(); + break; + case State_Paused: + mc->Pause(); + break; + case State_Running: + mc->Run(); + break; + } + OAFilterState s; + //blocking call + HRESULT hr = mc->GetState(INFINITE, &s); + + if (SUCCEEDED(hr)) { + if (s == State_Stopped) { + emit stateReady(w.graph, Phonon::StoppedState); + } else if (s == State_Paused) { + emit stateReady(w.graph, Phonon::PausedState); + } else /*if (s == State_Running)*/ { + emit stateReady(w.graph, Phonon::PlayingState); + } + } + } + + m_currentRender = Graph(); + m_currentRenderId = 0; + + } + + void WorkerThread::abortCurrentRender(qint16 renderId) + { + QMutexLocker locker(&m_mutex); + bool found = false; + //we try to see if there is already an attempt to seek and we remove it + for(int i = 0; !found && i < m_queue.size(); ++i) { + const Work &w = m_queue.at(i); + if (w.id == renderId) { + found = true; + m_queue.removeAt(i); + } + } + + if (m_currentRender && m_currentRenderId == renderId) { + m_currentRender->Abort(); + } + } + + //tells the thread to stop processing + void WorkerThread::signalStop() + { + QMutexLocker locker(&m_mutex); + m_queue.clear(); + if (m_currentRender) { + //in case we're currently rendering something + m_currentRender->Abort(); + + } + + m_finished = true; + m_waitCondition.set(); + } + + + MediaObject::MediaObject(QObject *parent) : BackendNode(parent), + transactionState(Phonon::StoppedState), + m_errorType(Phonon::NoError), + m_state(Phonon::LoadingState), + m_nextState(Phonon::StoppedState), + m_prefinishMark(0), + m_tickInterval(0), + m_buffering(false), + m_oldHasVideo(false), + m_prefinishMarkSent(false), + m_aboutToFinishSent(false), + m_nextSourceReadyToStart(false), +#ifndef QT_NO_PHONON_MEDIACONTROLLER + m_autoplayTitles(true), + m_currentTitle(0), +#endif //QT_NO_PHONON_MEDIACONTROLLER + m_targetTick(INFINITE) + { + + for(int i = 0; i < FILTER_COUNT; ++i) { + m_graphs[i] = new MediaGraph(this, i); + } + + connect(&m_thread, SIGNAL(stateReady(Graph, Phonon::State)), + SLOT(slotStateReady(Graph, Phonon::State))); + + connect(&m_thread, SIGNAL(eventReady(Graph, long, long)), + SLOT(handleEvents(Graph, long, long))); + + connect(&m_thread, SIGNAL(asyncRenderFinished(quint16, HRESULT, Graph)), + SLOT(finishLoading(quint16, HRESULT, Graph))); + + connect(&m_thread, SIGNAL(asyncSeekingFinished(quint16, qint64)), + SLOT(finishSeeking(quint16, qint64))); + //really special case + m_mediaObject = this; + m_thread.start(); + } + + MediaObject::~MediaObject() + { + //be sure to finish the timer first + m_tickTimer.stop(); + + //we finish the worker thread here + m_thread.signalStop(); + m_thread.wait(); + + //and then we delete the graphs + for (int i = 0; i < FILTER_COUNT; ++i) { + delete m_graphs[i]; + } + } + + WorkerThread *MediaObject::workerThread() + { + return &m_thread; + } + + MediaGraph *MediaObject::currentGraph() const + { + return m_graphs[0]; + } + + MediaGraph *MediaObject::nextGraph() const + { + return m_graphs[FILTER_COUNT - 1]; + } + + //utility function to save the graph to a file + void MediaObject::timerEvent(QTimerEvent *e) + { + if (e->timerId() == m_tickTimer.timerId()) { + + const qint64 current = currentTime(); + const qint64 total = totalTime(); + + if ( m_tickInterval != 0 && current > m_targetTick) { + updateTargetTick(); + emit tick(current); + } + + //check that the title hasn't changed +#ifndef QT_NO_PHONON_MEDIACONTROLLER + if (m_autoplayTitles && m_currentTitle < _iface_availableTitles() - 1) { + + if (current >= total) { + //we go to the next title + _iface_setCurrentTitle(m_currentTitle + 1, false); + emit tick(current); + } + return; + } +#endif //QT_NO_PHONON_MEDIACONTROLLER + + if (total) { + const qint64 remaining = total - current; + + if (m_transitionTime < 0 && m_nextSourceReadyToStart) { + if (remaining < -m_transitionTime + TIMER_INTERVAL/2) { + //we need to switch graphs to run the next source in the queue (with cross-fading) + switchToNextSource(); + return; + } else if (current < -m_transitionTime) { + //we are currently crossfading + for (int i = 0; i < m_audioOutputs.count(); ++i) { + m_audioOutputs.at(i)->setCrossFadingProgress( currentGraph()->index(), qMin( qreal(1.), qreal(current) / qreal(-m_transitionTime))); + } + } + } + + if (m_prefinishMark > 0 && !m_prefinishMarkSent && remaining < m_prefinishMark + TIMER_INTERVAL/2) { +#ifdef GRAPH_DEBUG + qDebug() << "DS9: emit prefinishMarkReached" << remaining << QTime::currentTime().toString(); +#endif + m_prefinishMarkSent = true; + + emit prefinishMarkReached( remaining ); + } + + if (!m_aboutToFinishSent && remaining < PRELOAD_TIME - m_transitionTime + TIMER_INTERVAL/2) { + //let's take a 2 seconds time time to actually load the next file +#ifdef GRAPH_DEBUG + qDebug() << "DS9: emit aboutToFinish" << remaining << QTime::currentTime().toString(); +#endif + m_aboutToFinishSent = true; + emit aboutToFinish(); + } + } else { + //total is 0: the stream is probably live (endless) + } + + if (m_buffering) { + ComPointer<IAMNetworkStatus> status(currentGraph()->realSource(), IID_IAMNetworkStatus); + if (status) { + long l; + status->get_BufferingProgress(&l); + emit bufferStatus(l); +#ifdef GRAPH_DEBUG + qDebug() << "emit bufferStatus(" << l << ")"; +#endif + } + } + } + } + + void MediaObject::switchToNextSource() + { + m_prefinishMarkSent = false; + m_aboutToFinishSent = false; + m_nextSourceReadyToStart = false; + + m_oldHasVideo = currentGraph()->hasVideo(); + + qSwap(m_graphs[0], m_graphs[1]); //swap the graphs + + //we tell the video widgets to switch now to the new source +#ifndef QT_NO_PHONON_VIDEO + for (int i = 0; i < m_videoWidgets.count(); ++i) { + m_videoWidgets.at(i)->setCurrentGraph(currentGraph()->index()); + } +#endif //QT_NO_PHONON_VIDEO + + emit currentSourceChanged(currentGraph()->mediaSource()); + + if (currentGraph()->isLoading()) { + //will simply tell that when loading is finished + //it should start the playback + play(); + } + + + + emit metaDataChanged(currentGraph()->metadata()); + + if (nextGraph()->hasVideo() != currentGraph()->hasVideo()) { + emit hasVideoChanged(currentGraph()->hasVideo()); + } + + emit tick(0); + emit totalTimeChanged(totalTime()); + +#ifndef QT_NO_PHONON_MEDIACONTROLLER + setTitles(currentGraph()->titles()); +#endif //QT_NO_PHONON_MEDIACONTROLLER + + //this manages only gapless transitions + if (currentGraph()->mediaSource().type() != Phonon::MediaSource::Invalid) { + if (catchComError(currentGraph()->renderResult())) { + setState(Phonon::ErrorState); + } else { + play(); + } + } + } + + Phonon::State MediaObject::state() const + { + if (m_buffering) { + return Phonon::BufferingState; + } else { + return m_state; + } + } + + bool MediaObject::hasVideo() const + { + return currentGraph()->hasVideo(); + } + + bool MediaObject::isSeekable() const + { + return currentGraph()->isSeekable(); + } + + qint64 MediaObject::totalTime() const + { +#ifndef QT_NO_PHONON_MEDIACONTROLLER + //1st, check if there is more titles after + const qint64 ret = (m_currentTitle < _iface_availableTitles() - 1) ? + titleAbsolutePosition(m_currentTitle+1) : currentGraph()->absoluteTotalTime(); + + //this is the duration of the current title + return ret - titleAbsolutePosition(m_currentTitle); +#else + return currentGraph()->absoluteTotalTime(); +#endif //QT_NO_PHONON_MEDIACONTROLLER + } + + qint64 MediaObject::currentTime() const + { + //this handles inaccuracy when stopping on a title + return currentGraph()->absoluteCurrentTime() +#ifndef QT_NO_PHONON_MEDIACONTROLLER + - titleAbsolutePosition(m_currentTitle) +#endif //QT_NO_PHONON_MEDIACONTROLLER + ; + } + + qint32 MediaObject::tickInterval() const + { + return m_tickInterval; + } + + void MediaObject::setTickInterval(qint32 newTickInterval) + { + m_tickInterval = newTickInterval; + updateTargetTick(); + } + + void MediaObject::pause() + { + if (currentGraph()->isLoading()) { + m_nextState = Phonon::PausedState; + } else { + currentGraph()->pause(); + } + } + + void MediaObject::stop() + { + if (currentGraph()->isLoading()) { + m_nextState = Phonon::StoppedState; + } else { + currentGraph()->stop(); + } + } + + void MediaObject::ensureStopped() + { + currentGraph()->ensureStopped(); + if (m_state == Phonon::ErrorState) { + //we reset the state here + m_state = Phonon::StoppedState; + } + } + + void MediaObject::play() + { + if (currentGraph()->isLoading()) { + m_nextState = Phonon::PlayingState; + } else { + currentGraph()->play(); + } + } + + QString MediaObject::errorString() const + { + return m_errorString; + } + + Phonon::ErrorType MediaObject::errorType() const + { + return m_errorType; + } + + + void MediaObject::updateTargetTick() + { + if (m_tickInterval) { + const qint64 current = currentTime(); + m_targetTick = current / m_tickInterval * m_tickInterval; + if (current == 0 || m_targetTick < current) { + m_targetTick += m_tickInterval; + } + } + } + + void MediaObject::setState(Phonon::State newstate) + { + if (newstate == Phonon::PlayingState) { + updateTargetTick(); + } + + if (newstate == m_state) { + return; + } + + //manage the timer + if (newstate == Phonon::PlayingState) { + m_tickTimer.start(TIMER_INTERVAL, this); + } else { + m_tickTimer.stop(); + } + + Phonon::State oldstate = state(); + m_state = newstate; + emit stateChanged(newstate, oldstate); + } + + + qint32 MediaObject::prefinishMark() const + { + return m_prefinishMark; + } + + void MediaObject::setPrefinishMark(qint32 newPrefinishMark) + { + m_prefinishMark = newPrefinishMark; + } + + qint32 MediaObject::transitionTime() const + { + return m_transitionTime; + } + + void MediaObject::setTransitionTime(qint32 time) + { + m_transitionTime = time; + } + + qint64 MediaObject::remainingTime() const + { + return totalTime() - currentTime(); + } + + + Phonon::MediaSource MediaObject::source() const + { + return currentGraph()->mediaSource(); + } + + void MediaObject::setNextSource(const Phonon::MediaSource &source) + { + m_nextSourceReadyToStart = true; + const bool shouldSwitch = (m_state == Phonon::StoppedState || m_state == Phonon::ErrorState); + nextGraph()->loadSource(source); //let's preload the source + + if (shouldSwitch) { + switchToNextSource(); + } + } + + void MediaObject::setSource(const Phonon::MediaSource &source) + { + m_nextSourceReadyToStart = false; + m_prefinishMarkSent = false; + m_aboutToFinishSent = false; + + m_oldHasVideo = currentGraph()->hasVideo(); + setState(Phonon::LoadingState); + //After loading we go into stopped state + m_nextState = Phonon::StoppedState; + catchComError(currentGraph()->loadSource(source)); + emit currentSourceChanged(source); + } + + void MediaObject::slotStateReady(Graph graph, Phonon::State newState) + { + if (graph == currentGraph()->graph() && !currentGraph()->isLoading()) { + setState(newState); + } + } + + void MediaObject::loadingFinished(MediaGraph *mg) + { + if (mg == currentGraph()) { +#ifndef QT_NO_PHONON_MEDIACONTROLLER + //Title interface + m_currentTitle = 0; + setTitles(currentGraph()->titles()); +#endif //QT_NO_PHONON_MEDIACONTROLLER + + HRESULT hr = mg->renderResult(); + + if (catchComError(hr)) { + return; + } + + if (m_oldHasVideo != currentGraph()->hasVideo()) { + emit hasVideoChanged(currentGraph()->hasVideo()); + } + +#ifndef QT_NO_PHONON_VIDEO + if (currentGraph()->hasVideo()) { + updateVideoGeometry(); + } +#endif //QT_NO_PHONON_VIDEO + + emit metaDataChanged(currentGraph()->metadata()); + emit totalTimeChanged(totalTime()); + + //let's put the next state + switch(m_nextState) + { + case Phonon::PausedState: + pause(); + break; + case Phonon::StoppedState: + stop(); + break; + case Phonon::PlayingState: + play(); + break; + case Phonon::ErrorState: + setState(Phonon::ErrorState); + break; + } + } + } + + void MediaObject::seek(qint64 time) + { + //we seek into the current title + currentGraph()->absoluteSeek(time +#ifndef QT_NO_PHONON_MEDIACONTROLLER + + titleAbsolutePosition(m_currentTitle) +#endif //QT_NO_PHONON_MEDIACONTROLLER + ); + } + + void MediaObject::seekingFinished(MediaGraph *mg) + { + if (mg == currentGraph()) { + + updateTargetTick(); + if (currentTime() < totalTime() - m_prefinishMark) { + m_prefinishMarkSent = false; + } + + if (currentTime() < totalTime() - PRELOAD_TIME + m_transitionTime) { + m_aboutToFinishSent = false; + } + + //this helps the update of the application (seekslider for example) + if (m_state == PausedState || m_state == PlayingState) { + emit tick(currentTime()); + } + } + } + + + bool MediaObject::catchComError(HRESULT hr) + { + + m_errorString.clear(); + m_errorType = Phonon::NoError; + + if (hr != S_OK) { +#ifdef GRAPH_DEBUG + qWarning("an error occurred 0x%x",hr); +#endif + LPAMGETERRORTEXT getErrorText = (LPAMGETERRORTEXT)QLibrary::resolve(QLatin1String("quartz"), "AMGetErrorTextW"); + + ushort buffer[MAX_ERROR_TEXT_LEN]; + if (getErrorText && getErrorText(hr, (WCHAR*)buffer, MAX_ERROR_TEXT_LEN)) { + m_errorString = QString::fromUtf16(buffer); + } else { + m_errorString = QString::fromLatin1("Unknown error"); + } + const QString comError = QString::number(uint(hr), 16); + if (!m_errorString.toLower().contains(comError.toLower())) { + m_errorString += QString::fromLatin1(" (0x%1)").arg(comError); + } + if (FAILED(hr)) { + m_errorType = Phonon::FatalError; + setState(Phonon::ErrorState); + } else { + m_errorType = Phonon::NormalError; + m_nextState = Phonon::ErrorState; + } + } else { + m_errorType = Phonon::NoError; + + } + + return m_errorType == Phonon::FatalError; + } + + + void MediaObject::grabNode(BackendNode *node) + { + for (int i = 0; i < FILTER_COUNT; ++i) { + m_graphs[i]->grabNode(node); + } + node->setMediaObject(this); + } + + bool MediaObject::connectNodes(BackendNode *source, BackendNode *sink) + { + bool ret = true; + for (int i = 0; i < FILTER_COUNT; ++i) { + ret = ret && m_graphs[i]->connectNodes(source, sink); + } + if (ret) { +#ifndef QT_NO_PHONON_VIDEO + if (VideoWidget *video = qobject_cast<VideoWidget*>(sink)) { + m_videoWidgets += video; + } else +#endif //QT_NO_PHONON_VIDEO + if (AudioOutput *audio = qobject_cast<AudioOutput*>(sink)) { + m_audioOutputs += audio; + } + } + return ret; + } + + bool MediaObject::disconnectNodes(BackendNode *source, BackendNode *sink) + { + bool ret = true; + for (int i = 0; i < FILTER_COUNT; ++i) { + ret = ret && m_graphs[i]->disconnectNodes(source, sink); + } + if (ret) { +#ifndef QT_NO_PHONON_VIDEO + if (VideoWidget *video = qobject_cast<VideoWidget*>(sink)) { + m_videoWidgets.removeOne(video); + } else +#endif //QT_NO_PHONON_VIDEO + if (AudioOutput *audio = qobject_cast<AudioOutput*>(sink)) { + m_audioOutputs.removeOne(audio); + } + } + return ret; + } + +#ifndef QT_NO_PHONON_VIDEO + void MediaObject::updateVideoGeometry() + { + for (int i = 0; i < m_videoWidgets.count(); ++i) { + m_videoWidgets.at(i)->notifyVideoLoaded(); + } + } +#endif //QT_NO_PHONON_VIDEO + + void MediaObject::handleComplete(IGraphBuilder *graph) + { + if (graph == currentGraph()->graph()) { + if (m_transitionTime >= PRELOAD_TIME || m_aboutToFinishSent == false) { + emit aboutToFinish(); //give a chance to the frontend to give a next source + m_aboutToFinishSent = true; + } + + if (!m_nextSourceReadyToStart) { + //this is the last source, we simply finish + const qint64 current = currentTime(); + const OAFilterState currentState = currentGraph()->syncGetRealState(); + + emit tick(current); //this ensures that the end of the seek slider is reached + emit finished(); + + if (currentTime() == current && currentGraph()->syncGetRealState() == currentState) { + //no seek operation in-between + pause(); + setState(Phonon::PausedState); //we set it here + } + + } else if (m_transitionTime == 0) { + //gapless transition + switchToNextSource(); //let's call the function immediately + } else if (m_transitionTime > 0) { + //management of the transition (if it is >= 0) + QTimer::singleShot(m_transitionTime, this, SLOT(switchToNextSource())); + } + } else { + //it is just the end of the previous source (in case of cross-fading) + nextGraph()->cleanup(); + } + for (int i = 0; i < m_audioOutputs.count(); ++i) { + m_audioOutputs.at(i)->setCrossFadingProgress( currentGraph()->index(), 1.); //cross-fading is in any case finished + } + } + + void MediaObject::finishLoading(quint16 workId, HRESULT hr, Graph graph) + { + for(int i = 0; i < FILTER_COUNT; ++i) { + m_graphs[i]->finishLoading(workId, hr, graph); + } + } + + void MediaObject::finishSeeking(quint16 workId, qint64 time) + { + for(int i = 0; i < FILTER_COUNT; ++i) { + m_graphs[i]->finishSeeking(workId, time); + } + } + + + void MediaObject::handleEvents(Graph graph, long eventCode, long param1) + { + QString eventDescription; + switch (eventCode) + { + case EC_BUFFERING_DATA: + if (graph == currentGraph()->graph()) { + m_buffering = param1; + emit stateChanged(state(), m_state); + } + break; + case EC_LENGTH_CHANGED: + if (graph == currentGraph()->graph()) { + emit totalTimeChanged( totalTime() ); + } + break; + + case EC_COMPLETE: + handleComplete(graph); + break; + +#ifndef QT_NO_PHONON_VIDEO + case EC_VIDEO_SIZE_CHANGED: + if (graph == currentGraph()->graph()) { + updateVideoGeometry(); + } + break; +#endif //QT_NO_PHONON_VIDEO + +#ifdef GRAPH_DEBUG + case EC_ACTIVATE: qDebug() << "EC_ACTIVATE: A video window is being " << (param1 ? "ACTIVATED" : "DEACTIVATED"); break; + case EC_BUILT: qDebug() << "EC_BUILT: Send by the Video Control when a graph has been built. Not forwarded to applications."; break; + case EC_CLOCK_CHANGED: qDebug() << "EC_CLOCK_CHANGED"; break; + case EC_CLOCK_UNSET: qDebug() << "EC_CLOCK_UNSET: The clock provider was disconnected."; break; + case EC_CODECAPI_EVENT: qDebug() << "EC_CODECAPI_EVENT: Sent by an encoder to signal an encoding event."; break; + case EC_DEVICE_LOST: qDebug() << "EC_DEVICE_LOST: A Plug and Play device was removed or has become available again."; break; + case EC_DISPLAY_CHANGED: qDebug() << "EC_DISPLAY_CHANGED: The display mode has changed."; break; + case EC_END_OF_SEGMENT: qDebug() << "EC_END_OF_SEGMENT: The end of a segment has been reached."; break; + case EC_ERROR_STILLPLAYING: qDebug() << "EC_ERROR_STILLPLAYING: An asynchronous command to run the graph has failed."; break; + case EC_ERRORABORT: qDebug() << "EC_ERRORABORT: An operation was aborted because of an error."; break; + case EC_EXTDEVICE_MODE_CHANGE: qDebug() << "EC_EXTDEVICE_MODE_CHANGE: Not supported."; break; + case EC_FULLSCREEN_LOST: qDebug() << "EC_FULLSCREEN_LOST: The video renderer is switching out of full-screen mode."; break; + case EC_GRAPH_CHANGED: qDebug() << "EC_GRAPH_CHANGED: The filter graph has changed."; break; + case EC_NEED_RESTART: qDebug() << "EC_NEED_RESTART: A filter is requesting that the graph be restarted."; break; + case EC_NOTIFY_WINDOW: qDebug() << "EC_NOTIFY_WINDOW: Notifies a filter of the video renderer's window."; break; + case EC_OLE_EVENT: qDebug() << "EC_OLE_EVENT: A filter is passing a text string to the application."; break; + case EC_OPENING_FILE: qDebug() << "EC_OPENING_FILE: The graph is opening a file, or has finished opening a file."; break; + case EC_PALETTE_CHANGED: qDebug() << "EC_PALETTE_CHANGED: The video palette has changed."; break; + case EC_PAUSED: qDebug() << "EC_PAUSED: A pause request has completed."; break; + case EC_PREPROCESS_COMPLETE: qDebug() << "EC_PREPROCESS_COMPLETE: Sent by the WM ASF Writer filter when it completes the pre-processing for multipass encoding."; break; + case EC_QUALITY_CHANGE: qDebug() << "EC_QUALITY_CHANGE: The graph is dropping samples, for quality control."; break; + case EC_REPAINT: qDebug() << "EC_REPAINT: A video renderer requires a repaint."; break; + case EC_SEGMENT_STARTED: qDebug() << "EC_SEGMENT_STARTED: A new segment has started."; break; + case EC_SHUTTING_DOWN: qDebug() << "EC_SHUTTING_DOWN: The filter graph is shutting down, prior to being destroyed."; break; + case EC_SNDDEV_IN_ERROR: qDebug() << "EC_SNDDEV_IN_ERROR: A device error has occurred in an audio capture filter."; break; + case EC_SNDDEV_OUT_ERROR: qDebug() << "EC_SNDDEV_OUT_ERROR: A device error has occurred in an audio renderer filter."; break; + case EC_STARVATION: qDebug() << "EC_STARVATION: A filter is not receiving enough data."; break; + case EC_STATE_CHANGE: qDebug() << "EC_STATE_CHANGE: The filter graph has changed state."; break; + case EC_STEP_COMPLETE: qDebug() << "EC_STEP_COMPLETE: A filter performing frame stepping has stepped the specified number of frames."; break; + case EC_STREAM_CONTROL_STARTED: qDebug() << "EC_STREAM_CONTROL_STARTED: A stream-control start command has taken effect."; break; + case EC_STREAM_CONTROL_STOPPED: qDebug() << "EC_STREAM_CONTROL_STOPPED: A stream-control stop command has taken effect."; break; + case EC_STREAM_ERROR_STILLPLAYING: qDebug() << "EC_STREAM_ERROR_STILLPLAYING: An error has occurred in a stream. The stream is still playing."; break; + case EC_STREAM_ERROR_STOPPED: qDebug() << "EC_STREAM_ERROR_STOPPED: A stream has stopped because of an error."; break; + case EC_TIMECODE_AVAILABLE: qDebug() << "EC_TIMECODE_AVAILABLE: Not supported."; break; + case EC_UNBUILT: qDebug() << "Sent by the Video Control when a graph has been torn down. Not forwarded to applications."; break; + case EC_USERABORT: qDebug() << "EC_USERABORT: Send by the Video Control when a graph has been torn down. Not forwarded to applications."; break; + case EC_VMR_RECONNECTION_FAILED: qDebug() << "EC_VMR_RECONNECTION_FAILED: Sent by the VMR-7 and the VMR-9 when it was unable to accept a dynamic format change request from the upstream decoder."; break; + case EC_VMR_RENDERDEVICE_SET: qDebug() << "EC_VMR_RENDERDEVICE_SET: Sent when the VMR has selected its rendering mechanism."; break; + case EC_VMR_SURFACE_FLIPPED: qDebug() << "EC_VMR_SURFACE_FLIPPED: Sent when the VMR-7's allocator presenter has called the DirectDraw Flip method on the surface being presented."; break; + case EC_WINDOW_DESTROYED: qDebug() << "EC_WINDOW_DESTROYED: The video renderer was destroyed or removed from the graph"; break; + case EC_WMT_EVENT: qDebug() << "EC_WMT_EVENT: Sent by the Windows Media Format SDK when an application uses the ASF Reader filter to play ASF files protected by digital rights management (DRM)."; break; + case EC_WMT_INDEX_EVENT: qDebug() << "EC_WMT_INDEX_EVENT: Sent by the Windows Media Format SDK when an application uses the ASF Writer to index Windows Media Video files."; break; + + //documented by Microsoft but not supported in the Platform SDK + // case EC_BANDWIDTHCHANGE : qDebug() << "EC_BANDWIDTHCHANGE: not supported"; break; + // case EC_CONTENTPROPERTY_CHANGED: qDebug() << "EC_CONTENTPROPERTY_CHANGED: not supported."; break; + // case EC_EOS_SOON: qDebug() << "EC_EOS_SOON: not supported"; break; + // case EC_ERRORABORTEX: qDebug() << "EC_ERRORABORTEX: An operation was aborted because of an error."; break; + // case EC_FILE_CLOSED: qDebug() << "EC_FILE_CLOSED: The source file was closed because of an unexpected event."; break; + // case EC_LOADSTATUS: qDebug() << "EC_LOADSTATUS: Notifies the application of progress when opening a network file."; break; + // case EC_MARKER_HIT: qDebug() << "EC_MARKER_HIT: not supported."; break; + // case EC_NEW_PIN: qDebug() << "EC_NEW_PIN: not supported."; break; + // case EC_PLEASE_REOPEN: qDebug() << "EC_PLEASE_REOPEN: The source file has changed."; break; + // case EC_PROCESSING_LATENCY: qDebug() << "EC_PROCESSING_LATENCY: Indicates the amount of time that a component is taking to process each sample."; break; + // case EC_RENDER_FINISHED: qDebug() << "EC_RENDER_FINISHED: Not supported."; break; + // case EC_SAMPLE_LATENCY: qDebug() << "EC_SAMPLE_LATENCY: Specifies how far behind schedule a component is for processing samples."; break; + // case EC_SAMPLE_NEEDED: qDebug() << "EC_SAMPLE_NEEDED: Requests a new input sample from the Enhanced Video Renderer (EVR) filter."; break; + // case EC_SCRUB_TIME: qDebug() << "EC_SCRUB_TIME: Specifies the time stamp for the most recent frame step."; break; + // case EC_STATUS: qDebug() << "EC_STATUS: Contains two arbitrary status strings."; break; + // case EC_VIDEOFRAMEREADY: qDebug() << "EC_VIDEOFRAMEREADY: A video frame is ready for display."; break; + + default: + qDebug() << "Unknown event" << eventCode << "(" << param1 << ")"; + break; +#else + default: + break; +#endif + } + } + + +#ifndef QT_NO_PHONON_MEDIACONTROLLER + //interface management + bool MediaObject::hasInterface(Interface iface) const + { + return iface == AddonInterface::TitleInterface; + } + + QVariant MediaObject::interfaceCall(Interface iface, int command, const QList<QVariant> ¶ms) + { + if (hasInterface(iface)) { + + switch (iface) + { + case TitleInterface: + switch (command) + { + case availableTitles: + return _iface_availableTitles(); + case title: + return _iface_currentTitle(); + case setTitle: + _iface_setCurrentTitle(params.first().toInt()); + break; + case autoplayTitles: + return m_autoplayTitles; + case setAutoplayTitles: + m_autoplayTitles = params.first().toBool(); + updateStopPosition(); + break; + } + break; + default: + break; + } + } + return QVariant(); + } + + + //TitleInterface + + //this is called to set the time for the different titles + qint64 MediaObject::titleAbsolutePosition(int title) const + { + if (title >= 0 && title < m_titles.count()) { + return m_titles.at(title); + } else { + return 0; + } + } + + void MediaObject::setTitles(const QList<qint64> &titles) + { + //this is called when the source is loaded + const bool emitSignal = m_titles.count() != titles.count(); + m_titles = titles; + if (emitSignal) { + emit availableTitlesChanged(titles.count()); + } + updateStopPosition(); + } + + + int MediaObject::_iface_availableTitles() const + { + return m_titles.count() - 1; + } + + int MediaObject::_iface_currentTitle() const + { + return m_currentTitle; + } + + void MediaObject::_iface_setCurrentTitle(int title, bool bseek) + { +#ifdef GRAPH_DEBUG + qDebug() << "_iface_setCurrentTitle" << title; +#endif + const int oldTitle = m_currentTitle; + m_currentTitle = title; + updateStopPosition(); + if (bseek) { + //let's seek to the beginning of the song + seek(0); + } else { + updateTargetTick(); + } + if (oldTitle != title) { + emit titleChanged(title); + emit totalTimeChanged(totalTime()); + } + + } + + void MediaObject::updateStopPosition() + { + if (!m_autoplayTitles && m_currentTitle < _iface_availableTitles() - 1) { + //stop position is set to the end of the track + currentGraph()->setStopPosition(titleAbsolutePosition(m_currentTitle+1)); + } else { + //stop position is set to the end + currentGraph()->setStopPosition(-1); + } + } +#endif //QT_NO_PHONON_QT_NO_PHONON_MEDIACONTROLLER + + void MediaObject::switchFilters(int index, Filter oldFilter, Filter newFilter) + { + if (currentGraph()->index() == index) { + currentGraph()->switchFilters(oldFilter, newFilter); + } else { + nextGraph()->switchFilters(oldFilter, newFilter); + } + + } + + + } +} + +QT_END_NAMESPACE + +#include "moc_mediaobject.cpp" diff --git a/src/3rdparty/phonon/ds9/mediaobject.h b/src/3rdparty/phonon/ds9/mediaobject.h new file mode 100644 index 0000000..2c34ffc --- /dev/null +++ b/src/3rdparty/phonon/ds9/mediaobject.h @@ -0,0 +1,313 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_MEDIAOBJECT_H +#define PHONON_MEDIAOBJECT_H + +#include <phonon/mediaobjectinterface.h> +#include <phonon/addoninterface.h> + +#include <QtCore/QHash> +#include <QtCore/QObject> +#include <QtCore/QQueue> +#include <QtCore/QBasicTimer> +#include <QtCore/QMutex> +#include <QtCore/QThread> + +#include "backendnode.h" +#include "mediagraph.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + class MediaSource; + + namespace DS9 + { + class VideoWidget; + class AudioOutput; + + class QWinWaitCondition + { + public: + QWinWaitCondition() : m_handle(::CreateEvent(0,0,0,0)) + { + } + + ~QWinWaitCondition() + { + ::CloseHandle(m_handle); + } + + void reset() + { + //will block + ::ResetEvent(m_handle); + } + + void set() + { + //will unblock + ::SetEvent(m_handle); + } + + operator HANDLE() + { + return m_handle; + } + + operator HEVENT() + { + return reinterpret_cast<HEVENT>(m_handle); + } + + + private: + HANDLE m_handle; + }; + + class WorkerThread : public QThread + { + Q_OBJECT + public: + WorkerThread(); + ~WorkerThread(); + + virtual void run(); + + //wants to know as soon as the state is set + void addStateChangeRequest(Graph graph, OAFilterState, QList<Filter> = QList<Filter>()); + + quint16 addSeekRequest(Graph graph, qint64 time); + quint16 addUrlToRender(const QString &url); + quint16 addFilterToRender(const Filter &filter); + + void replaceGraphForEventManagement(Graph newGraph, Graph oldGraph); + + void abortCurrentRender(qint16 renderId); + + //tells the thread to stop processing + void signalStop(); + + Q_SIGNALS: + void asyncRenderFinished(quint16, HRESULT, Graph); + void asyncSeekingFinished(quint16, qint64); + void stateReady(Graph, Phonon::State); + void eventReady(Graph, long eventCode, long param1); + + private: + + enum Task + { + Render, + Seek, + ChangeState, + ReplaceGraph //just updates recalls WaitForMultipleObject + }; + + struct Work + { + Task task; + quint16 id; + Graph graph; + Graph oldGraph; + Filter filter; + QString url; + union + { + qint64 time; + OAFilterState state; + }; + QList<Filter> decoders; //for the state change requests + }; + Work dequeueWork(); + void handleTask(); + + Graph m_currentRender; + qint16 m_currentRenderId; + QQueue<Work> m_queue; + bool m_finished; + quint16 m_currentWorkId; + QWinWaitCondition m_waitCondition; + QMutex m_mutex; + + //this is for WaitForMultipleObjects + struct + { + Graph graph; + HANDLE handle; + } m_graphHandle[FILTER_COUNT]; + }; + + + class MediaObject : public BackendNode, public Phonon::MediaObjectInterface +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + , public Phonon::AddonInterface +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + { + friend class Stream; + Q_OBJECT + Q_INTERFACES(Phonon::MediaObjectInterface +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + Phonon::AddonInterface +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + ) + public: + MediaObject(QObject *parent); + ~MediaObject(); + Phonon::State state() const; + bool hasVideo() const; + bool isSeekable() const; + qint64 currentTime() const; + qint32 tickInterval() const; + + void setTickInterval(qint32 newTickInterval); + void play(); + void pause(); + void stop(); + void ensureStopped(); + void seek(qint64 time); + + QString errorString() const; + Phonon::ErrorType errorType() const; + +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + bool hasInterface(Interface) const; + QVariant interfaceCall(Interface iface, int command, const QList<QVariant> ¶ms); +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + + qint64 totalTime() const; + qint32 prefinishMark() const; + void setPrefinishMark(qint32 newPrefinishMark); + + qint32 transitionTime() const; + void setTransitionTime(qint32); + + qint64 remainingTime() const; + + MediaSource source() const; + void setSource(const MediaSource &source); + void setNextSource(const MediaSource &source); + + + //COM error management + bool catchComError(HRESULT hr); + + void grabNode(BackendNode *node); + bool connectNodes(BackendNode *source, BackendNode *sink); + bool disconnectNodes(BackendNode *source, BackendNode *sink); + + void switchFilters(int index, Filter oldFilter, Filter newFilter); + + WorkerThread *workerThread(); + void loadingFinished(MediaGraph *mg); + void seekingFinished(MediaGraph *mg); + MediaGraph *currentGraph() const; + + //this is used by the backend only + Phonon::State transactionState; + + private Q_SLOTS: + void switchToNextSource(); + void slotStateReady(Graph, Phonon::State); + void handleEvents(Graph, long eventCode, long param1); + void finishLoading(quint16 workId, HRESULT hr, Graph); + void finishSeeking(quint16 workId, qint64 time); + + Q_SIGNALS: + void stateChanged(Phonon::State newstate, Phonon::State oldstate); + void tick(qint64 time); + void metaDataChanged(QMultiMap<QString, QString>); + void seekableChanged(bool); + void hasVideoChanged(bool); + void bufferStatus(int); + + // AddonInterface: + void titleChanged(int); + void availableTitlesChanged(int); + void chapterChanged(int); + void availableChaptersChanged(int); + void angleChanged(int); + void availableAnglesChanged(int); + + void finished(); + void prefinishMarkReached(qint32); + void aboutToFinish(); + void totalTimeChanged(qint64 length) const; + void currentSourceChanged(const MediaSource &); + + protected: + void setState(Phonon::State); + void timerEvent(QTimerEvent *e); + + private: +#ifndef QT_NO_PHONON_VIDEO + void updateVideoGeometry(); +#endif // QT_NO_PHONON_VIDEO + void handleComplete(IGraphBuilder *graph); + MediaGraph *nextGraph() const; + + void updateTargetTick(); + void updateStopPosition(); + + mutable QString m_errorString; + mutable Phonon::ErrorType m_errorType; + + Phonon::State m_state; + Phonon::State m_nextState; + qint32 m_transitionTime; + + qint32 m_prefinishMark; + + QBasicTimer m_tickTimer; + qint32 m_tickInterval; + + //the graph(s) + MediaGraph* m_graphs[FILTER_COUNT]; + + //...the videowidgets in the graph + QList<VideoWidget*> m_videoWidgets; + QList<AudioOutput*> m_audioOutputs; + + bool m_buffering:1; + bool m_oldHasVideo:1; + bool m_prefinishMarkSent:1; + bool m_aboutToFinishSent:1; + bool m_nextSourceReadyToStart:1; + + //for TitleInterface (and commands) +#ifndef QT_NO_PHONON_MEDIACONTROLLER + bool m_autoplayTitles:1; + QList<qint64> m_titles; + int m_currentTitle; + int _iface_availableTitles() const; + int _iface_currentTitle() const; + void _iface_setCurrentTitle(int title, bool bseek = true); + void setTitles(const QList<qint64> &titles); + qint64 titleAbsolutePosition(int title) const; +#endif //QT_NO_PHONON_MEDIACONTROLLER + qint64 m_targetTick; + + WorkerThread m_thread; + }; + } +} + +QT_END_NAMESPACE + +#endif // PHONON_MEDIAOBJECT_H diff --git a/src/3rdparty/phonon/ds9/phononds9_namespace.h b/src/3rdparty/phonon/ds9/phononds9_namespace.h new file mode 100644 index 0000000..e972d41 --- /dev/null +++ b/src/3rdparty/phonon/ds9/phononds9_namespace.h @@ -0,0 +1,33 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_DS9_NAMESPACE_H +#define PHONON_DS9_NAMESPACE_H + +#include <QtCore/qnamespace.h> + +#define FILTER_COUNT 2 //number of ds9 filters per object + +#ifndef QT_BEGIN_NAMESPACE +#define QT_BEGIN_NAMESPACE +#endif + +#ifndef QT_END_NAMESPACE +#define QT_END_NAMESPACE +#endif + +#endif diff --git a/src/3rdparty/phonon/ds9/qasyncreader.cpp b/src/3rdparty/phonon/ds9/qasyncreader.cpp new file mode 100644 index 0000000..68ec1f8 --- /dev/null +++ b/src/3rdparty/phonon/ds9/qasyncreader.cpp @@ -0,0 +1,198 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#include <QtCore/QFile> + +#include "qasyncreader.h" +#include "qbasefilter.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace DS9 + { + QAsyncReader::QAsyncReader(QBaseFilter *parent, const QVector<AM_MEDIA_TYPE> &mediaTypes) : QPin(parent, PINDIR_OUTPUT, mediaTypes) + { + } + + QAsyncReader::~QAsyncReader() + { + } + + STDMETHODIMP QAsyncReader::QueryInterface(REFIID iid, void **out) + { + if (!out) { + return E_POINTER; + } + + if (iid == IID_IAsyncReader) { + AddRef(); + *out = static_cast<IAsyncReader*>(this); + return S_OK; + } + + return QPin::QueryInterface(iid, out); + } + + STDMETHODIMP_(ULONG) QAsyncReader::AddRef() + { + return QPin::AddRef(); + } + + STDMETHODIMP_(ULONG) QAsyncReader::Release() + { + return QPin::Release(); + } + + + STDMETHODIMP QAsyncReader::RequestAllocator(IMemAllocator *preferred, ALLOCATOR_PROPERTIES *prop,IMemAllocator **actual) + { + ALLOCATOR_PROPERTIES prop2; + + if (prop->cbAlign == 0) { + prop->cbAlign = 1; //align on 1 char + } + + if (preferred && preferred->SetProperties(prop, &prop2) == S_OK) { + preferred->AddRef(); + *actual = preferred; + return S_OK; + } + + //we should try to create one memory allocator ourselves here + return E_FAIL; + } + + STDMETHODIMP QAsyncReader::Request(IMediaSample *sample,DWORD_PTR user) + { + QMutexLocker mutexLocker(&m_mutexWait); + QWriteLocker locker(&m_lock); + if (m_flushing) { + return VFW_E_WRONG_STATE; + } + + m_requestQueue.enqueue(AsyncRequest(sample, user)); + m_requestWait.wakeOne(); + return S_OK; + } + + STDMETHODIMP QAsyncReader::WaitForNext(DWORD timeout, IMediaSample **sample, DWORD_PTR *user) + { + QMutexLocker locker(&m_mutexWait); + if (!sample ||!user) { + return E_POINTER; + } + + *sample = 0; + *user = 0; + + AsyncRequest r = getNextRequest(); + + if (r.sample == 0) { + //there is no request in the queue + if (isFlushing()) { + return VFW_E_WRONG_STATE; + } else { + //First we need to lock the mutex + if (m_requestWait.wait(&m_mutexWait, timeout) == false) { + return VFW_E_TIMEOUT; + } + if (isFlushing()) { + return VFW_E_WRONG_STATE; + } + + r = getNextRequest(); + } + } + + //at this point we're sure to have a request to proceed + if (r.sample == 0) { + return E_FAIL; + } + + *sample = r.sample; + *user = r.user; + + return SyncReadAligned(r.sample); + } + + STDMETHODIMP QAsyncReader::BeginFlush() + { + QMutexLocker mutexLocker(&m_mutexWait); + QWriteLocker locker(&m_lock); + m_flushing = true; + m_requestWait.wakeOne(); + return S_OK; + } + + STDMETHODIMP QAsyncReader::EndFlush() + { + QWriteLocker locker(&m_lock); + m_flushing = false; + return S_OK; + } + + STDMETHODIMP QAsyncReader::SyncReadAligned(IMediaSample *sample) + { + if (!sample) { + return E_POINTER; + } + + REFERENCE_TIME start = 0, + stop = 0; + HRESULT hr = sample->GetTime(&start, &stop); + if(FAILED(hr)) { + return hr; + } + + LONGLONG startPos = start / 10000000; + LONG length = static_cast<LONG>((stop - start) / 10000000); + + BYTE *buffer; + hr = sample->GetPointer(&buffer); + if(FAILED(hr)) { + return hr; + } + + LONG actual = 0; + read(startPos, length, buffer, &actual); + + return sample->SetActualDataLength(actual); + } + + STDMETHODIMP QAsyncReader::SyncRead(LONGLONG pos, LONG length, BYTE *buffer) + { + return read(pos, length, buffer, 0); + } + + + //addition + QAsyncReader::AsyncRequest QAsyncReader::getNextRequest() + { + QWriteLocker locker(&m_lock); + AsyncRequest ret; + if (!m_requestQueue.isEmpty()) { + ret = m_requestQueue.dequeue(); + } + + return ret; + } + } +} + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/ds9/qasyncreader.h b/src/3rdparty/phonon/ds9/qasyncreader.h new file mode 100644 index 0000000..cb789ee --- /dev/null +++ b/src/3rdparty/phonon/ds9/qasyncreader.h @@ -0,0 +1,77 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_QASYNCREADER_H +#define PHONON_QASYNCREADER_H + +#include <QtCore/QWaitCondition> +#include <QtCore/QQueue> +#include <QtCore/QMutex> + +#include "qpin.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace DS9 + { + //his class reads asynchronously from a QIODevice + class QAsyncReader : public QPin, public IAsyncReader + { + public: + QAsyncReader(QBaseFilter *, const QVector<AM_MEDIA_TYPE> &mediaTypes); + ~QAsyncReader(); + + //reimplementation from IUnknown + STDMETHODIMP QueryInterface(REFIID iid, void** out); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + //reimplementation from IAsyncReader + STDMETHODIMP RequestAllocator(IMemAllocator *,ALLOCATOR_PROPERTIES *,IMemAllocator **); + STDMETHODIMP Request(IMediaSample *,DWORD_PTR); + STDMETHODIMP WaitForNext(DWORD,IMediaSample **,DWORD_PTR *); + STDMETHODIMP SyncReadAligned(IMediaSample *); + STDMETHODIMP SyncRead(LONGLONG,LONG,BYTE *); + virtual STDMETHODIMP Length(LONGLONG *,LONGLONG *) = 0; + STDMETHODIMP BeginFlush(); + STDMETHODIMP EndFlush(); + + protected: + virtual HRESULT read(LONGLONG pos, LONG length, BYTE *buffer, LONG *actual) = 0; + + private: + struct AsyncRequest + { + AsyncRequest(IMediaSample *s = 0, DWORD_PTR u = 0) : sample(s), user(u) {} + IMediaSample *sample; + DWORD_PTR user; + }; + AsyncRequest getNextRequest(); + + QMutex m_mutexWait; + + QQueue<AsyncRequest> m_requestQueue; + QWaitCondition m_requestWait; + }; + } +} + +QT_END_NAMESPACE + +#endif diff --git a/src/3rdparty/phonon/ds9/qaudiocdreader.cpp b/src/3rdparty/phonon/ds9/qaudiocdreader.cpp new file mode 100644 index 0000000..b9f9fd6 --- /dev/null +++ b/src/3rdparty/phonon/ds9/qaudiocdreader.cpp @@ -0,0 +1,332 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#include "qaudiocdreader.h" +#include <dshow.h> +#include <initguid.h> + +#include <winioctl.h> // needed for FILE_DEVICE_CD_ROM etc + +#define IOCTL_CDROM_READ_TOC CTL_CODE(FILE_DEVICE_CD_ROM, 0x0000, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_CDROM_RAW_READ CTL_CODE(FILE_DEVICE_CD_ROM, 0x000F, METHOD_OUT_DIRECT, FILE_READ_ACCESS) + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_MEDIACONTROLLER + +namespace Phonon +{ + namespace DS9 + { + // {CA46BFE1-D55B-4adf-B803-BC2B9AD57824} + DEFINE_GUID(IID_ITitleInterface, + 0xca46bfe1, 0xd55b, 0x4adf, 0xb8, 0x3, 0xbc, 0x2b, 0x9a, 0xd5, 0x78, 0x24); + + struct TRACK_DATA { + UCHAR Reserved; + UCHAR Control : 4; + UCHAR Adr : 4; + UCHAR TrackNumber; + UCHAR Reserved1; + UCHAR Address[4]; + }; + + struct CDROM_TOC { + UCHAR Length[2]; + UCHAR FirstTrack; + UCHAR LastTrack; + TRACK_DATA TrackData[100]; + }; + + struct WaveStructure + { + WaveStructure(); + + char riff[4]; + qint32 chunksize; + char wave[4]; + char fmt[4]; + const qint32 chunksize2; + const quint16 formatTag; + const quint16 nChannels; + const quint32 nSamplesPerSec; + const quint32 nAvgBytesPerSec; + const quint16 nBlockAlign; + const quint16 bitsPerSample; + char data[4]; + qint32 dataLength; + }; + + enum TRACK_MODE_TYPE { + YellowMode2, + XAForm2, + CDDA + }; + + + struct RAW_READ_INFO { + LARGE_INTEGER DiskOffset; + ULONG SectorCount; + TRACK_MODE_TYPE TrackMode; + }; + + class QAudioCDReader : public QAsyncReader, public ITitleInterface + { + public: + QAudioCDReader(QBaseFilter *parent, QChar drive = QChar()); + ~QAudioCDReader(); + + //reimplementation from IUnknown + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + STDMETHODIMP Length(LONGLONG *,LONGLONG *); + STDMETHODIMP QueryInterface(REFIID iid, void** out); + QList<qint64> titles() const; + + protected: + HRESULT read(LONGLONG pos, LONG length, BYTE *buffer, LONG *actual); + + private: + HANDLE m_cddrive; + CDROM_TOC *m_toc; + WaveStructure *m_waveHeader; + qint64 m_trackAddress; + }; + + +#define SECTOR_SIZE 2352 +#define NB_SECTORS_READ 20 + + static AM_MEDIA_TYPE getAudioCDMediaType() + { + AM_MEDIA_TYPE mt; + qMemSet(&mt, 0, sizeof(AM_MEDIA_TYPE)); + mt.majortype = MEDIATYPE_Stream; + mt.subtype = MEDIASUBTYPE_WAVE; + mt.bFixedSizeSamples = TRUE; + mt.bTemporalCompression = FALSE; + mt.lSampleSize = 1; + mt.formattype = GUID_NULL; + return mt; + } + + int addressToSectors(UCHAR address[4]) + { + return ((address[0] * 60 + address[1]) * 60 + address[2]) * 75 + address[3] - 150; + } + + WaveStructure::WaveStructure() : chunksize(0), chunksize2(16), + formatTag(WAVE_FORMAT_PCM), nChannels(2), nSamplesPerSec(44100), nAvgBytesPerSec(176400), nBlockAlign(4), bitsPerSample(16), + dataLength(0) + { + qMemCopy(riff, "RIFF", 4); + qMemCopy(wave, "WAVE", 4); + qMemCopy(fmt, "fmt ", 4); + qMemCopy(data, "data", 4); + } + + + QAudioCDReader::QAudioCDReader(QBaseFilter *parent, QChar drive) : QAsyncReader(parent, QVector<AM_MEDIA_TYPE>() << getAudioCDMediaType()) + { + m_toc = new CDROM_TOC; + m_waveHeader = new WaveStructure; + + //now open the cd-drive + QString path; + if (drive.isNull()) { + path = QString::fromLatin1("\\\\.\\Cdrom0"); + } else { + path = QString::fromLatin1("\\\\.\\%1:").arg(drive); + } + + m_cddrive = QT_WA_INLINE ( + ::CreateFile( (TCHAR*)path.utf16(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ), + ::CreateFileA( path.toLocal8Bit().constData(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ) + ); + + qMemSet(m_toc, 0, sizeof(CDROM_TOC)); + //read the TOC + DWORD bytesRead = 0; + bool tocRead = ::DeviceIoControl(m_cddrive, IOCTL_CDROM_READ_TOC, 0, 0, m_toc, sizeof(CDROM_TOC), &bytesRead, 0); + + if (!tocRead) { + qWarning("unable to load the TOC from the CD"); + return; + } + + m_trackAddress = addressToSectors(m_toc->TrackData[0].Address); + const qint32 nbSectorsToRead = (addressToSectors(m_toc->TrackData[m_toc->LastTrack + 1 - m_toc->FirstTrack].Address) + - m_trackAddress); + const qint32 dataLength = nbSectorsToRead * SECTOR_SIZE; + + m_waveHeader->chunksize = 4 + (8 + m_waveHeader->chunksize2) + (8 + dataLength); + m_waveHeader->dataLength = dataLength; + } + + QAudioCDReader::~QAudioCDReader() + { + ::CloseHandle(m_cddrive); + delete m_toc; + delete m_waveHeader; + + } + + STDMETHODIMP_(ULONG) QAudioCDReader::AddRef() + { + return QAsyncReader::AddRef(); + } + + STDMETHODIMP_(ULONG) QAudioCDReader::Release() + { + return QAsyncReader::Release(); + } + + + STDMETHODIMP QAudioCDReader::Length(LONGLONG *total,LONGLONG *available) + { + const LONGLONG length = sizeof(WaveStructure) + m_waveHeader->dataLength; + if (total) { + *total = length; + } + if (available) { + *available = length; + } + + return S_OK; + } + + STDMETHODIMP QAudioCDReader::QueryInterface(REFIID iid, void** out) + { + if (!out) { + return E_POINTER; + } + + if (iid == IID_ITitleInterface) { + //we reroute that to the pin + *out = static_cast<ITitleInterface*>(this); + AddRef(); + return S_OK; + } else { + return QAsyncReader::QueryInterface(iid, out); + } + } + + + HRESULT QAudioCDReader::read(LONGLONG pos, LONG length, BYTE *buffer, LONG *actual) + { + LONG nbRead = 0; + + if (actual) { + *actual = 0; + } + + if (pos < sizeof(WaveStructure)) { + //we first copy the content of the structure + nbRead = qMin(LONG(sizeof(WaveStructure) - pos), length); + qMemCopy(buffer, reinterpret_cast<char*>(m_waveHeader) + pos, nbRead); + } + + const LONGLONG posInTrack = pos - sizeof(WaveStructure) + nbRead; + const int bytesLeft = qMin(m_waveHeader->dataLength - posInTrack, LONGLONG(length - nbRead)); + + if (bytesLeft > 0) { + + //we need to read again + + const int surplus = posInTrack % SECTOR_SIZE; //how many bytes too much at the beginning + const int firstSector = posInTrack / SECTOR_SIZE, + lastSector = (posInTrack + length - 1) / SECTOR_SIZE; + const int sectorsNeeded = lastSector - firstSector + 1; + int sectorsRead = 0; + + QByteArray ba(sectorsNeeded * SECTOR_SIZE, 0); + + + RAW_READ_INFO ReadInfo; + ReadInfo.TrackMode = CDDA; // Always use CDDA (numerical: 2) + ReadInfo.DiskOffset.QuadPart = (m_trackAddress + firstSector) * 2048; + ReadInfo.SectorCount = qMin(sectorsNeeded - sectorsRead, NB_SECTORS_READ); + while (ReadInfo.SectorCount) { + DWORD dummy = 0; + if (::DeviceIoControl( m_cddrive, IOCTL_CDROM_RAW_READ, + &ReadInfo, sizeof(ReadInfo), + ba.data() + sectorsRead * SECTOR_SIZE, + ReadInfo.SectorCount * SECTOR_SIZE, + &dummy, NULL ) ) + { + ReadInfo.DiskOffset.QuadPart += ReadInfo.SectorCount * 2048; + sectorsRead += ReadInfo.SectorCount; + ReadInfo.SectorCount = qMin(sectorsNeeded - sectorsRead, NB_SECTORS_READ); + }else { + qWarning("an error occurred while reading from the media"); + return S_FALSE; + } + + } + + //consume bytes on the buffer + qMemCopy(buffer + nbRead, ba.data() + surplus, bytesLeft); + + //at this point we have all we need in the buffer + nbRead += bytesLeft; + } + + if (actual) { + *actual = nbRead; + } + + return nbRead == length ? S_OK : S_FALSE; + } + + QList<qint64> QAudioCDReader::titles() const + { + QList<qint64> ret; + ret << 0; + for(int i = m_toc->FirstTrack; i <= m_toc->LastTrack ; ++i) { + const uchar *address = m_toc->TrackData[i].Address; + ret << ((address[0] * 60 + address[1]) * 60 + address[2]) * 1000 + address[3]*1000/75 - 2000; + + } + return ret; + } + + + QAudioCDPlayer::QAudioCDPlayer() : QBaseFilter(CLSID_NULL) + { + new QAudioCDReader(this); + } + + QAudioCDPlayer::~QAudioCDPlayer() + { + } + + STDMETHODIMP QAudioCDPlayer::QueryInterface(REFIID iid, void** out) + { + if (iid == IID_ITitleInterface) { + //we reroute that to the pin + return pins().first()->QueryInterface(iid, out); + } else { + return QBaseFilter::QueryInterface(iid, out); + } + } + } +} + +#endif //QT_NO_PHONON_MEDIACONTROLLER + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/ds9/qaudiocdreader.h b/src/3rdparty/phonon/ds9/qaudiocdreader.h new file mode 100644 index 0000000..eff845d --- /dev/null +++ b/src/3rdparty/phonon/ds9/qaudiocdreader.h @@ -0,0 +1,58 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_QAUDIOCDREADER_H +#define PHONON_QAUDIOCDREADER_H + +#include "qasyncreader.h" +#include "qbasefilter.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_MEDIACONTROLLER + +namespace Phonon +{ + namespace DS9 + { + struct CDROM_TOC; + struct WaveStructure; + EXTERN_C const IID IID_ITitleInterface; + + //interface for the Titles + struct ITitleInterface : public IUnknown + { + virtual QList<qint64> titles() const = 0; + }; + + + class QAudioCDPlayer : public QBaseFilter + { + public: + QAudioCDPlayer(); + ~QAudioCDPlayer(); + STDMETHODIMP QueryInterface(REFIID iid, void** out); + }; + + } +} + +#endif //QT_NO_PHONON_MEDIACONTROLLER + +QT_END_NAMESPACE + +#endif diff --git a/src/3rdparty/phonon/ds9/qbasefilter.cpp b/src/3rdparty/phonon/ds9/qbasefilter.cpp new file mode 100644 index 0000000..95cab92 --- /dev/null +++ b/src/3rdparty/phonon/ds9/qbasefilter.cpp @@ -0,0 +1,831 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#include "qbasefilter.h" +#include "qpin.h" + +#include <QtCore/QMutex> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace DS9 + { + + class QEnumPins : public IEnumPins + { + public: + QEnumPins(QBaseFilter *filter) : m_refCount(1), + m_filter(filter), m_pins(filter->pins()), m_index(0) + { + m_filter->AddRef(); + } + + virtual ~QEnumPins() + { + m_filter->Release(); + } + + STDMETHODIMP QueryInterface(const IID &iid,void **out) + { + if (!out) { + return E_POINTER; + } + + HRESULT hr = S_OK; + if (iid == IID_IEnumPins) { + *out = static_cast<IEnumPins*>(this); + } else if (iid == IID_IUnknown) { + *out = static_cast<IUnknown*>(this); + } else { + *out = 0; + hr = E_NOINTERFACE; + } + + if (S_OK) + AddRef(); + return hr; + } + + STDMETHODIMP_(ULONG) AddRef() + { + return InterlockedIncrement(&m_refCount); + } + + STDMETHODIMP_(ULONG) Release() + { + ULONG refCount = InterlockedDecrement(&m_refCount); + if (refCount == 0) { + delete this; + } + + return refCount; + } + + STDMETHODIMP Next(ULONG count,IPin **ret,ULONG *fetched) + { + QMutexLocker locker(&m_mutex); + if (m_filter->pins() != m_pins) { + return VFW_E_ENUM_OUT_OF_SYNC; + } + + if (fetched == 0 && count > 1) { + return E_INVALIDARG; + } + + if (!ret) { + return E_POINTER; + } + + int nbfetched = 0; + while (nbfetched < int(count) && m_index < m_pins.count()) { + IPin *current = m_pins[m_index]; + current->AddRef(); + ret[nbfetched] = current; + nbfetched++; + m_index++; + } + + if (fetched) { + *fetched = nbfetched; + } + + return nbfetched == count ? S_OK : S_FALSE; + } + + STDMETHODIMP Skip(ULONG count) + { + QMutexLocker locker(&m_mutex); + if (m_filter->pins() != m_pins) { + return VFW_E_ENUM_OUT_OF_SYNC; + } + + m_index = qMin(m_index + int(count), m_pins.count()); + return m_index == m_pins.count() ? S_FALSE : S_OK; + } + + STDMETHODIMP Reset() + { + QMutexLocker locker(&m_mutex); + m_index = 0; + return S_OK; + } + + STDMETHODIMP Clone(IEnumPins **out) + { + QMutexLocker locker(&m_mutex); + if (m_filter->pins() != m_pins) { + return VFW_E_ENUM_OUT_OF_SYNC; + } + + if (!out) { + return E_POINTER; + } + + *out = new QEnumPins(m_filter); + (*out)->Skip(m_index); + return S_OK; + } + + + private: + LONG m_refCount; + QBaseFilter *m_filter; + QList<QPin*> m_pins; + int m_index; + QMutex m_mutex; + }; + + + QBaseFilter::QBaseFilter(const CLSID &clsid): + m_refCount(1), m_clsid(clsid), m_clock(0), m_graph(0), m_state(State_Stopped) + { + } + + QBaseFilter::~QBaseFilter() + { + while (!m_pins.isEmpty()) { + delete m_pins.first(); + } + } + + const QList<QPin *> QBaseFilter::pins() const + { + QReadLocker locker(&m_lock); + return m_pins; + } + + void QBaseFilter::addPin(QPin *pin) + { + QWriteLocker locker(&m_lock); + m_pins.append(pin); + } + + void QBaseFilter::removePin(QPin *pin) + { + QWriteLocker locker(&m_lock); + m_pins.removeAll(pin); + } + + FILTER_STATE QBaseFilter::state() const + { + return m_state; + } + + IFilterGraph *QBaseFilter::graph() const + { + return m_graph; + } + + STDMETHODIMP QBaseFilter::QueryInterface(REFIID iid, void **out) + { + if (!out) { + return E_POINTER; + } + + HRESULT hr = S_OK; + + if (iid == IID_IBaseFilter) { + *out = static_cast<IBaseFilter*>(this); + } else if (iid == IID_IMediaFilter) { + *out = static_cast<IMediaFilter*>(this); + } else if (iid == IID_IPersist) { + *out = static_cast<IPersist*>(this); + } else if (iid == IID_IUnknown) { + *out = static_cast<IUnknown*>(static_cast<IBaseFilter*>(this)); + } + else if (iid == IID_IMediaPosition || iid == IID_IMediaSeeking) { + if (inputPins().isEmpty()) { + if (*out = getUpStreamInterface(iid)) { + return S_OK; //we return here to avoid adding a reference + } else { + hr = E_NOINTERFACE; + } + } else if (iid == IID_IMediaSeeking) { + *out = static_cast<IMediaSeeking*>(this); + } else if (iid == IID_IMediaPosition ||iid == IID_IDispatch) { + *out = static_cast<IMediaPosition*>(this); + } + } else { + *out = 0; + hr = E_NOINTERFACE; + } + + if (hr == S_OK) { + AddRef(); + } + + return hr; + } + + STDMETHODIMP_(ULONG) QBaseFilter::AddRef() + { + return InterlockedIncrement(&m_refCount); + } + + STDMETHODIMP_(ULONG) QBaseFilter::Release() + { + ULONG refCount = InterlockedDecrement(&m_refCount); + if (refCount == 0) { + delete this; + } + + return refCount; + } + + STDMETHODIMP QBaseFilter::GetClassID(CLSID *clsid) + { + QReadLocker locker(&m_lock); + *clsid = m_clsid; + return S_OK; + } + + STDMETHODIMP QBaseFilter::Stop() + { + QWriteLocker locker(&m_lock); + m_state = State_Stopped; + return S_OK; + } + + STDMETHODIMP QBaseFilter::Pause() + { + QWriteLocker locker(&m_lock); + m_state = State_Paused; + return S_OK; + } + + STDMETHODIMP QBaseFilter::Run(REFERENCE_TIME) + { + QWriteLocker locker(&m_lock); + m_state = State_Running; + return S_OK; + } + + STDMETHODIMP QBaseFilter::GetState(DWORD, FILTER_STATE *state) + { + QReadLocker locker(&m_lock); + if (!state) { + return E_POINTER; + } + + *state = m_state; + return S_OK; + } + + STDMETHODIMP QBaseFilter::SetSyncSource(IReferenceClock *clock) + { + QWriteLocker locker(&m_lock); + if (clock) { + clock->AddRef(); + } + if (m_clock) { + m_clock->Release(); + } + m_clock = clock; + return S_OK; + } + + STDMETHODIMP QBaseFilter::GetSyncSource(IReferenceClock **clock) + { + QReadLocker locker(&m_lock); + if (!clock) { + return E_POINTER; + } + + if (m_clock) { + m_clock->AddRef(); + } + + *clock = m_clock; + return S_OK; + } + + STDMETHODIMP QBaseFilter::FindPin(LPCWSTR name, IPin**pin) + { + if (!pin) { + return E_POINTER; + } + + for (int i = 0; i < m_pins.count(); ++i) { + IPin * current = m_pins.at(i); + PIN_INFO info; + current->QueryPinInfo(&info); + if (info.pFilter) { + info.pFilter->Release(); + } + if ( wcscmp(info.achName, name) == 0) { + *pin = current; + current->AddRef(); + return S_OK; + } + } + + *pin = 0; + return VFW_E_NOT_FOUND; + } + + STDMETHODIMP QBaseFilter::QueryFilterInfo(FILTER_INFO *info ) + { + QReadLocker locker(&m_lock); + if (!info) { + return E_POINTER; + } + info->pGraph = m_graph; + if (m_graph) { + m_graph->AddRef(); + } + qMemCopy(info->achName, m_name.utf16(), qMin(MAX_FILTER_NAME, m_name.length()+1) *2); + return S_OK; + } + + STDMETHODIMP QBaseFilter::JoinFilterGraph(IFilterGraph *graph, LPCWSTR name) + { + QWriteLocker locker(&m_lock); + m_graph = graph; + m_name = QString::fromUtf16((const unsigned short*)name); + return S_OK; + } + + STDMETHODIMP QBaseFilter::EnumPins( IEnumPins **ep) + { + if (!ep) { + return E_POINTER; + } + + *ep = new QEnumPins(this); + return S_OK; + } + + + STDMETHODIMP QBaseFilter::QueryVendorInfo(LPWSTR *) + { + //we give no information on that + return E_NOTIMPL; + } + + //implementation from IMediaSeeking + STDMETHODIMP QBaseFilter::GetCapabilities(DWORD *pCapabilities) + { + IMediaSeeking *ms = getUpstreamMediaSeeking(); + if (!ms) { + return E_NOTIMPL; + } + + HRESULT hr = ms->GetCapabilities(pCapabilities); + ms->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::CheckCapabilities(DWORD *pCapabilities) + { + IMediaSeeking *ms = getUpstreamMediaSeeking(); + if (!ms) { + return E_NOTIMPL; + } + + HRESULT hr = ms->CheckCapabilities(pCapabilities); + ms->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::IsFormatSupported(const GUID *pFormat) + { + IMediaSeeking *ms = getUpstreamMediaSeeking(); + if (!ms) { + return E_NOTIMPL; + } + + HRESULT hr = ms->IsFormatSupported(pFormat); + ms->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::QueryPreferredFormat(GUID *pFormat) + { + IMediaSeeking *ms = getUpstreamMediaSeeking(); + if (!ms) { + return E_NOTIMPL; + } + + HRESULT hr = ms->QueryPreferredFormat(pFormat); + ms->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::GetTimeFormat(GUID *pFormat) + { + IMediaSeeking *ms = getUpstreamMediaSeeking(); + if (!ms) { + return E_NOTIMPL; + } + + HRESULT hr = ms->GetTimeFormat(pFormat); + ms->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::IsUsingTimeFormat(const GUID *pFormat) + { + IMediaSeeking *ms = getUpstreamMediaSeeking(); + if (!ms) { + return E_NOTIMPL; + } + + HRESULT hr = ms->IsUsingTimeFormat(pFormat); + ms->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::SetTimeFormat(const GUID *pFormat) + { + IMediaSeeking *ms = getUpstreamMediaSeeking(); + if (!ms) { + return E_NOTIMPL; + } + + HRESULT hr = ms->SetTimeFormat(pFormat); + ms->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::GetDuration(LONGLONG *pDuration) + { + IMediaSeeking *ms = getUpstreamMediaSeeking(); + if (!ms) { + return E_NOTIMPL; + } + + HRESULT hr = ms->GetDuration(pDuration); + ms->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::GetStopPosition(LONGLONG *pStop) + { + IMediaSeeking *ms = getUpstreamMediaSeeking(); + if (!ms) { + return E_NOTIMPL; + } + + HRESULT hr = ms->GetStopPosition(pStop); + ms->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::GetCurrentPosition(LONGLONG *pCurrent) + { + IMediaSeeking *ms = getUpstreamMediaSeeking(); + if (!ms) { + return E_NOTIMPL; + } + + HRESULT hr = ms->GetCurrentPosition(pCurrent); + ms->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::ConvertTimeFormat(LONGLONG *pTarget, + const GUID *pTargetFormat, LONGLONG Source, const GUID *pSourceFormat) + { + IMediaSeeking *ms = getUpstreamMediaSeeking(); + if (!ms) { + return E_NOTIMPL; + } + + HRESULT hr = ms->ConvertTimeFormat(pTarget, pTargetFormat, Source, pSourceFormat); + ms->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::SetPositions(LONGLONG *pCurrent, DWORD dwCurrentFlags, LONGLONG *pStop, DWORD dwStopFlags) + { + IMediaSeeking *ms = getUpstreamMediaSeeking(); + if (!ms) { + return E_NOTIMPL; + } + + HRESULT hr = ms->SetPositions(pCurrent, dwCurrentFlags, pStop, dwStopFlags); + ms->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::GetPositions(LONGLONG *pCurrent, LONGLONG *pStop) + { + IMediaSeeking *ms = getUpstreamMediaSeeking(); + if (!ms) { + return E_NOTIMPL; + } + + HRESULT hr = ms->GetPositions(pCurrent, pStop); + ms->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::GetAvailable(LONGLONG *pEarliest, LONGLONG *pLatest) + { + IMediaSeeking *ms = getUpstreamMediaSeeking(); + if (!ms) { + return E_NOTIMPL; + } + + HRESULT hr = ms->GetAvailable(pEarliest, pLatest); + ms->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::SetRate(double dRate) + { + IMediaSeeking *ms = getUpstreamMediaSeeking(); + if (!ms) { + return E_NOTIMPL; + } + + HRESULT hr = ms->SetRate(dRate); + ms->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::GetRate(double *dRate) + { + IMediaSeeking *ms = getUpstreamMediaSeeking(); + if (!ms) { + return E_NOTIMPL; + } + + HRESULT hr = ms->GetRate(dRate); + ms->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::GetPreroll(LONGLONG *pllPreroll) + { + IMediaSeeking *ms = getUpstreamMediaSeeking(); + if (!ms) { + return E_NOTIMPL; + } + + HRESULT hr = ms->GetPreroll(pllPreroll); + ms->Release(); + return hr; + } + + //implementation from IMediaPosition + STDMETHODIMP QBaseFilter::get_Duration(REFTIME *plength) + { + IMediaPosition *mp = getUpstreamMediaPosition(); + if (!mp) { + return E_NOTIMPL; + } + + HRESULT hr = mp->get_Duration(plength); + mp->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::put_CurrentPosition(REFTIME llTime) + { + IMediaPosition *mp = getUpstreamMediaPosition(); + if (!mp) { + return E_NOTIMPL; + } + + HRESULT hr = mp->put_CurrentPosition(llTime); + mp->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::get_CurrentPosition(REFTIME *pllTime) + { + IMediaPosition *mp = getUpstreamMediaPosition(); + if (!mp) { + return E_NOTIMPL; + } + + HRESULT hr = mp->get_CurrentPosition(pllTime); + mp->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::get_StopTime(REFTIME *pllTime) + { + IMediaPosition *mp = getUpstreamMediaPosition(); + if (!mp) { + return E_NOTIMPL; + } + + HRESULT hr = mp->get_StopTime(pllTime); + mp->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::put_StopTime(REFTIME llTime) + { + IMediaPosition *mp = getUpstreamMediaPosition(); + if (!mp) { + return E_NOTIMPL; + } + + HRESULT hr = mp->put_StopTime(llTime); + mp->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::get_PrerollTime(REFTIME *pllTime) + { + IMediaPosition *mp = getUpstreamMediaPosition(); + if (!mp) { + return E_NOTIMPL; + } + + HRESULT hr = mp->get_PrerollTime(pllTime); + mp->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::put_PrerollTime(REFTIME llTime) + { + IMediaPosition *mp = getUpstreamMediaPosition(); + if (!mp) { + return E_NOTIMPL; + } + + HRESULT hr = mp->put_PrerollTime(llTime); + mp->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::put_Rate(double dRate) + { + IMediaPosition *mp = getUpstreamMediaPosition(); + if (!mp) { + return E_NOTIMPL; + } + + HRESULT hr = mp->put_Rate(dRate); + mp->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::get_Rate(double *pdRate) + { + IMediaPosition *mp = getUpstreamMediaPosition(); + if (!mp) { + return E_NOTIMPL; + } + + HRESULT hr = mp->get_Rate(pdRate); + mp->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::CanSeekForward(LONG *pCanSeekForward) + { + IMediaPosition *mp = getUpstreamMediaPosition(); + if (!mp) { + return E_NOTIMPL; + } + + HRESULT hr = mp->CanSeekForward(pCanSeekForward); + mp->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::CanSeekBackward(LONG *pCanSeekBackward) + { + IMediaPosition *mp = getUpstreamMediaPosition(); + if (!mp) { + return E_NOTIMPL; + } + + HRESULT hr = mp->CanSeekBackward(pCanSeekBackward); + mp->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::GetTypeInfoCount(UINT *pctinfo) + { + IMediaPosition *mp = getUpstreamMediaPosition(); + if (!mp) { + return E_NOTIMPL; + } + + HRESULT hr = mp->GetTypeInfoCount(pctinfo); + mp->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) + { + IMediaPosition *mp = getUpstreamMediaPosition(); + if (!mp) { + return E_NOTIMPL; + } + + HRESULT hr = mp->GetTypeInfo(iTInfo, lcid, ppTInfo); + mp->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) + { + IMediaPosition *mp = getUpstreamMediaPosition(); + if (!mp) { + return E_NOTIMPL; + } + + HRESULT hr = mp->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispId); + mp->Release(); + return hr; + } + + STDMETHODIMP QBaseFilter::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, + VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) + { + IMediaPosition *mp = getUpstreamMediaPosition(); + if (!mp) { + return E_NOTIMPL; + } + + HRESULT hr = mp->Invoke(dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); + mp->Release(); + return hr; + } + + + IMediaSeeking *QBaseFilter::getUpstreamMediaSeeking() + { + return static_cast<IMediaSeeking*>(getUpStreamInterface(IID_IMediaSeeking)); + } + + IMediaPosition *QBaseFilter::getUpstreamMediaPosition() + { + return static_cast<IMediaPosition*>(getUpStreamInterface(IID_IMediaPosition)); + } + + QList<QPin*> QBaseFilter::inputPins() const + { + QList<QPin*> ret; + for(int i = 0; i < m_pins.count(); ++i) { + QPin * pin = m_pins.at(i); + if (pin->direction() == PINDIR_INPUT) { + ret += pin; + } + } + return ret; + } + + QList<QPin*> QBaseFilter::outputPins() const + { + QList<QPin*> ret; + for(int i = 0; i < m_pins.count(); ++i) { + QPin * pin = m_pins.at(i); + if (pin->direction() == PINDIR_OUTPUT) { + ret += pin; + } + } + return ret; + } + + void *QBaseFilter::getUpStreamInterface(const IID &iid) const + { + const QList<QPin*> inputs = inputPins(); + for (int i = 0; i < inputs.count(); ++i) { + IPin *out = inputs.at(i)->connected(); + if (out) { + void *ms = 0; + out->QueryInterface(iid, &ms); + if (ms) { + return ms; + } + } + } + //none was found + return 0; + } + + + //addition + HRESULT QBaseFilter::processSample(IMediaSample *) + { + return S_OK; + } + + } +} + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/ds9/qbasefilter.h b/src/3rdparty/phonon/ds9/qbasefilter.h new file mode 100644 index 0000000..85f1431 --- /dev/null +++ b/src/3rdparty/phonon/ds9/qbasefilter.h @@ -0,0 +1,136 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_QBASEFILTER_H +#define PHONON_QBASEFILTER_H + +#include "phononds9_namespace.h" + +#include <QtCore/QString> +#include <QtCore/QList> +#include <QtCore/QReadWriteLock> + +#include <dshow.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace DS9 + { + class QPin; + class QBaseFilter : public IBaseFilter, public IMediaSeeking, public IMediaPosition + { + public: + QBaseFilter(const CLSID &clsid); + virtual ~QBaseFilter(); + + //implementation from IUnknown + STDMETHODIMP QueryInterface(REFIID iid, void** out); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + //implementation from IPersist + STDMETHODIMP GetClassID(CLSID *); + + //implementation from IMediaFilter + STDMETHODIMP Stop(); + STDMETHODIMP Pause(); + STDMETHODIMP Run(REFERENCE_TIME); + STDMETHODIMP GetState(DWORD, FILTER_STATE*); + STDMETHODIMP SetSyncSource(IReferenceClock*); + STDMETHODIMP GetSyncSource(IReferenceClock**); + + //implementation from IBaseFilter + STDMETHODIMP EnumPins(IEnumPins**); + STDMETHODIMP FindPin(LPCWSTR, IPin**); + STDMETHODIMP QueryFilterInfo(FILTER_INFO*); + STDMETHODIMP JoinFilterGraph(IFilterGraph*, LPCWSTR); + STDMETHODIMP QueryVendorInfo(LPWSTR*); + + //implementation from IMediaSeeking + STDMETHODIMP GetCapabilities(DWORD *pCapabilities); + STDMETHODIMP CheckCapabilities(DWORD *pCapabilities); + STDMETHODIMP IsFormatSupported(const GUID *pFormat); + STDMETHODIMP QueryPreferredFormat(GUID *pFormat); + STDMETHODIMP GetTimeFormat(GUID *pFormat); + STDMETHODIMP IsUsingTimeFormat(const GUID *pFormat); + STDMETHODIMP SetTimeFormat(const GUID *pFormat); + STDMETHODIMP GetDuration(LONGLONG *pDuration); + STDMETHODIMP GetStopPosition(LONGLONG *pStop); + STDMETHODIMP GetCurrentPosition(LONGLONG *pCurrent); + STDMETHODIMP ConvertTimeFormat(LONGLONG *pTarget, const GUID *pTargetFormat, LONGLONG Source, const GUID *pSourceFormat); + STDMETHODIMP SetPositions(LONGLONG *pCurrent, DWORD dwCurrentFlags, LONGLONG *pStop, DWORD dwStopFlags); + STDMETHODIMP GetPositions(LONGLONG *pCurrent, LONGLONG *pStop); + STDMETHODIMP GetAvailable(LONGLONG *pEarliest, LONGLONG *pLatest); + STDMETHODIMP SetRate(double dRate); + STDMETHODIMP GetRate(double *dRate); + STDMETHODIMP GetPreroll(LONGLONG *pllPreroll); + + //implementation from IMediaPosition + STDMETHODIMP get_Duration(REFTIME *plength); + STDMETHODIMP put_CurrentPosition(REFTIME llTime); + STDMETHODIMP get_CurrentPosition(REFTIME *pllTime); + STDMETHODIMP get_StopTime(REFTIME *pllTime); + STDMETHODIMP put_StopTime(REFTIME llTime); + STDMETHODIMP get_PrerollTime(REFTIME *pllTime); + STDMETHODIMP put_PrerollTime(REFTIME llTime); + STDMETHODIMP put_Rate(double dRate); + STDMETHODIMP get_Rate(double *pdRate); + STDMETHODIMP CanSeekForward(LONG *pCanSeekForward); + STDMETHODIMP CanSeekBackward(LONG *pCanSeekBackward); + + //implementation from IDispatch (coming from IMediaPosition) + STDMETHODIMP GetTypeInfoCount(UINT *pctinfo); + STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo); + STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId); + STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, + VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr); + + //own methods + const QList<QPin *> pins() const; + void addPin(QPin *pin); + void removePin(QPin *pin); + IFilterGraph *graph() const; + FILTER_STATE state() const; + + + //reimplement this if you want specific processing of media sample + virtual HRESULT processSample(IMediaSample *); + + private: + QList<QPin*> outputPins() const; + QList<QPin*> inputPins() const; + + void *getUpStreamInterface(const IID &iid) const; + IMediaSeeking *getUpstreamMediaSeeking(); + IMediaPosition *getUpstreamMediaPosition(); + + LONG m_refCount; + CLSID m_clsid; + QString m_name; + IReferenceClock *m_clock; + IFilterGraph *m_graph; + FILTER_STATE m_state; + QList<QPin *> m_pins; + mutable QReadWriteLock m_lock; + }; + } +} +QT_END_NAMESPACE + +#endif diff --git a/src/3rdparty/phonon/ds9/qmeminputpin.cpp b/src/3rdparty/phonon/ds9/qmeminputpin.cpp new file mode 100644 index 0000000..0af1bfd --- /dev/null +++ b/src/3rdparty/phonon/ds9/qmeminputpin.cpp @@ -0,0 +1,357 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#include "qmeminputpin.h" +#include "qbasefilter.h" +#include "compointer.h" + +#include <QtCore/QDebug> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace DS9 + { + + QMemInputPin::QMemInputPin(QBaseFilter *parent, const QVector<AM_MEDIA_TYPE> &mt, bool transform) : + QPin(parent, PINDIR_INPUT, mt), m_shouldDuplicateSamples(true), m_transform(transform) + { + } + + QMemInputPin::~QMemInputPin() + { + } + + STDMETHODIMP QMemInputPin::QueryInterface(REFIID iid, void **out) + { + if (!out) { + return E_POINTER; + } + + if (iid == IID_IMemInputPin) { + *out = static_cast<IMemInputPin*>(this); + AddRef(); + return S_OK; + } else { + return QPin::QueryInterface(iid, out); + } + } + + STDMETHODIMP_(ULONG) QMemInputPin::AddRef() + { + return QPin::AddRef(); + } + + STDMETHODIMP_(ULONG) QMemInputPin::Release() + { + return QPin::Release(); + } + + STDMETHODIMP QMemInputPin::EndOfStream() + { + //this allows to serialize with Receive calls + QMutexLocker locker(&m_mutexReceive); + for(int i = 0; i < m_outputs.count(); ++i) { + IPin *conn = m_outputs.at(i)->connected(); + if (conn) { + conn->EndOfStream(); + } + } + return S_OK; + } + + STDMETHODIMP QMemInputPin::BeginFlush() + { + //pass downstream + for(int i = 0; i < m_outputs.count(); ++i) { + IPin *conn = m_outputs.at(i)->connected(); + if (conn) { + conn->BeginFlush(); + } + } + QWriteLocker locker(&m_lock); + m_flushing = true; + return S_OK; + } + + STDMETHODIMP QMemInputPin::EndFlush() + { + //pass downstream + for(int i = 0; i < m_outputs.count(); ++i) { + IPin *conn = m_outputs.at(i)->connected(); + if (conn) { + conn->EndFlush(); + } + } + QWriteLocker locker(&m_lock); + m_flushing = false; + return S_OK; + } + + STDMETHODIMP QMemInputPin::NewSegment(REFERENCE_TIME start, REFERENCE_TIME stop, double rate) + { + for(int i = 0; i < m_outputs.count(); ++i) { + m_outputs.at(i)->NewSegment(start, stop, rate); + } + return S_OK; + } + + //reimplementation to set the type for the output pin + //no need to make a deep copy here + STDMETHODIMP QMemInputPin::ReceiveConnection(IPin *pin ,const AM_MEDIA_TYPE *mt) + { + HRESULT hr = QPin::ReceiveConnection(pin, mt); + if (hr == S_OK && + mt->majortype != MEDIATYPE_NULL && + mt->subtype != MEDIASUBTYPE_NULL && + mt->formattype != GUID_NULL) { + //we tell the output pins that they should connect with this type + for(int i = 0; i < m_outputs.count(); ++i) { + hr = m_outputs.at(i)->setAcceptedMediaType(connectedType()); + if (FAILED(hr)) { + break; + } + } + } + return hr; + } + + STDMETHODIMP QMemInputPin::GetAllocator(IMemAllocator **alloc) + { + if (!alloc) { + return E_POINTER; + } + + if (*alloc = memoryAllocator(true)) { + return S_OK; + } + + return VFW_E_NO_ALLOCATOR; + } + + STDMETHODIMP QMemInputPin::NotifyAllocator(IMemAllocator *alloc, BOOL readonly) + { + if (!alloc) { + return E_POINTER; + } + + { + QWriteLocker locker(&m_lock); + m_shouldDuplicateSamples = m_transform && readonly; + } + + setMemoryAllocator(alloc); + + for(int i = 0; i < m_outputs.count(); ++i) { + IPin *pin = m_outputs.at(i)->connected(); + if (pin) { + ComPointer<IMemInputPin> input(pin, IID_IMemInputPin); + input->NotifyAllocator(alloc, m_shouldDuplicateSamples); + } + } + + return S_OK; + } + + STDMETHODIMP QMemInputPin::GetAllocatorRequirements(ALLOCATOR_PROPERTIES *prop) + { + if (!prop) { + return E_POINTER; + } + + //we have no particular requirements + return E_NOTIMPL; + } + + STDMETHODIMP QMemInputPin::Receive(IMediaSample *sample) + { + QMutexLocker locker(&m_mutexReceive); + if (!sample) { + return E_POINTER; + } + + if (filterState() == State_Stopped) { + return VFW_E_WRONG_STATE; + } + + if (isFlushing()) { + return S_FALSE; //we are still flushing + } + + if (!m_shouldDuplicateSamples) { + //we do it just once + HRESULT hr = m_parent->processSample(sample); + if (!SUCCEEDED(hr)) { + return hr; + } + } + + for (int i = 0; i < m_outputs.count(); ++i) { + QPin *current = m_outputs.at(i); + IMediaSample *outSample = m_shouldDuplicateSamples ? + duplicateSampleForOutput(sample, current->memoryAllocator()) + : sample; + + if (m_shouldDuplicateSamples) { + m_parent->processSample(outSample); + } + + IPin *pin = current->connected(); + if (pin) { + ComPointer<IMemInputPin> input(pin, IID_IMemInputPin); + if (input) { + input->Receive(outSample); + } + } + + if (m_shouldDuplicateSamples) { + outSample->Release(); + } + } + return S_OK; + } + + STDMETHODIMP QMemInputPin::ReceiveMultiple(IMediaSample **samples,long count,long *nbDone) + { + //no need to lock here: there is no access to member data + if (!samples || !nbDone) { + return E_POINTER; + } + + *nbDone = 0; //initialization + while( *nbDone != count) { + HRESULT hr = Receive(samples[*nbDone]); + if (FAILED(hr)) { + return hr; + } + (*nbDone)++; + } + + return S_OK; + } + + STDMETHODIMP QMemInputPin::ReceiveCanBlock() + { + //we test the output to see if they can block + for(int i = 0; i < m_outputs.count(); ++i) { + IPin *input = m_outputs.at(i)->connected(); + if (input) { + ComPointer<IMemInputPin> meminput(input, IID_IMemInputPin); + if (meminput && meminput->ReceiveCanBlock() != S_FALSE) { + return S_OK; + } + } + } + return S_FALSE; + } + + //addition + //this should be used by the filter to tell it's input pins to which output they should route the samples + + void QMemInputPin::addOutput(QPin *output) + { + QWriteLocker locker(&m_lock); + m_outputs += output; + } + + void QMemInputPin::removeOutput(QPin *output) + { + QWriteLocker locker(&m_lock); + m_outputs.removeOne(output); + } + + QList<QPin*> QMemInputPin::outputs() const + { + QReadLocker locker(&m_lock); + return m_outputs; + } + + ALLOCATOR_PROPERTIES QMemInputPin::getDefaultAllocatorProperties() const + { + //those values reduce buffering a lot (good for the volume effect) + ALLOCATOR_PROPERTIES prop = {4096, 1, 1, 0}; + return prop; + } + + + IMediaSample *QMemInputPin::duplicateSampleForOutput(IMediaSample *sample, IMemAllocator *alloc) + { + LONG length = sample->GetActualDataLength(); + + HRESULT hr = alloc->Commit(); + if (hr == VFW_E_SIZENOTSET) { + ALLOCATOR_PROPERTIES prop = getDefaultAllocatorProperties(); + prop.cbBuffer = qMax(prop.cbBuffer, length); + ALLOCATOR_PROPERTIES actual; + //we just try to set the properties... + alloc->SetProperties(&prop, &actual); + hr = alloc->Commit(); + } + + Q_ASSERT(SUCCEEDED(hr)); + + IMediaSample *out; + hr = alloc->GetBuffer(&out, 0, 0, AM_GBF_NOTASYNCPOINT); + Q_ASSERT(SUCCEEDED(hr)); + + //let's copy the sample + { + REFERENCE_TIME start, end; + sample->GetTime(&start, &end); + out->SetTime(&start, &end); + } + + hr = out->SetActualDataLength(length); + Q_ASSERT(SUCCEEDED(hr)); + hr = out->SetDiscontinuity(sample->IsDiscontinuity()); + Q_ASSERT(SUCCEEDED(hr)); + + { + LONGLONG start, end; + hr = sample->GetMediaTime(&start, &end); + if (hr != VFW_E_MEDIA_TIME_NOT_SET) { + hr = out->SetMediaTime(&start, &end); + Q_ASSERT(SUCCEEDED(hr)); + } + } + + AM_MEDIA_TYPE *type = 0; + hr = sample->GetMediaType(&type); + Q_ASSERT(SUCCEEDED(hr)); + hr = out->SetMediaType(type); + Q_ASSERT(SUCCEEDED(hr)); + + hr = out->SetPreroll(sample->IsPreroll()); + Q_ASSERT(SUCCEEDED(hr)); + hr = out->SetSyncPoint(sample->IsSyncPoint()); + Q_ASSERT(SUCCEEDED(hr)); + + BYTE *dest = 0, *src = 0; + hr = out->GetPointer(&dest); + Q_ASSERT(SUCCEEDED(hr)); + hr = sample->GetPointer(&src); + Q_ASSERT(SUCCEEDED(hr)); + + qMemCopy(dest, src, sample->GetActualDataLength()); + + return out; + } + } +} + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/ds9/qmeminputpin.h b/src/3rdparty/phonon/ds9/qmeminputpin.h new file mode 100644 index 0000000..c449721 --- /dev/null +++ b/src/3rdparty/phonon/ds9/qmeminputpin.h @@ -0,0 +1,82 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_QMEMINPUTPIN_H +#define PHONON_QMEMINPUTPIN_H + + +#include <QtCore/QList> +#include <QtCore/QMutex> +#include "qpin.h" + +#include <dshow.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace DS9 + { + class QBaseFilter; + + //this class will be used for our effects + class QMemInputPin : public QPin, public IMemInputPin + { + public: + QMemInputPin(QBaseFilter *, const QVector<AM_MEDIA_TYPE> &, bool transform); + ~QMemInputPin(); + + //reimplementation from IUnknown + STDMETHODIMP QueryInterface(REFIID iid, void** out); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + //reimplementation from IPin + STDMETHODIMP ReceiveConnection(IPin *,const AM_MEDIA_TYPE *); + STDMETHODIMP BeginFlush(); + STDMETHODIMP EndFlush(); + STDMETHODIMP EndOfStream(); + STDMETHODIMP NewSegment(REFERENCE_TIME start, REFERENCE_TIME stop, double rate); + + //reimplementation from IMemAllocator + STDMETHODIMP GetAllocator(IMemAllocator **); + STDMETHODIMP NotifyAllocator(IMemAllocator *,BOOL); + STDMETHODIMP GetAllocatorRequirements(ALLOCATOR_PROPERTIES *); + STDMETHODIMP Receive(IMediaSample *); + STDMETHODIMP ReceiveMultiple(IMediaSample **,long,long *); + STDMETHODIMP ReceiveCanBlock(); + + //addition + void addOutput(QPin *output); + void removeOutput(QPin *output); + QList<QPin*> outputs() const; + + private: + IMediaSample *duplicateSampleForOutput(IMediaSample *, IMemAllocator *); + ALLOCATOR_PROPERTIES getDefaultAllocatorProperties() const; + + bool m_shouldDuplicateSamples; + const bool m_transform; //defines if the pin is transforming the samples + QList<QPin*> m_outputs; + QMutex m_mutexReceive; + }; + } +} + +QT_END_NAMESPACE + +#endif diff --git a/src/3rdparty/phonon/ds9/qpin.cpp b/src/3rdparty/phonon/ds9/qpin.cpp new file mode 100644 index 0000000..37fe48d --- /dev/null +++ b/src/3rdparty/phonon/ds9/qpin.cpp @@ -0,0 +1,653 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#include "qbasefilter.h" +#include "qpin.h" +#include "compointer.h" + +#include <QtCore/QMutex> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace DS9 + { + + static const AM_MEDIA_TYPE defaultMediaType() + { + AM_MEDIA_TYPE ret; + ret.majortype = MEDIATYPE_NULL; + ret.subtype = MEDIASUBTYPE_NULL; + ret.bFixedSizeSamples = TRUE; + ret.bTemporalCompression = FALSE; + ret.lSampleSize = 1; + ret.formattype = GUID_NULL; + ret.pUnk = 0; + ret.cbFormat = 0; + ret.pbFormat = 0; + return ret; + } + + class QEnumMediaTypes : public IEnumMediaTypes + { + public: + QEnumMediaTypes(QPin *pin) : m_refCount(1), m_pin(pin), m_index(0) + { + m_pin->AddRef(); + } + + ~QEnumMediaTypes() + { + m_pin->Release(); + } + + STDMETHODIMP QueryInterface(const IID &iid,void **out) + { + if (!out) { + return E_POINTER; + } + + HRESULT hr = S_OK; + if (iid == IID_IEnumMediaTypes) { + *out = static_cast<IEnumMediaTypes*>(this); + } else if (iid == IID_IUnknown) { + *out = static_cast<IUnknown*>(this); + } else { + *out = 0; + hr = E_NOINTERFACE; + } + + if (hr == S_OK) { + AddRef(); + } + return hr; + } + + STDMETHODIMP_(ULONG) AddRef() + { + return InterlockedIncrement(&m_refCount); + } + + STDMETHODIMP_(ULONG) Release() + { + ULONG refCount = InterlockedDecrement(&m_refCount); + if (refCount == 0) { + delete this; + } + + return refCount; + } + + STDMETHODIMP Next(ULONG count, AM_MEDIA_TYPE **out, ULONG *fetched) + { + QMutexLocker locker(&m_mutex); + if (!out) { + return E_POINTER; + } + + if (!fetched && count > 1) { + return E_INVALIDARG; + } + + int nbFetched = 0; + while (nbFetched < int(count) && m_index < m_pin->mediaTypes().count()) { + //the caller will deallocate the memory + *out = static_cast<AM_MEDIA_TYPE *>(::CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE))); + const AM_MEDIA_TYPE original = m_pin->mediaTypes().at(m_index); + **out = QPin::copyMediaType(original); + nbFetched++; + m_index++; + out++; + } + + if (fetched) { + *fetched = nbFetched; + } + + return nbFetched == count ? S_OK : S_FALSE; + } + + STDMETHODIMP Skip(ULONG count) + { + QMutexLocker locker(&m_mutex); + m_index = qMin(m_index + int(count), m_pin->mediaTypes().count()); + return (m_index == m_pin->mediaTypes().count()) ? S_FALSE : S_OK; + } + + STDMETHODIMP Reset() + { + QMutexLocker locker(&m_mutex); + m_index = 0; + return S_OK; + } + + STDMETHODIMP Clone(IEnumMediaTypes **out) + { + QMutexLocker locker(&m_mutex); + if (!out) { + return E_POINTER; + } + + *out = new QEnumMediaTypes(m_pin); + (*out)->Skip(m_index); + return S_OK; + } + + + private: + LONG m_refCount; + QPin *m_pin; + int m_index; + QMutex m_mutex; + }; + + + QPin::QPin(QBaseFilter *parent, PIN_DIRECTION dir, const QVector<AM_MEDIA_TYPE> &mt) : + m_memAlloc(0), m_parent(parent), m_refCount(1), m_connected(0), + m_direction(dir), m_mediaTypes(mt), m_connectedType(defaultMediaType()), + m_flushing(false) + { + Q_ASSERT(m_parent); + m_parent->addPin(this); + } + + QPin::~QPin() + { + m_parent->removePin(this); + setMemoryAllocator(0); + freeMediaType(m_connectedType); + } + + //reimplementation from IUnknown + STDMETHODIMP QPin::QueryInterface(REFIID iid, void**out) + { + if (!out) { + return E_POINTER; + } + + HRESULT hr = S_OK; + + if (iid == IID_IPin) { + *out = static_cast<IPin*>(this); + } else if (iid == IID_IUnknown) { + *out = static_cast<IUnknown*>(this); + } else if (m_direction == PINDIR_OUTPUT && (iid == IID_IMediaSeeking || iid == IID_IMediaPosition)) { + return m_parent->QueryInterface(iid, out); + } else { + *out = 0; + hr = E_NOINTERFACE; + } + + if (hr == S_OK) { + AddRef(); + } + return hr; + } + + STDMETHODIMP_(ULONG) QPin::AddRef() + { + return InterlockedIncrement(&m_refCount); + } + + STDMETHODIMP_(ULONG) QPin::Release() + { + ULONG refCount = InterlockedDecrement(&m_refCount); + if (refCount == 0) { + delete this; + } + + return refCount; + } + + //this is called on the input pins + STDMETHODIMP QPin::ReceiveConnection(IPin *pin, const AM_MEDIA_TYPE *type) + { + if (!pin ||!type) { + return E_POINTER; + } + + if (connected()) { + return VFW_E_ALREADY_CONNECTED; + } + + if (filterState() != State_Stopped) { + return VFW_E_NOT_STOPPED; + } + + if (QueryAccept(type) != S_OK) { + return VFW_E_TYPE_NOT_ACCEPTED; + } + + setConnected(pin); + setConnectedType(*type); + + return S_OK; + } + + //this is called on the output pins + STDMETHODIMP QPin::Connect(IPin *pin, const AM_MEDIA_TYPE *type) + { + if (!pin) { + return E_POINTER; + } + + if (connected()) { + return VFW_E_ALREADY_CONNECTED; + } + + if (filterState() != State_Stopped) { + return VFW_E_NOT_STOPPED; + } + + HRESULT hr = S_OK; + + setConnected(pin); + if (!type) { + + //let(s first try the output pin's mediaTypes + if (checkOutputMediaTypesConnection(pin) != S_OK && + checkOwnMediaTypesConnection(pin) != S_OK) { + hr = VFW_E_NO_ACCEPTABLE_TYPES; + } + } else if (QueryAccept(type) == S_OK) { + setConnectedType(*type); + hr = pin->ReceiveConnection(this, type); + } else { + hr = VFW_E_TYPE_NOT_ACCEPTED; + } + + if (FAILED(hr)) { + setConnected(0); + setConnectedType(defaultMediaType()); + } else { + ComPointer<IMemInputPin> input(pin, IID_IMemInputPin); + if (input) { + ComPointer<IMemAllocator> alloc; + input->GetAllocator(alloc.pparam()); + if (alloc) { + //be default we take the allocator from the input pin + //we have no reason to force using our own + setMemoryAllocator(alloc); + } + } + if (memoryAllocator() == 0) { + ALLOCATOR_PROPERTIES prop; + if (input && input->GetAllocatorRequirements(&prop) == S_OK) { + createDefaultMemoryAllocator(&prop); + } else { + createDefaultMemoryAllocator(); + } + } + + Q_ASSERT(memoryAllocator() != 0); + if (input) { + input->NotifyAllocator(memoryAllocator(), TRUE); //TRUE is arbitrarily chosen here + } + + } + + return hr; + } + + STDMETHODIMP QPin::Disconnect() + { + if (!connected()) { + return S_FALSE; + } + + if (filterState() != State_Stopped) { + return VFW_E_NOT_STOPPED; + } + + setConnected(0); + setConnectedType(defaultMediaType()); + if (m_direction == PINDIR_INPUT) { + setMemoryAllocator(0); + } + return S_OK; + } + + STDMETHODIMP QPin::ConnectedTo(IPin **other) + { + if (!other) { + return E_POINTER; + } + + *other = connected(true); + if (!(*other)) { + return VFW_E_NOT_CONNECTED; + } + + return S_OK; + } + + STDMETHODIMP QPin::ConnectionMediaType(AM_MEDIA_TYPE *type) + { + QReadLocker locker(&m_lock); + if (!type) { + return E_POINTER; + } + + *type = copyMediaType(m_connectedType); + if (!m_connected) { + return VFW_E_NOT_CONNECTED; + } else { + return S_OK; + } + } + + STDMETHODIMP QPin::QueryPinInfo(PIN_INFO *info) + { + QReadLocker locker(&m_lock); + if (!info) { + return E_POINTER; + } + + info->dir = m_direction; + info->pFilter = m_parent; + m_parent->AddRef(); + qMemCopy(info->achName, m_name.utf16(), qMin(MAX_FILTER_NAME, m_name.length()+1) *2); + + return S_OK; + } + + STDMETHODIMP QPin::QueryDirection(PIN_DIRECTION *dir) + { + QReadLocker locker(&m_lock); + if (!dir) { + return E_POINTER; + } + + *dir = m_direction; + return S_OK; + } + + STDMETHODIMP QPin::QueryId(LPWSTR *id) + { + QReadLocker locker(&m_lock); + if (!id) { + return E_POINTER; + } + + int nbBytes = (m_name.length()+1)*2; + *id = static_cast<LPWSTR>(::CoTaskMemAlloc(nbBytes)); + qMemCopy(*id, m_name.utf16(), nbBytes); + return S_OK; + } + + STDMETHODIMP QPin::QueryAccept(const AM_MEDIA_TYPE *type) + { + QReadLocker locker(&m_lock); + if (!type) { + return E_POINTER; + } + + for (int i = 0; i < m_mediaTypes.count(); ++i) { + const AM_MEDIA_TYPE ¤t = m_mediaTypes.at(i); + if ( (type->majortype == current.majortype) && + (current.subtype == MEDIASUBTYPE_NULL || type->subtype == current.subtype) && + (type->majortype == MEDIATYPE_Stream || type->formattype != GUID_NULL || current.formattype != GUID_NULL) && + (current.formattype == GUID_NULL || type->formattype == current.formattype) + ) { + return S_OK; + } + } + return S_FALSE; + } + + + STDMETHODIMP QPin::EnumMediaTypes(IEnumMediaTypes **emt) + { + if (!emt) { + return E_POINTER; + } + + *emt = new QEnumMediaTypes(this); + return S_OK; + } + + + STDMETHODIMP QPin::EndOfStream() + { + return E_UNEXPECTED; + } + + STDMETHODIMP QPin::BeginFlush() + { + return E_UNEXPECTED; + } + + STDMETHODIMP QPin::EndFlush() + { + return E_UNEXPECTED; + } + + STDMETHODIMP QPin::NewSegment(REFERENCE_TIME start, REFERENCE_TIME stop, double rate) + { + QReadLocker locker(&m_lock); + if (m_direction == PINDIR_OUTPUT && m_connected) { + //we deliver this downstream + m_connected->NewSegment(start, stop, rate); + } + return S_OK; + } + + STDMETHODIMP QPin::QueryInternalConnections(IPin **, ULONG*) + { + //this is not implemented on purpose (the input pins are connected to all the output pins) + return E_NOTIMPL; + } + + + HRESULT QPin::checkOutputMediaTypesConnection(IPin *pin) + { + IEnumMediaTypes *emt = 0; + HRESULT hr = pin->EnumMediaTypes(&emt); + if (hr != S_OK) { + return hr; + } + + AM_MEDIA_TYPE *type = 0; + while (emt->Next(1, &type, 0) == S_OK) { + if (QueryAccept(type) == S_OK) { + setConnectedType(*type); + if (pin->ReceiveConnection(this, type) == S_OK) { + freeMediaType(type); + return S_OK; + } else { + setConnectedType(defaultMediaType()); + freeMediaType(type); + } + } + } + + //we didn't find a suitable type + return S_FALSE; + } + + HRESULT QPin::checkOwnMediaTypesConnection(IPin *pin) + { + for(int i = 0; i < m_mediaTypes.count(); ++i) { + const AM_MEDIA_TYPE ¤t = m_mediaTypes.at(i); + setConnectedType(current); + HRESULT hr = pin->ReceiveConnection(this, ¤t); + if (hr == S_OK) { + return S_OK; + } + } + + //we didn't find a suitable type + return S_FALSE; + } + + void QPin::freeMediaType(const AM_MEDIA_TYPE &type) + { + if (type.cbFormat) { + ::CoTaskMemFree(type.pbFormat); + } + if (type.pUnk) { + type.pUnk->Release(); + } + } + + void QPin::freeMediaType(AM_MEDIA_TYPE *type) + { + freeMediaType(*type); + ::CoTaskMemFree(type); + } + + //addition + + PIN_DIRECTION QPin::direction() const + { + return m_direction; + } + + void QPin::setConnectedType(const AM_MEDIA_TYPE &type) + { + QWriteLocker locker(&m_lock); + + //1st we free memory + freeMediaType(m_connectedType); + + m_connectedType = copyMediaType(type); + } + + const AM_MEDIA_TYPE &QPin::connectedType() const + { + QReadLocker locker(&m_lock); + return m_connectedType; + } + + void QPin::setConnected(IPin *pin) + { + QWriteLocker locker(&m_lock); + if (pin) { + pin->AddRef(); + } + if (m_connected) { + m_connected->Release(); + } + m_connected = pin; + } + + IPin *QPin::connected(bool addref) const + { + QReadLocker locker(&m_lock); + if (addref && m_connected) { + m_connected->AddRef(); + } + return m_connected; + } + + bool QPin::isFlushing() const + { + QReadLocker locker(&m_lock); + return m_flushing; + } + + FILTER_STATE QPin::filterState() const + { + QReadLocker locker(&m_lock); + FILTER_STATE fstate = State_Stopped; + m_parent->GetState(0, &fstate); + return fstate; + } + + QVector<AM_MEDIA_TYPE> QPin::mediaTypes() const + { + QReadLocker locker(&m_lock); + return m_mediaTypes; + } + + HRESULT QPin::setAcceptedMediaType(const AM_MEDIA_TYPE &mt) + { + const QVector<AM_MEDIA_TYPE> oldMediaTypes = m_mediaTypes; + m_mediaTypes = QVector<AM_MEDIA_TYPE>() << mt; + + HRESULT hr = S_OK; + + IPin *conn = connected(); + if (conn) { + //try to reconnect to redefine the media type + conn->Disconnect(); + Disconnect(); + hr = Connect(conn, 0); + if (FAILED(hr)) { + m_mediaTypes = oldMediaTypes; + Connect(conn, 0); //just redo the connection with the old media types + } + } + return hr; + } + + void QPin::createDefaultMemoryAllocator(ALLOCATOR_PROPERTIES *prop) + { + ComPointer<IMemAllocator> alloc(CLSID_MemoryAllocator, IID_IMemAllocator); + if (prop) { + alloc->SetProperties(prop, 0); + } + setMemoryAllocator(alloc); + } + + void QPin::setMemoryAllocator(IMemAllocator *alloc) + { + QWriteLocker locker(&m_lock); + if (alloc) { + alloc->AddRef(); + } + if (m_memAlloc) { + m_memAlloc->Release(); + } + m_memAlloc = alloc; + } + + IMemAllocator *QPin::memoryAllocator(bool addref) const + { + QReadLocker locker(&m_lock); + if (addref && m_memAlloc) { + m_memAlloc->AddRef(); + } + return m_memAlloc; + } + + AM_MEDIA_TYPE QPin::copyMediaType(const AM_MEDIA_TYPE &type) + { + AM_MEDIA_TYPE ret = type; + + //make a deep copy here + if (ret.cbFormat == 0 || ret.pbFormat == 0) { + ret.cbFormat = 0; + ret.pbFormat = 0; + ret.formattype = GUID_NULL; + } else { + ret.pbFormat = reinterpret_cast<BYTE*>(::CoTaskMemAlloc(type.cbFormat)); + qMemCopy(ret.pbFormat, type.pbFormat, type.cbFormat); + } + + if (type.pUnk) { + type.pUnk->AddRef(); + } + return ret; + } + + + } +} + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/ds9/qpin.h b/src/3rdparty/phonon/ds9/qpin.h new file mode 100644 index 0000000..a3287c4 --- /dev/null +++ b/src/3rdparty/phonon/ds9/qpin.h @@ -0,0 +1,121 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_QPIN_H +#define PHONON_QPIN_H + +#include "phononds9_namespace.h" + +#include <QtCore/QString> +#include <QtCore/QVector> +#include <QtCore/QReadWriteLock> + +#include <dshow.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace DS9 + { + class QBaseFilter; + + //this is the base class for our self-implemented Pins + class QPin : public IPin + { + public: + QPin(QBaseFilter *parent, PIN_DIRECTION dir, const QVector<AM_MEDIA_TYPE> &mt); + virtual ~QPin(); + + //reimplementation from IUnknown + STDMETHODIMP QueryInterface(REFIID iid, void** out); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + //reimplementation from IPin + STDMETHODIMP Connect(IPin *,const AM_MEDIA_TYPE *); + STDMETHODIMP ReceiveConnection(IPin *,const AM_MEDIA_TYPE *); + STDMETHODIMP Disconnect(); + STDMETHODIMP ConnectedTo(IPin **); + STDMETHODIMP ConnectionMediaType(AM_MEDIA_TYPE *); + STDMETHODIMP QueryPinInfo(PIN_INFO *); + STDMETHODIMP QueryDirection(PIN_DIRECTION *); + STDMETHODIMP QueryId(LPWSTR*); + STDMETHODIMP QueryAccept(const AM_MEDIA_TYPE*); + STDMETHODIMP EnumMediaTypes(IEnumMediaTypes **); + STDMETHODIMP QueryInternalConnections(IPin **, ULONG*); + STDMETHODIMP EndOfStream(); + STDMETHODIMP BeginFlush(); + STDMETHODIMP EndFlush(); + STDMETHODIMP NewSegment(REFERENCE_TIME, REFERENCE_TIME, double); + + QVector<AM_MEDIA_TYPE> mediaTypes() const; + + HRESULT setAcceptedMediaType(const AM_MEDIA_TYPE &); + + bool isFlushing() const; + void setConnectedType(const AM_MEDIA_TYPE &type); + const AM_MEDIA_TYPE &connectedType() const; + void setConnected(IPin *pin); + IPin *connected(bool = false) const; + void setMemoryAllocator(IMemAllocator *alloc); + IMemAllocator *memoryAllocator(bool = false) const; + void createDefaultMemoryAllocator(ALLOCATOR_PROPERTIES * = 0); + PIN_DIRECTION direction() const; + + FILTER_STATE filterState() const; + + static AM_MEDIA_TYPE copyMediaType(const AM_MEDIA_TYPE &type); + static void freeMediaType(AM_MEDIA_TYPE *type); + static void freeMediaType(const AM_MEDIA_TYPE &type); + + protected: + //this can be used by sub-classes + mutable QReadWriteLock m_lock; + QBaseFilter *m_parent; + bool m_flushing; + + private: + HRESULT checkOutputMediaTypesConnection(IPin *pin); + HRESULT checkOwnMediaTypesConnection(IPin *pin); + + LONG m_refCount; + IPin *m_connected; + const PIN_DIRECTION m_direction; + QVector<AM_MEDIA_TYPE> m_mediaTypes; //accepted media types + AM_MEDIA_TYPE m_connectedType; + QString m_name; + IMemAllocator *m_memAlloc; + }; + + //utility function + class QAMMediaType : public AM_MEDIA_TYPE + { + public: + ~QAMMediaType() + { + QPin::freeMediaType(*this); + } + + }; + + } +} + +QT_END_NAMESPACE + +#endif //PHONON_QPIN_H diff --git a/src/3rdparty/phonon/ds9/videorenderer_soft.cpp b/src/3rdparty/phonon/ds9/videorenderer_soft.cpp new file mode 100644 index 0000000..dd6e076 --- /dev/null +++ b/src/3rdparty/phonon/ds9/videorenderer_soft.cpp @@ -0,0 +1,1011 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + + +#include "videorenderer_soft.h" + +#ifndef QT_NO_PHONON_VIDEO + +#include "qmeminputpin.h" +#include "qbasefilter.h" + +#include <QtGui/QPainter> +#include <QtGui/QPaintEngine> +#include <QtGui/QApplication> +#include <QtCore/QTime> + +#define _USE_MATH_DEFINES //for pi +#include <QtCore/qmath.h> //for sin and cos +/* M_PI is a #define that may or may not be handled in <cmath> */ +#ifndef M_PI +#define M_PI 3.14159265358979323846264338327950288419717 +#endif + +#include <dvdmedia.h> //for VIDEOINFOHEADER2 + +//this will make a display every second of how many frames were pocessed and actually displayed +//#define FPS_COUNTER + +#ifdef Q_OS_WINCE +#define QT_NO_OPENGL +#endif + +#ifndef QT_NO_OPENGL +#include <gl/gl.h> +#ifndef GL_FRAGMENT_PROGRAM_ARB +#define GL_FRAGMENT_PROGRAM_ARB 0x8804 +#define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875 +#endif + +// support old OpenGL installations (1.2) +// assume that if TEXTURE0 isn't defined, none are +#ifndef GL_TEXTURE0 +# define GL_TEXTURE0 0x84C0 +# define GL_TEXTURE1 0x84C1 +# define GL_TEXTURE2 0x84C2 +#endif + +// arbfp1 fragment program for converting yuv (YV12) to rgb +static const char yv12ToRgb[] = +"!!ARBfp1.0" +"PARAM c[5] = { program.local[0..1]," +" { 1.164, 0, 1.596, 0.5 }," +" { 0.0625, 1.164, -0.391, -0.81300002 }," +" { 1.164, 2.0179999, 0 } };" +"TEMP R0;" +"TEX R0.x, fragment.texcoord[0], texture[1], 2D;" +"ADD R0.y, R0.x, -c[2].w;" +"TEX R0.x, fragment.texcoord[0], texture[2], 2D;" +"ADD R0.x, R0, -c[2].w;" +"MUL R0.z, R0.y, c[0].w;" +"MAD R0.z, R0.x, c[0], R0;" +"MUL R0.w, R0.x, c[0];" +"MUL R0.z, R0, c[0].y;" +"TEX R0.x, fragment.texcoord[0], texture[0], 2D;" +"MAD R0.y, R0, c[0].z, R0.w;" +"ADD R0.x, R0, -c[3];" +"MUL R0.y, R0, c[0];" +"MUL R0.z, R0, c[1].x;" +"MAD R0.x, R0, c[0].y, c[0];" +"MUL R0.y, R0, c[1].x;" +"DP3 result.color.x, R0, c[2];" +"DP3 result.color.y, R0, c[3].yzww;" +"DP3 result.color.z, R0, c[4];" +"MOV result.color.w, c[1].y;" +"END"; + +static const char yuy2ToRgb[] = + "!!ARBfp1.0" +"PARAM c[5] = { program.local[0..1]," +" { 0.5, 2, 1, 0.0625 }," +" { 1.164, 0, 1.596, 2.0179999 }," +" { 1.164, -0.391, -0.81300002 } };" +"TEMP R0;" +"TEMP R1;" +"TEMP R2;" +"FLR R1.z, fragment.texcoord[0].x;" +"ADD R0.x, R1.z, c[2];" +"ADD R1.z, fragment.texcoord[0].x, -R1;" +"MUL R1.x, fragment.texcoord[0].z, R0;" +"MOV R1.y, fragment.texcoord[0];" +"TEX R0, R1, texture[0], 2D;" +"ADD R1.y, R0.z, -R0.x;" +"MUL R2.x, R1.z, R1.y;" +"MAD R0.x, R2, c[2].y, R0;" +"MOV R1.y, fragment.texcoord[0];" +"ADD R1.x, fragment.texcoord[0].z, R1;" +"TEX R1.xyw, R1, texture[0], 2D;" +"ADD R2.x, R1, -R0.z;" +"MAD R1.x, R1.z, c[2].y, -c[2].z;" +"MAD R0.z, R1.x, R2.x, R0;" +"ADD R1.xy, R1.ywzw, -R0.ywzw;" +"ADD R0.z, R0, -R0.x;" +"SGE R1.w, R1.z, c[2].x;" +"MAD R0.x, R1.w, R0.z, R0;" +"MAD R0.yz, R1.z, R1.xxyw, R0.xyww;" +"ADD R0.xyz, R0, -c[2].wxxw;" +"MUL R0.w, R0.y, c[0];" +"MAD R0.w, R0.z, c[0].z, R0;" +"MUL R0.z, R0, c[0].w;" +"MAD R0.y, R0, c[0].z, R0.z;" +"MUL R0.w, R0, c[0].y;" +"MUL R0.y, R0, c[0];" +"MUL R0.z, R0.w, c[1].x;" +"MAD R0.x, R0, c[0].y, c[0];" +"MUL R0.y, R0, c[1].x;" +"DP3 result.color.x, R0, c[3];" +"DP3 result.color.y, R0, c[4];" +"DP3 result.color.z, R0, c[3].xwyw;" +"MOV result.color.w, c[1].y;" +"END"; + +#endif //QT_NO_OPENGL + +#define CLIP_SHIFT_RIGHT_8(c) ((c) < 0 ? 0 : (c) > 0xffff ? 0xff : (c) >> 8) +#define CLIP_SHIFT_LEFT_8(c) ((c) < 0 ? 0 : (c) > 0xffff ? 0xff0000 : ( ((c) << 8) & 0xff0000) ) +#define CLIP_NO_SHIFT(c) ((c) < 0 ? 0 : (c) > 0xffff ? 0xff00 : ((c) & 0xff00) ) +#define CLIPPED_PIXEL(base, r, g, b) (0xff000000u | CLIP_SHIFT_LEFT_8(base+r) | CLIP_NO_SHIFT(base+g) | CLIP_SHIFT_RIGHT_8(base+b)) +#define CLIPPED_PIXEL2(r, g, b) (0xff000000u | CLIP_SHIFT_LEFT_8(r) | CLIP_NO_SHIFT(g) | CLIP_SHIFT_RIGHT_8(b)) + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace DS9 + { + static const QVector<AM_MEDIA_TYPE> videoMediaTypes() + { + AM_MEDIA_TYPE mt; + qMemSet(&mt, 0, sizeof(AM_MEDIA_TYPE)); + mt.majortype = MEDIATYPE_Video; + + //we accept any video format + mt.formattype = GUID_NULL; + mt.cbFormat = 0; + mt.pbFormat = 0; + + QVector<AM_MEDIA_TYPE> ret; + + //we support YUV (YV12 and YUY2) and RGB32 + mt.subtype = MEDIASUBTYPE_YV12; + ret << mt; + mt.subtype = MEDIASUBTYPE_YUY2; + ret << mt; + mt.subtype = MEDIASUBTYPE_RGB32; + ret << mt; + + return ret; + } + + class VideoRendererSoftFilter : public QBaseFilter + { + public: + VideoRendererSoftFilter(VideoRendererSoft *renderer); + + ~VideoRendererSoftFilter(); + + QSize videoSize() const; + +#ifndef QT_NO_OPENGL + void freeGLResources() + { + if (m_usingOpenGL) { + //let's reinitialize those values + m_usingOpenGL = false; + //to be sure we recreate it + if (m_textureUploaded) { + glDeleteTextures(3, m_texture); + m_textureUploaded = false; + } + } + m_checkedPrograms = false; + } +#endif // QT_NO_OPENGL + + void freeResources() + { + QMutexLocker locker(&m_mutex); + m_sampleBuffer = ComPointer<IMediaSample>(); +#ifndef QT_NO_OPENGL + freeGLResources(); +#endif // QT_NO_OPENGL + m_textureUploaded = false; + } + + void endOfStream() + { + //received from the input pin + ::SetEvent(m_receiveCanWait); //unblocks the flow + + //we send the message to the graph + ComPointer<IMediaEventSink> sink(graph(), IID_IMediaEventSink); + if (sink) { + sink->Notify(EC_COMPLETE, S_OK, + reinterpret_cast<LONG_PTR>(static_cast<IBaseFilter*>(this))); + } + } + + void freeMediaSample() + { + QMutexLocker locker(&m_mutex); + m_sampleBuffer = ComPointer<IMediaSample>(); + } + + void beginFlush() + { + freeMediaSample(); + ::SetEvent(m_receiveCanWait); //unblocks the flow + } + + void endFlush() + { + if (m_inputPin->connected() == 0) { + ::SetEvent(m_receiveCanWait); //unblock the flow in receive + } else { + ::ResetEvent(m_receiveCanWait); //block the flow again + } + } + + STDMETHODIMP Stop() + { + HRESULT hr = QBaseFilter::Stop(); + beginFlush(); + return hr; + } + + STDMETHODIMP Pause() + { + HRESULT hr = QBaseFilter::Pause(); + if (m_inputPin->connected() == 0) { + ::SetEvent(m_receiveCanWait); //unblock the flow in receive + } else { + ::ResetEvent(m_receiveCanWait); //this will block + } + return hr; + } + + STDMETHODIMP Run(REFERENCE_TIME start) + { + HRESULT hr = QBaseFilter::Run(start); + m_start = start; + + if (m_inputPin->connected() == 0) { + endOfStream(); + } else { + ::SetEvent(m_receiveCanWait); //unblocks the flow (this event will block then again) + } + +#ifdef FPS_COUNTER + fpsTime.restart(); + nbFramesProcessed = 0; + nbFramesDisplayed = 0; +#endif + + return hr; + } + + HRESULT processSample(IMediaSample *sample); + + void applyMixerSettings(qreal brightness, qreal contrast, qreal hue, qreal saturation) + { + //let's normalize the values + m_brightness = brightness * 128; + m_contrast = contrast + 1.; + m_hue = hue * M_PI; + m_saturation = saturation + 1.; + } + + QImage currentImage() const + { + return m_currentImage; + } + + void setCurrentImage(const QImage &image) + { + QMutexLocker locker(&m_mutex); + m_currentImage = image; + } + + //the following function is called from the GUI thread + void repaintCurrentFrame(QPainter &painter, const QRect &r); + + + protected: + static void convertYV12toRGB(const uchar *data, const QSize &s, QImage &dest, + qreal brightness, qreal contrast, qreal hue, qreal saturation); + static void convertYUY2toRGB(const uchar *data, const QSize &s, QImage &dest, + qreal brightness, qreal contrast, qreal hue, qreal saturation); + static void normalizeRGB(const uchar *data, const QSize &s, QImage &destImage); + + private: + QPin *const m_inputPin; + ComPointer<IMediaSample> m_sampleBuffer; + QImage m_currentImage; + + + VideoRendererSoft *m_renderer; + mutable QMutex m_mutex; + REFERENCE_TIME m_start; + HANDLE m_renderEvent, m_receiveCanWait; // Signals sample to render + QSize m_size; + bool m_textureUploaded; + + //mixer settings + qreal m_brightness, + m_contrast, + m_hue, + m_saturation; + +#ifdef FPS_COUNTER + QTime fpsTime; + int nbFramesProcessed; + int nbFramesDisplayed; +#endif + +#ifndef QT_NO_OPENGL + enum Program + { + YV12toRGB = 0, + YUY2toRGB = 1, + ProgramCount = 2 + }; + + void updateTexture(); + bool checkGLPrograms(); + + // ARB_fragment_program + typedef void (APIENTRY *_glProgramStringARB) (GLenum, GLenum, GLsizei, const GLvoid *); + typedef void (APIENTRY *_glBindProgramARB) (GLenum, GLuint); + typedef void (APIENTRY *_glDeleteProgramsARB) (GLsizei, const GLuint *); + typedef void (APIENTRY *_glGenProgramsARB) (GLsizei, GLuint *); + typedef void (APIENTRY *_glProgramLocalParameter4fARB) (GLenum, GLuint, GLfloat, GLfloat, GLfloat, GLfloat); + typedef void (APIENTRY *_glActiveTexture) (GLenum); + + _glProgramStringARB glProgramStringARB; + _glBindProgramARB glBindProgramARB; + _glDeleteProgramsARB glDeleteProgramsARB; + _glGenProgramsARB glGenProgramsARB; + _glProgramLocalParameter4fARB glProgramLocalParameter4fARB; + _glActiveTexture glActiveTexture; + + bool m_checkedPrograms; + bool m_usingOpenGL; + GLuint m_program[2]; + GLuint m_texture[3]; +#endif + }; + + class VideoRendererSoftPin : public QMemInputPin + { + public: + VideoRendererSoftPin(VideoRendererSoftFilter *parent) : + QMemInputPin(parent, videoMediaTypes(), false /*no transformation of the samples*/), + m_renderer(parent) + { + } + + STDMETHODIMP EndOfStream() + { + m_renderer->endOfStream(); + return QMemInputPin::EndOfStream(); + } + + STDMETHODIMP ReceiveCanBlock() + { + //yes, it can block + return S_OK; + } + + STDMETHODIMP BeginFlush() + { + m_renderer->beginFlush(); + return QMemInputPin::BeginFlush(); + } + + STDMETHODIMP EndFlush() + { + m_renderer->endFlush(); + return QMemInputPin::EndFlush(); + } + + + STDMETHODIMP GetAllocatorRequirements(ALLOCATOR_PROPERTIES *prop) + { + if (!prop) { + return E_POINTER; + } + + //we need 2 buffers + prop->cBuffers = 2; + return S_OK; + } + + + STDMETHODIMP NotifyAllocator(IMemAllocator *alloc, BOOL readonly) + { + if (!alloc) { + return E_POINTER; + } + ALLOCATOR_PROPERTIES prop; + HRESULT hr = alloc->GetProperties(&prop); + if (SUCCEEDED(hr) && prop.cBuffers == 1) { + //we ask to get 2 buffers so that we don't block the flow + //when we addref the mediasample + prop.cBuffers = 2; + ALLOCATOR_PROPERTIES dummy; + alloc->SetProperties(&prop, &dummy); + } + + return QMemInputPin::NotifyAllocator(alloc, readonly); + } + + + + private: + VideoRendererSoftFilter * const m_renderer; + + }; + + VideoRendererSoftFilter::VideoRendererSoftFilter(VideoRendererSoft *renderer) : + QBaseFilter(CLSID_NULL), m_inputPin(new VideoRendererSoftPin(this)), + m_renderer(renderer), m_start(0) +#ifndef QT_NO_OPENGL + ,m_usingOpenGL(false), m_checkedPrograms(false), m_textureUploaded(false) +#endif + { + m_renderEvent = ::CreateEvent(0, 0, 0, 0); + m_receiveCanWait = ::CreateEvent(0, 0, 0, 0); + //simply initialize the array with default values + applyMixerSettings(0., 0., 0., 0.); +#ifndef QT_NO_OPENGL +#endif + } + + VideoRendererSoftFilter::~VideoRendererSoftFilter() + { + ::CloseHandle(m_renderEvent); + ::CloseHandle(m_receiveCanWait); + //this frees up resources + freeResources(); + } + + QSize VideoRendererSoftFilter::videoSize() const + { + QSize ret; + const AM_MEDIA_TYPE &mt = m_inputPin->connectedType(); + if (mt.pbFormat && mt.pbFormat) { + if (mt.formattype == FORMAT_VideoInfo) { + const VIDEOINFOHEADER *header = reinterpret_cast<VIDEOINFOHEADER*>(mt.pbFormat); + const int h = qAbs(header->bmiHeader.biHeight), + w = qAbs(header->bmiHeader.biWidth); + ret = QSize(w, h); + } else if (mt.formattype == FORMAT_VideoInfo2) { + const VIDEOINFOHEADER2 *header = reinterpret_cast<VIDEOINFOHEADER2*>(mt.pbFormat); + const int h = qAbs(header->bmiHeader.biHeight), + w = qAbs(header->bmiHeader.biWidth); + ret = QSize(w, h); + } + } + return ret; + } + + + HRESULT VideoRendererSoftFilter::processSample(IMediaSample *sample) + { +#ifdef FPS_COUNTER + if (fpsTime.elapsed() > 1000) { + qDebug("FPS_COUNTER: processed=%d, displayed=%d (%d)", nbFramesProcessed, nbFramesDisplayed, fpsTime.elapsed()); + nbFramesProcessed = 0; + nbFramesDisplayed = 0; + fpsTime.restart(); + + } +#endif + + AM_MEDIA_TYPE *type = 0; + if (sample->GetMediaType(&type) == S_OK) { + //let's update the media type of the input pin + m_inputPin->setConnectedType(*type); + } + + + const AM_MEDIA_TYPE &mt = m_inputPin->connectedType(); + + if (mt.pbFormat == 0 || mt.cbFormat == 0) { + return VFW_E_INVALIDMEDIATYPE; + } + + m_size = videoSize(); + if (!m_size.isValid()) { + return VFW_E_INVALIDMEDIATYPE; + } + +#ifdef FPS_COUNTER + nbFramesProcessed++; +#endif + + REFERENCE_TIME start = 0, stop = 0; + HRESULT hr = sample->GetTime(&start, &stop); + + ComPointer<IReferenceClock> clock; + GetSyncSource(clock.pparam()); + + const bool playing = SUCCEEDED(hr) && state() == State_Running && clock; + + if (playing) { + REFERENCE_TIME current; + clock->GetTime(¤t); + + DWORD_PTR advise; + + //let's synchronize here + clock->AdviseTime(m_start, start, + reinterpret_cast<HEVENT>(m_renderEvent), &advise); + + HANDLE handles[] = {m_receiveCanWait, m_renderEvent}; + if (::WaitForMultipleObjects(2, handles, false, INFINITE) == WAIT_OBJECT_0) { + if (state() != State_Stopped && !m_inputPin->isFlushing()) { + ::ResetEvent(m_receiveCanWait); + } + } + } + + + //the let's lock the sample to be used in the GUI thread + { + QMutexLocker locker(&m_mutex); + sample->AddRef(); + m_sampleBuffer = ComPointer<IMediaSample>(sample); + } + + //image is updated: we should update the widget + //we should never call directly members of target due to thread-safety + QApplication::postEvent(m_renderer, new QEvent(QEvent::UpdateRequest)); + + if (!playing) { + //useless to test the return value of WaitForSingleObject: timeout can't happen + ::WaitForSingleObject(m_receiveCanWait, INFINITE); + if (state() != State_Stopped && !m_inputPin->isFlushing()) { + ::ResetEvent(m_receiveCanWait); + } + } + + //everything should be ok + return S_OK; + } + +#ifndef QT_NO_OPENGL + bool VideoRendererSoftFilter::checkGLPrograms() + { + if (!m_checkedPrograms) { + m_checkedPrograms = true; + + glProgramStringARB = (_glProgramStringARB) wglGetProcAddress("glProgramStringARB"); + glBindProgramARB = (_glBindProgramARB) wglGetProcAddress("glBindProgramARB"); + glDeleteProgramsARB = (_glDeleteProgramsARB) wglGetProcAddress("glDeleteProgramsARB"); + glGenProgramsARB = (_glGenProgramsARB) wglGetProcAddress("glGenProgramsARB"); + glProgramLocalParameter4fARB = (_glProgramLocalParameter4fARB) wglGetProcAddress("glProgramLocalParameter4fARB"); + glActiveTexture = (_glActiveTexture) wglGetProcAddress("glActiveTexture"); + + //we check only once if the widget is drawn using opengl + if (glProgramStringARB && glBindProgramARB && glDeleteProgramsARB && + glGenProgramsARB && glActiveTexture && glProgramLocalParameter4fARB) { + glGenProgramsARB(2, m_program); + + const char *code[] = {yv12ToRgb, yuy2ToRgb}; + + bool error = false; + for(int i = 0; i < ProgramCount && !error; ++i) { + + glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_program[i]); + + const GLbyte *gl_src = reinterpret_cast<const GLbyte *>(code[i]); + glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, + strlen(code[i]), gl_src); + + if (glGetError() != GL_NO_ERROR) { + error = true; + } + } + + if (error) { + glDeleteProgramsARB(2, m_program); + } else { + //everything went fine we store the context here (we support YV12 and YUY2) + m_usingOpenGL = m_inputPin->connectedType().subtype == MEDIASUBTYPE_YV12 + || m_inputPin->connectedType().subtype == MEDIASUBTYPE_YUY2; + //those "textures" will be used as byte streams + //to pass Y, U and V data to the graphics card + glGenTextures(3, m_texture); + } + } + } + return m_usingOpenGL; + } + + void VideoRendererSoftFilter::updateTexture() + { + if (!m_sampleBuffer) { + return; //the texture is already up2date or their is no data yet + } + + uchar *data = 0; + m_sampleBuffer->GetPointer(&data); + + if (m_inputPin->connectedType().subtype == MEDIASUBTYPE_YV12) { + int w[3] = { m_size.width(), m_size.width()/2, m_size.width()/2 }; + int h[3] = { m_size.height(), m_size.height()/2, m_size.height()/2 }; + int offs[3] = { 0, m_size.width()*m_size.height(), m_size.width()*m_size.height()*5/4 }; + + for (int i = 0; i < 3; ++i) { + glBindTexture(GL_TEXTURE_2D, m_texture[i]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w[i], h[i], 0, + GL_LUMINANCE, GL_UNSIGNED_BYTE, data + offs[i]); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + } else { //m_inputPin->connectedType().subtype == MEDIASUBTYPE_YUY2 + //we upload 1 texture + glBindTexture(GL_TEXTURE_2D, m_texture[0]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_size.width() / 2, m_size.height(), 0, + GL_RGBA, GL_UNSIGNED_BYTE, data); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + } + m_sampleBuffer = ComPointer<IMediaSample>(); + m_textureUploaded = true; + } +#endif + + void VideoRendererSoftFilter::repaintCurrentFrame(QPainter &painter, const QRect &r) + { + QMutexLocker locker(&m_mutex); + +#ifdef FPS_COUNTER + nbFramesDisplayed++; +#endif + + +#ifndef QT_NO_OPENGL + if (painter.paintEngine() && painter.paintEngine()->type() == QPaintEngine::OpenGL && checkGLPrograms()) { + //for now we only support YUV (both YV12 and YUY2) + updateTexture(); + + if (!m_textureUploaded) { + //we simply fill the whole video with content + //the callee has already set the brush + painter.drawRect(r); + return; + } + + //let's draw the texture + + //Let's pass the other arguments + const Program prog = (m_inputPin->connectedType().subtype == MEDIASUBTYPE_YV12) ? YV12toRGB : YUY2toRGB; + glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_program[prog]); + //loading the parameters + glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 0, m_brightness / 256., m_contrast, qCos(m_hue), qSin(m_hue)); + glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 1, m_saturation, painter.opacity() /*alpha */, 0. /*dummy*/, 0. /*dummy*/); + + glEnable(GL_FRAGMENT_PROGRAM_ARB); + + const float v_array[] = { r.left(), r.top(), r.right()+1, r.top(), r.right()+1, r.bottom()+1, r.left(), r.bottom()+1 }; + + float tx_array[12] = {0., 0., 0., 1., + 0., 0., 1., 1., + 0., 0., 1., 0.}; + + if (prog == YUY2toRGB) { + const float w = m_size.width() / 2, + iw = 1. / w; + + tx_array[3] = w; + tx_array[6] = w; + + for (int i = 0; i < 4; ++i) { + tx_array[3*i + 2] = iw; + } + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_texture[0]); + + if (prog == YV12toRGB) { + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, m_texture[2]); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, m_texture[1]); + glActiveTexture(GL_TEXTURE0); + } + + + glVertexPointer(2, GL_FLOAT, 0, v_array); + glTexCoordPointer(3, GL_FLOAT, 0, tx_array); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glDrawArrays(GL_QUADS, 0, 4); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + glDisable(GL_FRAGMENT_PROGRAM_ARB); + return; + } else +#endif + if (m_sampleBuffer) { + //we need to get the sample data + uchar *data = 0; + m_sampleBuffer->GetPointer(&data); + + + //let's update the current image + if (m_inputPin->connectedType().subtype == MEDIASUBTYPE_YV12) { + convertYV12toRGB(data, m_size, m_currentImage, + m_brightness, m_contrast, m_hue, m_saturation); + } else if (m_inputPin->connectedType().subtype == MEDIASUBTYPE_YUY2) { + convertYUY2toRGB(data, m_size, m_currentImage, + m_brightness, m_contrast, m_hue, m_saturation); + } else if (m_inputPin->connectedType().subtype == MEDIASUBTYPE_RGB32) { + normalizeRGB(data, m_size, m_currentImage); + } + m_sampleBuffer = ComPointer<IMediaSample>(); + } + + if (m_currentImage.isNull()) { + //we simply fill the whole video with content + //the callee has alrtead set the brush + painter.drawRect(r); + } else { + painter.drawImage(0, 0, m_currentImage); + } + } + + + void VideoRendererSoftFilter::normalizeRGB(const uchar *data, const QSize &s, QImage &destImage) + { + const int w = s.width(), + h = s.height(); + if (destImage.size() != s) { + destImage = QImage(w, h, QImage::Format_ARGB32_Premultiplied); + } + if (destImage.isNull()) { + return; //the system can't allocate the memory for the image drawing + } + + const QRgb *rgb = reinterpret_cast<const QRgb*>(data); + + //this sets the alpha channel to 0xff and flip the image vertically + for (int y = h - 1; y >= 0; --y) { + QRgb *dest = reinterpret_cast<QRgb*>(destImage.scanLine(y)); + for(int i = w; i > 0; --i, ++rgb, ++dest) { + *dest = *rgb | (0xff << 24); //we force the alpha channel to 0xff + } + } + } + + + //we render data interpreted as YV12 into m_renderbuffer + void VideoRendererSoftFilter::convertYV12toRGB(const uchar *data, const QSize &s, QImage &destImage, + qreal brightness, qreal contrast, qreal hue, qreal saturation) + { + const int w = s.width(), + h = s.height(); + + //let's cache some computation + const int cosHx256 = qRound(qCos(hue) * contrast * saturation * 256), + sinHx256 = qRound(qSin(hue) * contrast * saturation * 256); + + int Yvalue[256]; + for(int i = 0;i<256;++i) { + Yvalue[i] = qRound(((i - 16) * contrast + brightness) * 298 + 128); + } + + + if (destImage.size() != s) { + destImage = QImage(w, h, QImage::Format_ARGB32_Premultiplied); + } + + if (destImage.isNull()) { + return; //the system can't allocate the memory for the image drawing + } + + QRgb *dest = reinterpret_cast<QRgb*>(destImage.bits()); + const uchar *dataY = data, + *dataV = data + (w*h), + *dataU = dataV + (w*h)/4; + + uint *line1 = dest, + *line2 = dest + w; + + for(int l = (h >> 1); l > 0; --l) { + //we treat 2 lines by 2 lines + + for(int x = (w >> 1); x > 0; --x) { + + const int u = *dataU++ - 128, + v = *dataV++ - 128; + const int d = (u * cosHx256 + v * sinHx256) >> 8, + e = (v * cosHx256 + u * sinHx256) >> 8; + + const int compRed = 409 * e, + compGreen = -100 * d - 208 * e, + compBlue = 516 * d; + + const int y21 = Yvalue[ dataY[w] ], + y11 = Yvalue[ *dataY++ ], + y22 = Yvalue[ dataY[w] ], + y12 = Yvalue[ *dataY++ ]; + + //1st line 1st pixel + *line1++ = CLIPPED_PIXEL(y11, compRed, compGreen, compBlue); + + //1st line, 2nd pixel + *line1++ = CLIPPED_PIXEL(y12, compRed, compGreen, compBlue); + + //2nd line 1st pixel + *line2++ = CLIPPED_PIXEL(y21, compRed, compGreen, compBlue); + + //2nd line 2nd pixel + *line2++ = CLIPPED_PIXEL(y22, compRed, compGreen, compBlue); + + } //for + + //end of the line + dataY += w; + line1 = line2; + line2 += w; + + } //for + + } + + //we render data interpreted as YUY2 into m_renderbuffer + void VideoRendererSoftFilter::convertYUY2toRGB(const uchar *data, const QSize &s, QImage &destImage, + qreal brightness, qreal contrast, qreal hue, qreal saturation) + { + const int w = s.width(), + h = s.height(); + + //let's cache some computation + int Yvalue[256]; + for(int i = 0;i<256;++i) { + Yvalue[i] = qRound(((i - 16) * contrast + brightness) * 298 + 128); + } + + const int cosHx256 = qRound(qCos(hue) * contrast * saturation * 256), + sinHx256 = qRound(qSin(hue) * contrast * saturation * 256); + + if (destImage.size() != s) { + //this will only allocate memory when needed + destImage = QImage(w, h, QImage::Format_ARGB32_Premultiplied); + } + if (destImage.isNull()) { + return; //the system can't allocate the memory for the image drawing + } + + QRgb *dest = reinterpret_cast<QRgb*>(destImage.bits()); + + //the number of iterations is width * height / 2 because we treat 2 pixels at each iterations + for (int c = w * h / 2; c > 0 ; --c) { + + //the idea of that algorithm comes from + //http://msdn2.microsoft.com/en-us/library/ms867704.aspx#yuvformats_identifying_yuv_formats_in_directshow + + //we treat 2 pixels by 2 pixels (we start reading 2 pixels info ie. "YUYV" + const int y1 = Yvalue[*data++], + u = *data++ - 128, + y2 = Yvalue[*data++], + v = *data++ - 128; + + const int d = (u * cosHx256 + v * sinHx256) >> 8, + e = (v * cosHx256 + u * sinHx256) >> 8; + + const int compRed = 409 * e, + compGreen = -100 * d - 208 * e, + compBlue = 516 * d; + + //first pixel + *dest++ = CLIPPED_PIXEL(y1, compRed, compGreen, compBlue); + + //second pixel + *dest++ = CLIPPED_PIXEL(y2, compRed, compGreen, compBlue); + } + } + + + VideoRendererSoft::VideoRendererSoft(QWidget *target) : + m_renderer(new VideoRendererSoftFilter(this)), m_target(target) + { + m_filter = Filter(m_renderer); + } + + VideoRendererSoft::~VideoRendererSoft() + { + } + + + bool VideoRendererSoft::isNative() const + { + return false; + } + + + void VideoRendererSoft::repaintCurrentFrame(QWidget *target, const QRect &rect) + { + QPainter painter(target); + + QColor backColor = target->palette().color(target->backgroundRole()); + painter.setBrush(backColor); + painter.setPen(Qt::NoPen); + if (!m_videoRect.contains(rect)) { + //we repaint the borders only when needed + const QVector<QRect> reg = (QRegion(rect) - m_videoRect).rects(); + for (int i = 0; i < reg.count(); ++i) { + painter.drawRect(reg.at(i)); + } + } + + painter.setRenderHint(QPainter::SmoothPixmapTransform); + painter.setTransform(m_transform, true); + QSize vsize = videoSize(); + m_renderer->repaintCurrentFrame(painter, QRect(0,0, vsize.width(), vsize.height())); + } + + void VideoRendererSoft::notifyResize(const QSize &size, + Phonon::VideoWidget::AspectRatio aspectRatio, Phonon::VideoWidget::ScaleMode scaleMode) + { + const QSize vsize = videoSize(); + internalNotifyResize(size, vsize, aspectRatio, scaleMode); + + m_transform.reset(); + + if (vsize.isValid() && size.isValid()) { + m_transform.translate(m_dstX, m_dstY); + const qreal sx = qreal(m_dstWidth) / qreal(vsize.width()), + sy = qreal(m_dstHeight) / qreal(vsize.height()); + m_transform.scale(sx, sy); + m_videoRect = m_transform.mapRect( QRect(0,0, vsize.width(), vsize.height())); + } + } + + QSize VideoRendererSoft::videoSize() const + { + if (m_renderer->pins().first()->connected()) { + return m_renderer->videoSize(); + } else { + return m_renderer->currentImage().size(); + } + } + + void VideoRendererSoft::applyMixerSettings(qreal brightness, qreal contrast, qreal hue, qreal saturation) + { + m_renderer->applyMixerSettings(brightness, contrast, hue, saturation); + } + + QImage VideoRendererSoft::snapshot() const + { + return m_renderer->currentImage(); //not accurate (especially when using opengl...) + } + + void VideoRendererSoft::setSnapshot(const QImage &image) + { + m_renderer->setCurrentImage(image); + } + + bool VideoRendererSoft::event(QEvent *e) + { + if (e->type() == QEvent::UpdateRequest) { + m_target->update(m_videoRect); + return true; + } + return QObject::event(e); + } + + + } +} + +QT_END_NAMESPACE + +#endif //QT_NO_PHONON_VIDEO diff --git a/src/3rdparty/phonon/ds9/videorenderer_soft.h b/src/3rdparty/phonon/ds9/videorenderer_soft.h new file mode 100644 index 0000000..e47bca6 --- /dev/null +++ b/src/3rdparty/phonon/ds9/videorenderer_soft.h @@ -0,0 +1,68 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + + +#ifndef PHONON_VIDEORENDERER_SOFT_H +#define PHONON_VIDEORENDERER_SOFT_H + +#include "abstractvideorenderer.h" + +QT_BEGIN_NAMESPACE +#ifndef QT_NO_PHONON_VIDEO + +namespace Phonon +{ + namespace DS9 + { + class VideoRendererSoftFilter; + //this class is used to render evrything in software (like in the Graphics View) + class VideoRendererSoft : public AbstractVideoRenderer, + public QObject //this is used to receive events + { + public: + VideoRendererSoft(QWidget *); + ~VideoRendererSoft(); + + //Implementation from AbstractVideoRenderer + void repaintCurrentFrame(QWidget *target, const QRect &rect); + void notifyResize(const QSize&, Phonon::VideoWidget::AspectRatio, Phonon::VideoWidget::ScaleMode); + QSize videoSize() const; + void applyMixerSettings(qreal brightness, qreal contrast, qreal hue, qreal saturation); + bool isNative() const; + + QImage snapshot() const; + void setSnapshot(const QImage &); + + protected: + bool event(QEvent *); + + private: + VideoRendererSoftFilter *m_renderer; + QTransform m_transform; + QRect m_videoRect; //rectangle where the video is displayed + QWidget *m_target; + }; + } +} + +#endif //QT_NO_PHONON_VIDEO + +QT_END_NAMESPACE + +#endif + + diff --git a/src/3rdparty/phonon/ds9/videorenderer_vmr9.cpp b/src/3rdparty/phonon/ds9/videorenderer_vmr9.cpp new file mode 100644 index 0000000..298e9fa --- /dev/null +++ b/src/3rdparty/phonon/ds9/videorenderer_vmr9.cpp @@ -0,0 +1,333 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + + +#include "videorenderer_vmr9.h" + +#ifndef QT_NO_PHONON_VIDEO + +#include <QtGui/QWidget> +#include <QtGui/QPainter> +#include <QtCore/QTimerEvent> + +#ifndef Q_OS_WINCE +#include <d3d9.h> +#include <vmr9.h> +#else +#include <uuids.h> +#endif + +QT_BEGIN_NAMESPACE + + +namespace Phonon +{ + namespace DS9 + { + VideoRendererVMR9::~VideoRendererVMR9() + { + } + + bool VideoRendererVMR9::isNative() const + { + return true; + } + + +#ifdef Q_OS_WINCE + VideoRendererVMR9::VideoRendererVMR9(QWidget *target) : m_target(target) + { + m_target->setAttribute(Qt::WA_PaintOnScreen, true); + m_filter = Filter(CLSID_VideoRenderer, IID_IBaseFilter); + } + + QSize VideoRendererVMR9::videoSize() const + { + LONG w = 0, + h = 0; + ComPointer<IBasicVideo> basic(m_filter, IID_IBasicVideo); + if (basic) { + basic->GetVideoSize( &w, &h); + } + return QSize(w, h); + } + + void VideoRendererVMR9::repaintCurrentFrame(QWidget * /*target*/, const QRect & /*rect*/) + { + //nothing to do here: the renderer paints everything + } + + void VideoRendererVMR9::notifyResize(const QSize &size, Phonon::VideoWidget::AspectRatio aspectRatio, + Phonon::VideoWidget::ScaleMode scaleMode) + { + if (!isActive()) { + ComPointer<IBasicVideo> basic(m_filter, IID_IBasicVideo); + if (basic) { + basic->SetDestinationPosition(0, 0, 0, 0); + } + return; + } + + ComPointer<IVideoWindow> video(m_filter, IID_IVideoWindow); + + OAHWND owner; + HRESULT hr = video->get_Owner(&owner); + if (FAILED(hr)) { + return; + } + + const OAHWND newOwner = reinterpret_cast<OAHWND>(m_target->winId()); + if (owner != newOwner) { + video->put_Owner(newOwner); + video->put_MessageDrain(newOwner); + video->put_WindowStyle(WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); + } + + //make sure the widget takes the whole size of the parent + video->SetWindowPosition(0, 0, size.width(), size.height()); + + const QSize vsize = videoSize(); + internalNotifyResize(size, vsize, aspectRatio, scaleMode); + + ComPointer<IBasicVideo> basic(m_filter, IID_IBasicVideo); + if (basic) { + basic->SetDestinationPosition(m_dstX, m_dstY, m_dstWidth, m_dstHeight); + } + } + + void VideoRendererVMR9::applyMixerSettings(qreal /*brightness*/, qreal /*contrast*/, qreal /*m_hue*/, qreal /*saturation*/) + { + //this can't be supported for WinCE + } + + QImage VideoRendererVMR9::snapshot() const + { + ComPointer<IBasicVideo> basic(m_filter, IID_IBasicVideo); + if (basic) { + LONG bufferSize = 0; + //1st we get the buffer size + basic->GetCurrentImage(&bufferSize, 0); + + QByteArray buffer; + buffer.resize(bufferSize); + HRESULT hr = basic->GetCurrentImage(&bufferSize, reinterpret_cast<long*>(buffer.data())); + + if (SUCCEEDED(hr)) { + + const BITMAPINFOHEADER *bmi = reinterpret_cast<const BITMAPINFOHEADER*>(buffer.constData()); + + const int w = qAbs(bmi->biWidth), + h = qAbs(bmi->biHeight); + + // Create image and copy data into image. + QImage ret(w, h, QImage::Format_RGB32); + + if (!ret.isNull()) { + const char *data = buffer.constData() + bmi->biSize; + const int bytes_per_line = w * sizeof(QRgb); + for (int y = h - 1; y >= 0; --y) { + qMemCopy(ret.scanLine(y), //destination + data, //source + bytes_per_line); + data += bytes_per_line; + } + } + return ret; + } + } + return QImage(); + } + +#else + VideoRendererVMR9::VideoRendererVMR9(QWidget *target) : m_target(target) + { + m_filter = Filter(CLSID_VideoMixingRenderer9, IID_IBaseFilter); + if (!m_filter) { + qWarning("the video widget could not be initialized correctly"); + return; + } + + ComPointer<IVMRFilterConfig9> config(m_filter, IID_IVMRFilterConfig9); + Q_ASSERT(config); + HRESULT hr = config->SetRenderingMode(VMR9Mode_Windowless); + Q_ASSERT(SUCCEEDED(hr)); + hr = config->SetNumberOfStreams(1); //for now we limit it to 1 input stream + Q_ASSERT(SUCCEEDED(hr)); + ComPointer<IVMRWindowlessControl9> windowlessControl(m_filter, IID_IVMRWindowlessControl9); + windowlessControl->SetVideoClippingWindow(reinterpret_cast<HWND>(target->winId())); + } + + QImage VideoRendererVMR9::snapshot() const + { + ComPointer<IVMRWindowlessControl9> windowlessControl(m_filter, IID_IVMRWindowlessControl9); + if (windowlessControl) { + BYTE *buffer = 0; + HRESULT hr = windowlessControl->GetCurrentImage(&buffer); + if (SUCCEEDED(hr)) { + + const BITMAPINFOHEADER *bmi = reinterpret_cast<BITMAPINFOHEADER*>(buffer); + const int w = qAbs(bmi->biWidth), + h = qAbs(bmi->biHeight); + + // Create image and copy data into image. + QImage ret(w, h, QImage::Format_RGB32); + + if (!ret.isNull()) { + uchar *data = buffer + bmi->biSize; + const int bytes_per_line = w * sizeof(QRgb); + for (int y = h - 1; y >= 0; --y) { + qMemCopy(ret.scanLine(y), //destination + data, //source + bytes_per_line); + data += bytes_per_line; + } + } + ::CoTaskMemFree(buffer); + return ret; + } + } + return QImage(); + } + + QSize VideoRendererVMR9::videoSize() const + { + LONG w = 0, + h = 0; + ComPointer<IVMRWindowlessControl9> windowlessControl(m_filter, IID_IVMRWindowlessControl9); + if (windowlessControl) { + windowlessControl->GetNativeVideoSize( &w, &h, 0, 0); + } + return QSize(w, h); + } + + void VideoRendererVMR9::repaintCurrentFrame(QWidget *target, const QRect &rect) + { + HDC hDC = target->getDC(); + // repaint the video + ComPointer<IVMRWindowlessControl9> windowlessControl(m_filter, IID_IVMRWindowlessControl9); + + HRESULT hr = windowlessControl ? windowlessControl->RepaintVideo(target->winId(), hDC) : E_POINTER; + if (FAILED(hr) || m_dstY > 0 || m_dstX > 0) { + const QColor c = target->palette().color(target->backgroundRole()); + COLORREF color = RGB(c.red(), c.green(), c.blue()); + HPEN hPen = ::CreatePen(PS_SOLID, 1, color); + HBRUSH hBrush = ::CreateSolidBrush(color); + ::SelectObject(hDC, hPen); + ::SelectObject(hDC, hBrush); + // repaint the video + if (FAILED(hr)) { + //black background : we use the Win32 API to avoid the ghost effect of the backing store + ::Rectangle(hDC, 0, 0, target->width(), target->height()); + } else { + if (m_dstY > 0) { + ::Rectangle(hDC, 0, 0, target->width(), m_dstY); //top + ::Rectangle(hDC, 0, target->height() - m_dstY, target->width(), target->height()); //bottom + } + if (m_dstX > 0) { + ::Rectangle(hDC, 0, m_dstY, m_dstX, m_dstHeight + m_dstY); //left + ::Rectangle(hDC, m_dstWidth + m_dstX, m_dstY, target->width(), m_dstHeight + m_dstY); //right + } + } + ::DeleteObject(hPen); + ::DeleteObject(hBrush); + } + target->releaseDC(hDC); + + } + + void VideoRendererVMR9::notifyResize(const QSize &size, Phonon::VideoWidget::AspectRatio aspectRatio, + Phonon::VideoWidget::ScaleMode scaleMode) + { + if (!isActive()) { + RECT dummyRect = { 0, 0, 0, 0}; + ComPointer<IVMRWindowlessControl9> windowlessControl(m_filter, IID_IVMRWindowlessControl9); + windowlessControl->SetVideoPosition(&dummyRect, &dummyRect); + return; + } + + + const QSize vsize = videoSize(); + internalNotifyResize(size, vsize, aspectRatio, scaleMode); + + RECT dstRectWin = { m_dstX, m_dstY, m_dstWidth + m_dstX, m_dstHeight + m_dstY}; + RECT srcRectWin = { 0, 0, vsize.width(), vsize.height()}; + + ComPointer<IVMRWindowlessControl9> windowlessControl(m_filter, IID_IVMRWindowlessControl9); + if (windowlessControl) { + windowlessControl->SetVideoPosition(&srcRectWin, &dstRectWin); + } + } + + void VideoRendererVMR9::applyMixerSettings(qreal brightness, qreal contrast, qreal hue, qreal saturation) + { + InputPin sink = BackendNode::pins(m_filter, PINDIR_INPUT).first(); + OutputPin source; + if (FAILED(sink->ConnectedTo(source.pparam()))) { + return; //it must be connected to work + } + + //get the mixer (used for brightness/contrast/saturation/hue) + ComPointer<IVMRMixerControl9> mixer(m_filter, IID_IVMRMixerControl9); + Q_ASSERT(mixer); + + VMR9ProcAmpControl ctrl; + ctrl.dwSize = sizeof(ctrl); + ctrl.dwFlags = ProcAmpControl9_Contrast | ProcAmpControl9_Brightness | ProcAmpControl9_Saturation | ProcAmpControl9_Hue; + VMR9ProcAmpControlRange range; + range.dwSize = sizeof(range); + + range.dwProperty = ProcAmpControl9_Contrast; + HRESULT hr = mixer->GetProcAmpControlRange(0, &range); + if (FAILED(hr)) { + return; + } + ctrl.Contrast = ((contrast < 0 ? range.MinValue : range.MaxValue) - range.DefaultValue) * qAbs(contrast) + range.DefaultValue; + + //brightness + range.dwProperty = ProcAmpControl9_Brightness; + hr = mixer->GetProcAmpControlRange(0, &range); + if (FAILED(hr)) { + return; + } + ctrl.Brightness = ((brightness < 0 ? range.MinValue : range.MaxValue) - range.DefaultValue) * qAbs(brightness) + range.DefaultValue; + + //saturation + range.dwProperty = ProcAmpControl9_Saturation; + hr = mixer->GetProcAmpControlRange(0, &range); + if (FAILED(hr)) { + return; + } + ctrl.Saturation = ((saturation < 0 ? range.MinValue : range.MaxValue) - range.DefaultValue) * qAbs(saturation) + range.DefaultValue; + + //hue + range.dwProperty = ProcAmpControl9_Hue; + hr = mixer->GetProcAmpControlRange(0, &range); + if (FAILED(hr)) { + return; + } + ctrl.Hue = ((hue < 0 ? range.MinValue : range.MaxValue) - range.DefaultValue) * qAbs(hue) + range.DefaultValue; + + //finally set the settings + mixer->SetProcAmpControl(0, &ctrl); + } +#endif + } +} + +QT_END_NAMESPACE + +#endif //QT_NO_PHONON_VIDEO diff --git a/src/3rdparty/phonon/ds9/videorenderer_vmr9.h b/src/3rdparty/phonon/ds9/videorenderer_vmr9.h new file mode 100644 index 0000000..4eb237e --- /dev/null +++ b/src/3rdparty/phonon/ds9/videorenderer_vmr9.h @@ -0,0 +1,56 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_VIDEORENDERER_VMR9_H +#define PHONON_VIDEORENDERER_VMR9_H + +#include "abstractvideorenderer.h" +#include "compointer.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_VIDEO + +namespace Phonon +{ + namespace DS9 + { + class VideoRendererVMR9 : public AbstractVideoRenderer + { + public: + VideoRendererVMR9(QWidget *target); + ~VideoRendererVMR9(); + + //Implementation from AbstractVideoRenderer + void repaintCurrentFrame(QWidget *target, const QRect &rect); + void notifyResize(const QSize&, Phonon::VideoWidget::AspectRatio, Phonon::VideoWidget::ScaleMode); + QSize videoSize() const; + QImage snapshot() const; + void applyMixerSettings(qreal brightness, qreal contrast, qreal m_hue, qreal saturation); + bool isNative() const; + private: + QWidget *m_target; + }; + } +} + +#endif //QT_NO_PHONON_VIDEO + +QT_END_NAMESPACE + +#endif + diff --git a/src/3rdparty/phonon/ds9/videowidget.cpp b/src/3rdparty/phonon/ds9/videowidget.cpp new file mode 100644 index 0000000..de7ce5f --- /dev/null +++ b/src/3rdparty/phonon/ds9/videowidget.cpp @@ -0,0 +1,397 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#include "videowidget.h" + +#include <QtGui/QPainter> +#include <QtGui/QPaintEvent> +#include <QtCore/QTimer> +#include <QtCore/QSettings> + +#include "mediaobject.h" + +#include "videorenderer_vmr9.h" +#include "videorenderer_soft.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_VIDEO + +namespace Phonon +{ + namespace DS9 + { + //class used internally to return the widget where the video is shown on + class VideoWindow : public QWidget + { + public: + explicit VideoWindow(QWidget *parent, VideoWidget *vw) + : QWidget(parent), m_node(vw), m_currentRenderer(0) + { + //default background color + setPalette(QPalette(Qt::black)); + setAttribute(Qt::WA_OpaquePaintEvent, true); + setAttribute(Qt::WA_NoSystemBackground, true); + setAttribute(Qt::WA_PaintOnScreen, true); + setAutoFillBackground(false); + } + + QPaintEngine* paintEngine() const + { + return 0; + } + + bool isEmbedded() const + { +#if QT_VERSION >= 0x040400 + return window()->testAttribute(Qt::WA_DontShowOnScreen); +#else + return false; +#endif + } + + bool needsSoftRendering() const + { + QPaintDevice *dev = QPainter::redirected(this, 0); + return (dev && dev != this); + } + + void resizeEvent(QResizeEvent *e) + { + m_node->updateVideoSize(); + QWidget::resizeEvent(e); + } + + AbstractVideoRenderer *currentRenderer() const + { + return m_currentRenderer; + } + + void setCurrentRenderer(AbstractVideoRenderer *renderer) + { + m_currentRenderer = renderer; + update(); + } + + QSize sizeHint() const + { + return m_currentRenderer->sizeHint().expandedTo(QWidget::sizeHint()); + } + + void changeEvent(QEvent *e) + { + checkCurrentRenderingMode(); + QWidget::changeEvent(e); + } + + void setVisible(bool visible) + { + checkCurrentRenderingMode(); + QWidget::setVisible(visible); + } + + void paintEvent(QPaintEvent *e) + { + checkCurrentRenderingMode(); + m_currentRenderer->repaintCurrentFrame(this, e->rect()); + } + + //this code manages the activation/deactivation of the screensaver + /*bool event(QEvent *e) + { + if (e->type() == QEvent::Resize) { + //we disable the screensaver if the video is in fullscreen mode + disableScreenSaver(window()->windowState() & Qt::WindowFullScreen); + } + return QWidget::event(e); + }*/ + + private: + //for fullscreen mode + void disableScreenSaver(bool b) + { + const QLatin1String screenSaverActive("ScreenSaveActive"); + QSettings settings( QLatin1String("HKEY_CURRENT_USER\\Control Panel\\Desktop"), QSettings::NativeFormat); + if (b) { + if (m_restoreScreenSaverActive.isNull()) { + //we store the value to be able to restore it later + m_restoreScreenSaverActive = settings.value(screenSaverActive); + settings.setValue(screenSaverActive, QString::number(!b)); + } + } else if (!m_restoreScreenSaverActive.isNull()) { + //we restore the previous value + settings.setValue(screenSaverActive, m_restoreScreenSaverActive); + } + } + + void checkCurrentRenderingMode() + { + if (!m_currentRenderer) + return; + + if (m_currentRenderer->isNative()) { + if (isEmbedded()) { + //we need to switch to software renderer + m_currentRenderer = m_node->switchRendering(m_currentRenderer); + setAttribute(Qt::WA_PaintOnScreen, false); + } else if (needsSoftRendering()) { + m_node->performSoftRendering(m_currentRenderer->snapshot()); + } + } else if (!isEmbedded()) { + m_currentRenderer = m_node->switchRendering(m_currentRenderer); + setAttribute(Qt::WA_PaintOnScreen, true); + } + } + + VideoWidget *m_node; + AbstractVideoRenderer *m_currentRenderer; + QVariant m_restoreScreenSaverActive; + }; + + VideoWidget::VideoWidget(QWidget *parent) + : BackendNode(parent), m_aspectRatio(Phonon::VideoWidget::AspectRatioAuto), + m_scaleMode(Phonon::VideoWidget::FitInView), + m_brightness(0.), m_contrast(0.), m_hue(0.), m_saturation(0.), m_noNativeRendererSupported(false) + + { + //initialisation of the widget + m_widget = new VideoWindow(parent, this); + + //initialization of the renderers + qMemSet(m_renderers, 0, sizeof(m_renderers)); + + for(int i = 0; i< FILTER_COUNT ;++i) { + //This might return a non native (ie Qt) renderer in case native is not supported + AbstractVideoRenderer *renderer = getRenderer(i, Native, true); + m_filters[i] = renderer->getFilter(); + } + + //by default, we take the first VideoWindow object + setCurrentGraph(0); + } + + VideoWidget::~VideoWidget() + { + for (int i = 0; i < 4; ++i) { + delete m_renderers[i]; + } + } + + void VideoWidget::notifyVideoLoaded() + { + updateVideoSize(); + m_widget->updateGeometry(); + } + + AbstractVideoRenderer *VideoWidget::switchRendering(AbstractVideoRenderer *current) + { + const bool toNative = !current->isNative(); + if (toNative && m_noNativeRendererSupported) + return current; //no switch here + + //firt we delete the renderer + //initialization of the widgets + for(int i = 0; i < FILTER_COUNT; ++i) { + Filter oldFilter = m_filters[i]; + + //Let's create a software renderer + AbstractVideoRenderer *renderer = getRenderer(i, toNative ? Native : NonNative, true); + + if (m_mediaObject) { + m_mediaObject->switchFilters(i, oldFilter, renderer->getFilter()); + } + + m_filters[i] = renderer->getFilter(); + } + + return getRenderer(mediaObject()->currentGraph()->index(), toNative ? Native: NonNative); + } + + void VideoWidget::performSoftRendering(const QImage ¤tImage) + { + const int graphIndex = mediaObject()->currentGraph()->index(); + VideoRendererSoft *r = static_cast<VideoRendererSoft*>(getRenderer(graphIndex, NonNative, true /*autocreation*/)); + r->setSnapshot(currentImage); + r->notifyResize(m_widget->size(), m_aspectRatio, m_scaleMode); + r->repaintCurrentFrame(m_widget, m_widget->rect()); + + } + + void VideoWidget::setCurrentGraph(int index) + { + for(int i = 0; i < 2; ++i) { + if (AbstractVideoRenderer *renderer = getRenderer(i, Native)) + renderer->setActive(index == i); + } + + //be sure to update all the things that needs an update + applyMixerSettings(); + updateVideoSize(); + + AbstractVideoRenderer *r = m_widget->currentRenderer(); + + //we determine dynamically if it is native or non native + r = getRenderer(index, !r || r->isNative() ? Native : NonNative); + if (!r) + r = getRenderer(index, NonNative); + m_widget->setCurrentRenderer(r); + } + + + Phonon::VideoWidget::AspectRatio VideoWidget::aspectRatio() const + { + return m_aspectRatio; + } + + void VideoWidget::setAspectRatio(Phonon::VideoWidget::AspectRatio aspectRatio) + { + m_aspectRatio = aspectRatio; + updateVideoSize(); + } + + Phonon::VideoWidget::ScaleMode VideoWidget::scaleMode() const + { + return m_scaleMode; + } + + + QWidget *VideoWidget::widget() + { + return m_widget; + } + + + void VideoWidget::setScaleMode(Phonon::VideoWidget::ScaleMode scaleMode) + { + m_scaleMode = scaleMode; + updateVideoSize(); + } + + void VideoWidget::setBrightness(qreal b) + { + m_brightness = b; + applyMixerSettings(); + } + + void VideoWidget::setContrast(qreal c) + { + m_contrast = c; + applyMixerSettings(); + } + + void VideoWidget::setHue(qreal h) + { + m_hue = h; + applyMixerSettings(); + } + + void VideoWidget::setSaturation(qreal s) + { + m_saturation = s; + applyMixerSettings(); + } + + qreal VideoWidget::brightness() const + { + return m_brightness; + } + + + qreal VideoWidget::contrast() const + { + return m_contrast; + } + + qreal VideoWidget::hue() const + { + return m_hue; + } + + qreal VideoWidget::saturation() const + { + return m_saturation; + } + + + AbstractVideoRenderer *VideoWidget::getRenderer(int graphIndex, RendererType type, bool autoCreate) + { + int index = graphIndex * 2 + type; + if (m_renderers[index] == 0 && autoCreate) { + AbstractVideoRenderer *renderer = 0; + if (type == Native) { + renderer = new VideoRendererVMR9(m_widget); + if (renderer->getFilter() == 0) { + //instanciating the renderer might fail with error VFW_E_DDRAW_CAPS_NOT_SUITABLE (0x80040273) + m_noNativeRendererSupported = true; + delete renderer; + renderer = 0; + } + } + + if (renderer == 0) { + type = NonNative; + index = graphIndex * 2 + type; + if (m_renderers[index] == 0) + renderer = new VideoRendererSoft(m_widget); //this always succeeds + else + renderer = m_renderers[index]; + } + + m_renderers[index] = renderer; + + //be sure to update all the things that needs an update + applyMixerSettings(); + updateVideoSize(); + + } + return m_renderers[index]; + } + + //this must be called whe nthe node is actually connected + void VideoWidget::applyMixerSettings() const + { + for (int i = 0; i < 4; ++i) { + if (AbstractVideoRenderer *renderer = m_renderers[i]) + renderer->applyMixerSettings(m_brightness, m_contrast, m_hue, m_saturation); + } + } + + void VideoWidget::connected(BackendNode *, const InputPin&) + { + //in case of a connection, we simply reapply the mixer settings + applyMixerSettings(); + updateVideoSize(); + } + + void VideoWidget::updateVideoSize() const + { + for (int i = 0; i < 4; ++i) { + if (AbstractVideoRenderer *renderer = m_renderers[i]) + renderer->notifyResize(m_widget->size(), m_aspectRatio, m_scaleMode); + } + } + + + + } +} + +#endif //QT_NO_PHONON_VIDEO + +QT_END_NAMESPACE + +#include "moc_videowidget.cpp" diff --git a/src/3rdparty/phonon/ds9/videowidget.h b/src/3rdparty/phonon/ds9/videowidget.h new file mode 100644 index 0000000..fc8a6e3 --- /dev/null +++ b/src/3rdparty/phonon/ds9/videowidget.h @@ -0,0 +1,96 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_VIDEOWIDGET_H +#define PHONON_VIDEOWIDGET_H + +#include <QtGui/QWidget> +#include <phonon/videowidgetinterface.h> +#include "backendnode.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_VIDEO + +namespace Phonon +{ + namespace DS9 + { + class VideoWindow; + class AbstractVideoRenderer; + + class VideoWidget : public BackendNode, public Phonon::VideoWidgetInterface + { + enum RendererType + { + Native = 0, + NonNative = 1 + }; + + Q_OBJECT + Q_INTERFACES(Phonon::VideoWidgetInterface) + public: + VideoWidget(QWidget *parent = 0); + ~VideoWidget(); + + Phonon::VideoWidget::AspectRatio aspectRatio() const; + void setAspectRatio(Phonon::VideoWidget::AspectRatio aspectRatio); + Phonon::VideoWidget::ScaleMode scaleMode() const; + void setScaleMode(Phonon::VideoWidget::ScaleMode); + qreal brightness() const; + void setBrightness(qreal); + qreal contrast() const; + void setContrast(qreal); + qreal hue() const; + void setHue(qreal); + qreal saturation() const; + void setSaturation(qreal); + + void setCurrentGraph(int index); + + QWidget *widget(); + + void notifyVideoLoaded(); + AbstractVideoRenderer *switchRendering(AbstractVideoRenderer *current); + void performSoftRendering(const QImage ¤tImage); + + //apply contrast/brightness/hue/saturation + void applyMixerSettings() const; + void updateVideoSize() const; + + protected: + virtual void connected(BackendNode *, const InputPin& inpin); + + private: + AbstractVideoRenderer *getRenderer(int graphIndex, RendererType type, bool autoCreate = false); + + Phonon::VideoWidget::AspectRatio m_aspectRatio; + Phonon::VideoWidget::ScaleMode m_scaleMode; + + VideoWindow *m_widget; + qreal m_brightness, m_contrast, m_hue, m_saturation; + AbstractVideoRenderer* m_renderers[4]; + mutable bool m_noNativeRendererSupported; + }; + } +} + +QT_END_NAMESPACE + +#endif // PHONON_VIDEOWIDGET_H + +#endif //QT_NO_PHONON_VIDEO diff --git a/src/3rdparty/phonon/ds9/volumeeffect.cpp b/src/3rdparty/phonon/ds9/volumeeffect.cpp new file mode 100644 index 0000000..2fd1afc --- /dev/null +++ b/src/3rdparty/phonon/ds9/volumeeffect.cpp @@ -0,0 +1,296 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#include "volumeeffect.h" +#include "qbasefilter.h" +#include "qmeminputpin.h" + +#include <QtCore/qmath.h> //for sqrt + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_VOLUMEFADEREFFECT + + +namespace Phonon +{ + namespace DS9 + { + /************************************************************************** + * curve functions + *************************************************************************/ + + static qreal curveValueFadeIn3dB(const qreal fadeStart, const qreal fadeDiff, const qreal completed) + { + return (fadeStart + fadeDiff * sqrt(completed)); + } + static qreal curveValueFadeOut3dB(const qreal fadeStart, const qreal fadeDiff, const qreal completed) + { + return (fadeStart + fadeDiff * (1.0 - sqrt(1.0 - completed))); + } + // in == out for a linear fade + static qreal curveValueFade6dB(const qreal fadeStart, const qreal fadeDiff, const qreal completed) + { + return (fadeStart + fadeDiff * completed); + } + static qreal curveValueFadeIn9dB(const qreal fadeStart, const qreal fadeDiff, const qreal completed) + { + return (fadeStart + fadeDiff * pow(completed, 1.5)); + } + static qreal curveValueFadeOut9dB(const qreal fadeStart, const qreal fadeDiff, const qreal completed) + { + return (fadeStart + fadeDiff * (1.0 - pow(1.0 - completed, 1.5))); + } + static qreal curveValueFadeIn12dB(const qreal fadeStart, const qreal fadeDiff, const qreal completed) + { + return (fadeStart + fadeDiff * completed * completed); + } + static qreal curveValueFadeOut12dB(const qreal fadeStart, const qreal fadeDiff, const qreal completed) + { + const qreal x = 1.0 - completed; + return (fadeStart + fadeDiff * (1.0 - x * x)); + } + + static const QVector<AM_MEDIA_TYPE> audioMediaType() + { + QVector<AM_MEDIA_TYPE> ret; + + AM_MEDIA_TYPE mt; + mt.majortype = MEDIATYPE_Audio; + mt.subtype = MEDIASUBTYPE_PCM; + mt.bFixedSizeSamples = 1; + mt.bTemporalCompression = 0; + mt.pUnk = 0; + mt.lSampleSize = 1; + mt.cbFormat = 0; + mt.pbFormat = 0; + mt.formattype = GUID_NULL; + ret << mt; + return ret; + } + + class VolumeMemInputPin : public QMemInputPin + { + public: + VolumeMemInputPin(QBaseFilter *parent, const QVector<AM_MEDIA_TYPE> &mt) : QMemInputPin(parent, mt, true /*transform*/) + { + } + + ~VolumeMemInputPin() + { + } + + STDMETHODIMP NotifyAllocator(IMemAllocator *alloc, BOOL b) + { + ALLOCATOR_PROPERTIES prop; + HRESULT hr = alloc->GetProperties(&prop); + if (SUCCEEDED(hr) && prop.cBuffers > 1) { + //this allows to reduce the latency for sound + //the problem is that too low numbers makes the whole thing fail... + ALLOCATOR_PROPERTIES actual; + prop.cBuffers = 1; + alloc->SetProperties(&prop, &actual); + } + return QMemInputPin::NotifyAllocator(alloc, b); + } + + }; + + class VolumeMemOutputPin : public QPin + { + public: + VolumeMemOutputPin(QBaseFilter *parent, const QVector<AM_MEDIA_TYPE> &mt) : QPin(parent, PINDIR_OUTPUT, mt) + { + } + + ~VolumeMemOutputPin() + { + } + + }; + + class VolumeEffectFilter : public QBaseFilter + { + public: + VolumeEffectFilter(VolumeEffect *); + + //reimplementation + virtual HRESULT processSample(IMediaSample *); + + private: + void treatOneSamplePerChannel(BYTE **buffer, int sampleSize, int channelCount, int frequency); + + QMemInputPin *m_input; + QPin *m_output; + VolumeEffect *m_volumeEffect; + }; + + VolumeEffectFilter::VolumeEffectFilter(VolumeEffect *ve) : QBaseFilter(CLSID_NULL), + m_volumeEffect(ve) + { + QVector<AM_MEDIA_TYPE> mt; + + //creating the output + m_output = new VolumeMemOutputPin(this, mt); + + //then creating the input + mt << audioMediaType(); + m_input = new VolumeMemInputPin(this, mt); + m_input->addOutput(m_output); //make the connection here + } + + void VolumeEffectFilter::treatOneSamplePerChannel(BYTE **buffer, int sampleSize, int channelCount, int frequency) + { + float volume = m_volumeEffect->volume(); + if (m_volumeEffect->m_fading) { + const qreal lastSample = m_volumeEffect->m_fadeDuration * frequency / 1000; + const qreal completed = qreal(m_volumeEffect->m_fadeSamplePosition++) / lastSample; + + if (qFuzzyCompare(completed, qreal(1.))) { + m_volumeEffect->setVolume(m_volumeEffect->m_targetVolume); + m_volumeEffect->m_fading = false; //we end the fading effect + } else { + volume = m_volumeEffect->m_fadeCurveFn(m_volumeEffect->m_initialVolume, + m_volumeEffect->m_targetVolume - m_volumeEffect->m_initialVolume, + completed); + } + } + + for(int c = 0; c < channelCount; ++c) { + switch (sampleSize) + { + case 2: + { + short *shortBuffer = reinterpret_cast<short*>(*buffer); + *shortBuffer *= qRound(volume); + } + break; + case 1: + **buffer *= qRound(volume); + break; + default: + break; + } + + *buffer += sampleSize; + } + } + + HRESULT VolumeEffectFilter::processSample(IMediaSample * ms) + { + BYTE *buffer = 0; + ms->GetPointer(&buffer); + if (buffer) { + const AM_MEDIA_TYPE &mt = m_output->connectedType(); + if (mt.formattype != FORMAT_WaveFormatEx) { + return VFW_E_INVALIDMEDIATYPE; + } + WAVEFORMATEX *format = reinterpret_cast<WAVEFORMATEX*>(mt.pbFormat); + const int channelCount = format->nChannels; + const int sampleSize = format->wBitsPerSample / 8; //...in bytes + + + const BYTE *end = buffer + ms->GetActualDataLength(); + while (buffer < end) { + treatOneSamplePerChannel(&buffer, sampleSize, channelCount, format->nSamplesPerSec); + } + } + + return S_OK; + } + + VolumeEffect::VolumeEffect(QObject *parent) : Effect(parent), + m_volume(1), m_fadeCurve(Phonon::VolumeFaderEffect::Fade3Decibel), + m_fading(false), m_initialVolume(0), m_targetVolume(0), m_fadeDuration(0), + m_fadeSamplePosition(0) + { + //creation of the effects + for(int i = 0; i < FILTER_COUNT; ++i) { + VolumeEffectFilter *f = new VolumeEffectFilter(this); + m_filters[i] = Filter(f); + } + } + + float VolumeEffect::volume() const + { + return m_volume; + } + + void VolumeEffect::setVolume(float newVolume) + { + m_volume = newVolume; + } + + Phonon::VolumeFaderEffect::FadeCurve VolumeEffect::fadeCurve() const + { + return m_fadeCurve; + } + + void VolumeEffect::setFadeCurve(Phonon::VolumeFaderEffect::FadeCurve curve) + { + m_fadeCurve = curve; + } + + + void VolumeEffect::fadeTo(float vol, int duration) + { + m_fading = true; //will be set back to false when fading is finished + m_targetVolume = vol; + m_fadeSamplePosition = 0; + m_initialVolume = m_volume; + m_fadeDuration = duration; + + //in or out? + const bool in = vol > m_volume; + + switch(m_fadeCurve) + { + case Phonon::VolumeFaderEffect::Fade6Decibel: + m_fadeCurveFn = curveValueFade6dB; + break; + case Phonon::VolumeFaderEffect::Fade9Decibel: + if (in) { + m_fadeCurveFn = curveValueFadeIn9dB; + } else { + m_fadeCurveFn = curveValueFadeOut9dB; + } + break; + case Phonon::VolumeFaderEffect::Fade12Decibel: + if (in) { + m_fadeCurveFn = curveValueFadeIn12dB; + } else { + m_fadeCurveFn = curveValueFadeOut12dB; + } + break; + case Phonon::VolumeFaderEffect::Fade3Decibel: + default: + if (in) { + m_fadeCurveFn = curveValueFadeIn3dB; + } else { + m_fadeCurveFn = curveValueFadeOut3dB; + } + break; + } + } + } +} + +#endif //QT_NO_PHONON_VOLUMEFADEREFFECT + +QT_END_NAMESPACE + +#include "moc_volumeeffect.cpp" diff --git a/src/3rdparty/phonon/ds9/volumeeffect.h b/src/3rdparty/phonon/ds9/volumeeffect.h new file mode 100644 index 0000000..d1b0186 --- /dev/null +++ b/src/3rdparty/phonon/ds9/volumeeffect.h @@ -0,0 +1,71 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_VOLUMEEFFECT_H +#define PHONON_VOLUMEEFFECT_H + +#include "effect.h" +#include <phonon/volumefaderinterface.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_VOLUMEFADEREFFECT + +namespace Phonon +{ + namespace DS9 + { + class VolumeEffectFilter; + class VolumeEffect : public Effect, public Phonon::VolumeFaderInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::VolumeFaderInterface) + public: + VolumeEffect(QObject *parent); + + //reimplementation + virtual float volume() const; + virtual void setVolume(float); + virtual Phonon::VolumeFaderEffect::FadeCurve fadeCurve() const; + virtual void setFadeCurve(Phonon::VolumeFaderEffect::FadeCurve); + virtual void fadeTo(float, int); + + private: + float m_volume; + + //paramaters used to fade + Phonon::VolumeFaderEffect::FadeCurve m_fadeCurve; + + bool m_fading; //determines if we should be fading. + float m_initialVolume; + float m_targetVolume; + int m_fadeDuration; + int m_fadeSamplePosition; + qreal (*m_fadeCurveFn)(const qreal, const qreal, const qreal); + + //allow the filter to get access to that + friend class VolumeEffectFilter; + + }; + } +} + +#endif //QT_NO_PHONON_VOLUMEFADEREFFECT + +QT_END_NAMESPACE + +#endif diff --git a/src/3rdparty/phonon/gstreamer/CMakeLists.txt b/src/3rdparty/phonon/gstreamer/CMakeLists.txt new file mode 100644 index 0000000..4946f5f --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/CMakeLists.txt @@ -0,0 +1,72 @@ +# Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +# Copyright (C) 2008 Matthias Kretz <kretz@kde.org> +# +# 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 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 <http://www.gnu.org/licenses/>. + +project(phonon-gstreamer) +include(ConfigureChecks.cmake) + +if (BUILD_PHONON_GSTREAMER) + include_directories(${GSTREAMER_INCLUDE_DIR} ${GLIB2_INCLUDE_DIR} + ${LIBXML2_INCLUDE_DIR}) + + set(phonon_gstreamer_SRCS + audiooutput.cpp + artssink.cpp + backend.cpp + devicemanager.cpp + effectmanager.cpp + gsthelper.cpp + mediaobject.cpp + medianode.cpp + effect.cpp + medianodeevent.cpp + videowidget.cpp + qwidgetvideosink.cpp + streamreader.cpp + phononsrc.cpp + message.cpp + audioeffect.cpp + abstractrenderer.cpp + x11renderer.cpp + widgetrenderer.cpp + glrenderer.cpp + volumefadereffect.cpp + ) + + find_package(Alsa) + if(ALSA_FOUND) + add_definitions(-DUSE_ALSASINK2) + include_directories(${ALSA_INCLUDES}) + set(phonon_gstreamer_SRCS + ${phonon_gstreamer_SRCS} + alsasink2.c + ) + endif(ALSA_FOUND) + + automoc4(phonon_gstreamer phonon_gstreamer_SRCS) + add_library(phonon_gstreamer SHARED ${phonon_gstreamer_SRCS}) + set_target_properties(phonon_gstreamer PROPERTIES PREFIX "") + target_link_libraries(phonon_gstreamer + ${QT_QTOPENGL_LIBRARY} + ${PHONON_LIBS} ${OPENGL_gl_LIBRARY} + ${GSTREAMER_LIBRARIES} ${GSTREAMER_BASE_LIBRARY} ${GSTREAMER_INTERFACE_LIBRARY} + ${GSTREAMER_PLUGIN_VIDEO_LIBRARIES} ${GSTREAMER_PLUGIN_AUDIO_LIBRARIES} + ${GLIB2_LIBRARIES} ${GOBJECT_LIBRARIES}) + if(ALSA_FOUND) + target_link_libraries(phonon_gstreamer ${ASOUND_LIBRARY}) + endif(ALSA_FOUND) + + install(TARGETS phonon_gstreamer DESTINATION ${PLUGIN_INSTALL_DIR}/plugins/phonon_backend) + install(FILES gstreamer.desktop DESTINATION ${SERVICES_INSTALL_DIR}/phononbackends) +endif (BUILD_PHONON_GSTREAMER) diff --git a/src/3rdparty/phonon/gstreamer/ConfigureChecks.cmake b/src/3rdparty/phonon/gstreamer/ConfigureChecks.cmake new file mode 100644 index 0000000..f2922e1 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/ConfigureChecks.cmake @@ -0,0 +1,37 @@ +# Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +# +# 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 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 <http://www.gnu.org/licenses/>. + +macro_optional_find_package(GStreamer) +macro_log_feature(GSTREAMER_FOUND "GStreamer" "gstreamer 0.10 is required for the multimedia backend" "http://gstreamer.freedesktop.org/modules/" FALSE "0.10") + +macro_optional_find_package(GStreamerPlugins) +macro_log_feature(GSTREAMER_PLUGIN_VIDEO_LIBRARIES "GStreamer video plugin" "The gstreamer video plugin (part of gstreamer-plugins-base 0.10) is required for the multimedia gstreamer backend" "http://gstreamer.freedesktop.org/modules/" FALSE "0.10") + +macro_optional_find_package(GLIB2) +macro_log_feature(GLIB2_FOUND "GLib2" "GLib 2 is required to compile the gstreamer backend for Phonon" "http://www.gtk.org/download/" FALSE) + +macro_optional_find_package(GObject) +# No log, since GObject is bundled with GLib + +macro_optional_find_package(LibXml2) +macro_log_feature(LIBXML2_FOUND "LibXml2" "LibXml2 is required to compile the gstreamer backend for Phonon" "http://xmlsoft.org/downloads.html" FALSE) + +macro_optional_find_package(OpenGL) +macro_log_feature(OPENGL_FOUND "OpenGL" "OpenGL support is required to compile the gstreamer backend for Phonon" "" FALSE) + +if (GSTREAMER_FOUND AND GSTREAMER_PLUGIN_VIDEO_LIBRARIES AND GLIB2_FOUND AND GOBJECT_FOUND AND LIBXML2_FOUND AND OPENGL_FOUND) + set(BUILD_PHONON_GSTREAMER TRUE) +else (GSTREAMER_FOUND AND GSTREAMER_PLUGIN_VIDEO_LIBRARIES AND GLIB2_FOUND AND GOBJECT_FOUND AND LIBXML2_FOUND AND OPENGL_FOUND) + set(BUILD_PHONON_GSTREAMER FALSE) +endif (GSTREAMER_FOUND AND GSTREAMER_PLUGIN_VIDEO_LIBRARIES AND GLIB2_FOUND AND GOBJECT_FOUND AND LIBXML2_FOUND AND OPENGL_FOUND) diff --git a/src/3rdparty/phonon/gstreamer/Messages.sh b/src/3rdparty/phonon/gstreamer/Messages.sh new file mode 100644 index 0000000..4fdf1ef --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/Messages.sh @@ -0,0 +1,5 @@ +#! /usr/bin/env bash +find ./ -maxdepth 1 -name "*.cpp" -print > files +find ./ -maxdepth 1 -name "*.h" -print >> files +$XGETTEXT_QT --copyright-holder=This_file_is_part_of_KDE --msgid-bugs-address=http://bugs.kde.org --files-from=files -o $podir/phonon_gstreamer.pot +rm files diff --git a/src/3rdparty/phonon/gstreamer/abstractrenderer.cpp b/src/3rdparty/phonon/gstreamer/abstractrenderer.cpp new file mode 100644 index 0000000..924b611 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/abstractrenderer.cpp @@ -0,0 +1,56 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "abstractrenderer.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ + + +AbstractRenderer::~AbstractRenderer() +{ + if (m_videoSink) { + gst_object_unref (GST_OBJECT (m_videoSink)); //Take ownership + m_videoSink = 0; + } +} + +void AbstractRenderer::aspectRatioChanged(Phonon::VideoWidget::AspectRatio aspectRatio) +{ + Q_UNUSED(aspectRatio); +} + +void AbstractRenderer::scaleModeChanged(Phonon::VideoWidget::ScaleMode scaleMode) +{ + Q_UNUSED(scaleMode); +} + +void AbstractRenderer::movieSizeChanged(const QSize &size) +{ + Q_UNUSED(size); +} + +} +} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE + + diff --git a/src/3rdparty/phonon/gstreamer/abstractrenderer.h b/src/3rdparty/phonon/gstreamer/abstractrenderer.h new file mode 100644 index 0000000..140413d --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/abstractrenderer.h @@ -0,0 +1,62 @@ +/* This file is part of the KDE project. + + Copyright (C) 2 //Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).007 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_GSTREAMER_ABSTRACTRENDERER_H +#define Phonon_GSTREAMER_ABSTRACTRENDERER_H + +#include "backend.h" +#include "common.h" +#include "medianode.h" +#include <phonon/videowidget.h> + +QT_BEGIN_NAMESPACE + +class QString; +namespace Phonon +{ +namespace Gstreamer +{ + +class VideoWidget; + +class AbstractRenderer +{ +public: + AbstractRenderer(VideoWidget *video) : + m_videoWidget(video) + , m_videoSink(0) { } + virtual ~AbstractRenderer(); + virtual GstElement *videoSink() {return m_videoSink;} + virtual void aspectRatioChanged(Phonon::VideoWidget::AspectRatio aspectRatio); + virtual void scaleModeChanged(Phonon::VideoWidget::ScaleMode scaleMode); + virtual void movieSizeChanged(const QSize &movieSize); + virtual void handleMediaNodeEvent(const MediaNodeEvent *event) = 0; + virtual bool eventFilter(QEvent *) = 0; + virtual void handlePaint(QPaintEvent *) {} + virtual bool paintsOnWidget() { return true; } // Controls overlays + +protected: + VideoWidget *m_videoWidget; + GstElement *m_videoSink; +}; + +} +} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE + +#endif // Phonon_GSTREAMER_ABSTRACTRENDERER_H diff --git a/src/3rdparty/phonon/gstreamer/alsasink2.c b/src/3rdparty/phonon/gstreamer/alsasink2.c new file mode 100644 index 0000000..4dcb140 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/alsasink2.c @@ -0,0 +1,1756 @@ +/* GStreamer + * Copyright (C) 2001 CodeFactory AB + * Copyright (C) 2001 Thomas Nyberg <thomas@codefactory.se> + * Copyright (C) 2001-2002 Andy Wingo <apwingo@eos.ncsu.edu> + * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de> + * Copyright (C) 2005 Wim Taymans <wim@fluendo.com> + * Copyright (C) 2005, 2006 Tim-Philipp Müller <tim centricular net> + * Copyright (C) 2008 Matthias Kretz <kretz@kde.org> + * + * gstalsasink2.c: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library 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 <http://www.gnu.org/licenses/>. + */ + +/** + * SECTION:element-alsasink2 + * @short_description: play audio to an ALSA device + * @see_also: alsasrc, alsamixer + * + * <refsect2> + * <para> + * This element renders raw audio samples using the ALSA api. + * </para> + * <title>Example pipelines</title> + * <para> + * Play an Ogg/Vorbis file. + * </para> + * <programlisting> + * gst-launch -v filesrc location=sine.ogg ! oggdemux ! vorbisdec ! audioconvert ! audioresample ! alsasink2 + * </programlisting> + * </refsect2> + * + * Last reviewed on 2006-03-01 (0.10.4) + */ + +#define _XOPEN_SOURCE 600 + +#include <sys/ioctl.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <string.h> +#include <getopt.h> +#include <alsa/asoundlib.h> + +#include "alsasink2.h" + +#include <gst/interfaces/propertyprobe.h> +#include <gst/audio/multichannel.h> + +#define _(text) (text) + +#define GST_CHECK_ALSA_VERSION(major,minor,micro) \ + (SND_LIB_MAJOR > (major) || \ + (SND_LIB_MAJOR == (major) && SND_LIB_MINOR > (minor)) || \ + (SND_LIB_MAJOR == (major) && SND_LIB_MINOR == (minor) && \ + SND_LIB_SUBMINOR >= (micro))) + +static const GList * +gst_alsa_device_property_probe_get_properties (GstPropertyProbe * probe) +{ + GObjectClass *klass = G_OBJECT_GET_CLASS (probe); + static GList *list = NULL; + + /* well, not perfect, but better than no locking at all. + * In the worst case we leak a list node, so who cares? */ + GST_CLASS_LOCK (GST_OBJECT_CLASS (klass)); + + if (!list) { + GParamSpec *pspec; + + pspec = g_object_class_find_property (klass, "device"); + list = g_list_append (NULL, pspec); + } + + GST_CLASS_UNLOCK (GST_OBJECT_CLASS (klass)); + + return list; +} + +static GList * +gst_alsa_get_device_list (snd_pcm_stream_t stream) +{ + snd_ctl_t *handle; + int card, err, dev; + snd_ctl_card_info_t *info; + snd_pcm_info_t *pcminfo; + gboolean mixer = (stream == ~0u); + GList *list = NULL; + + if (stream == ~0u) + stream = 0; + + snd_ctl_card_info_malloc (&info); + snd_pcm_info_malloc (&pcminfo); + card = -1; + + if (snd_card_next (&card) < 0 || card < 0) { + /* no soundcard found */ + return NULL; + } + + while (card >= 0) { + gchar name[32]; + + g_snprintf (name, sizeof (name), "hw:%d", card); + if ((err = snd_ctl_open (&handle, name, 0)) < 0) { + goto next_card; + } + if ((err = snd_ctl_card_info (handle, info)) < 0) { + snd_ctl_close (handle); + goto next_card; + } + + if (mixer) { + list = g_list_append (list, g_strdup (name)); + } else { + g_snprintf (name, sizeof (name), "default:CARD=%d", card); + list = g_list_append (list, g_strdup (name)); + dev = -1; + while (1) { + gchar *gst_device; + + snd_ctl_pcm_next_device (handle, &dev); + + if (dev < 0) + break; + snd_pcm_info_set_device (pcminfo, dev); + snd_pcm_info_set_subdevice (pcminfo, 0); + snd_pcm_info_set_stream (pcminfo, stream); + if ((err = snd_ctl_pcm_info (handle, pcminfo)) < 0) { + continue; + } + + gst_device = g_strdup_printf ("hw:%d,%d", card, dev); + list = g_list_append (list, gst_device); + } + } + snd_ctl_close (handle); + next_card: + if (snd_card_next (&card) < 0) { + break; + } + } + + snd_ctl_card_info_free (info); + snd_pcm_info_free (pcminfo); + + return list; +} + +static void +gst_alsa_device_property_probe_probe_property (GstPropertyProbe * probe, + guint prop_id, const GParamSpec * pspec) +{ + if (!g_str_equal (pspec->name, "device")) { + G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec); + } +} + +static gboolean +gst_alsa_device_property_probe_needs_probe (GstPropertyProbe * probe, + guint prop_id, const GParamSpec * pspec) +{ + /* don't cache probed data */ + return TRUE; +} + +static GValueArray * +gst_alsa_device_property_probe_get_values (GstPropertyProbe * probe, + guint prop_id, const GParamSpec * pspec) +{ + GstElementClass *klass; + const GList *templates; + snd_pcm_stream_t mode = -1; + GValueArray *array; + GValue value = { 0, }; + GList *l, *list; + + if (!g_str_equal (pspec->name, "device")) { + G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec); + return NULL; + } + + klass = GST_ELEMENT_GET_CLASS (GST_ELEMENT (probe)); + + /* I'm pretty sure ALSA has a good way to do this. However, their cool + * auto-generated documentation is pretty much useless if you try to + * do function-wise look-ups. */ + /* we assume one pad template at max [zero=mixer] */ + templates = gst_element_class_get_pad_template_list (klass); + if (templates) { + if (GST_PAD_TEMPLATE_DIRECTION (templates->data) == GST_PAD_SRC) + mode = SND_PCM_STREAM_CAPTURE; + else + mode = SND_PCM_STREAM_PLAYBACK; + } + + list = gst_alsa_get_device_list (mode); + + if (list == NULL) { + GST_LOG_OBJECT (probe, "No devices found"); + return NULL; + } + + array = g_value_array_new (g_list_length (list)); + g_value_init (&value, G_TYPE_STRING); + for (l = list; l != NULL; l = l->next) { + GST_LOG_OBJECT (probe, "Found device: %s", (gchar *) l->data); + g_value_take_string (&value, (gchar *) l->data); + l->data = NULL; + g_value_array_append (array, &value); + } + g_value_unset (&value); + g_list_free (list); + + return array; +} + +static void +gst_alsa_property_probe_interface_init (GstPropertyProbeInterface * iface) +{ + iface->get_properties = gst_alsa_device_property_probe_get_properties; + iface->probe_property = gst_alsa_device_property_probe_probe_property; + iface->needs_probe = gst_alsa_device_property_probe_needs_probe; + iface->get_values = gst_alsa_device_property_probe_get_values; +} + +static void +gst_alsa_type_add_device_property_probe_interface (GType type) +{ + static const GInterfaceInfo probe_iface_info = { + (GInterfaceInitFunc) gst_alsa_property_probe_interface_init, + NULL, + NULL, + }; + + g_type_add_interface_static (type, GST_TYPE_PROPERTY_PROBE, + &probe_iface_info); +} + +static GstCaps * +gst_alsa_detect_rates (GstObject * obj, snd_pcm_hw_params_t * hw_params, + GstCaps * in_caps) +{ + GstCaps *caps; + guint min, max; + gint err, dir, min_rate, max_rate; + guint i; + + GST_LOG_OBJECT (obj, "probing sample rates ..."); + + if ((err = snd_pcm_hw_params_get_rate_min (hw_params, &min, &dir)) < 0) + goto min_rate_err; + + if ((err = snd_pcm_hw_params_get_rate_max (hw_params, &max, &dir)) < 0) + goto max_rate_err; + + min_rate = min; + max_rate = max; + + if (min_rate < 4000) + min_rate = 4000; /* random 'sensible minimum' */ + + if (max_rate <= 0) + max_rate = G_MAXINT; /* or maybe just use 192400 or so? */ + else if (max_rate > 0 && max_rate < 4000) + max_rate = MAX (4000, min_rate); + + GST_DEBUG_OBJECT (obj, "Min. rate = %u (%d)", min_rate, min); + GST_DEBUG_OBJECT (obj, "Max. rate = %u (%d)", max_rate, max); + + caps = gst_caps_make_writable (in_caps); + + for (i = 0; i < gst_caps_get_size (caps); ++i) { + GstStructure *s; + + s = gst_caps_get_structure (caps, i); + if (min_rate == max_rate) { + gst_structure_set (s, "rate", G_TYPE_INT, min_rate, NULL); + } else { + gst_structure_set (s, "rate", GST_TYPE_INT_RANGE, + min_rate, max_rate, NULL); + } + } + + return caps; + + /* ERRORS */ +min_rate_err: + { + GST_ERROR_OBJECT (obj, "failed to query minimum sample rate: %s", + snd_strerror (err)); + gst_caps_unref (in_caps); + return NULL; + } +max_rate_err: + { + GST_ERROR_OBJECT (obj, "failed to query maximum sample rate: %s", + snd_strerror (err)); + gst_caps_unref (in_caps); + return NULL; + } +} + +static const struct +{ + const int width; + const int depth; + const int sformat; + const int uformat; +} pcmformats[] = { + { + 8, 8, SND_PCM_FORMAT_S8, SND_PCM_FORMAT_U8}, { + 16, 16, SND_PCM_FORMAT_S16, SND_PCM_FORMAT_U16}, { + 32, 24, SND_PCM_FORMAT_S24, SND_PCM_FORMAT_U24}, { +#if (G_BYTE_ORDER == G_LITTLE_ENDIAN) /* no endian-unspecific enum available */ + 24, 24, SND_PCM_FORMAT_S24_3LE, SND_PCM_FORMAT_U24_3LE}, { +#else + 24, 24, SND_PCM_FORMAT_S24_3BE, SND_PCM_FORMAT_U24_3BE}, { +#endif + 32, 32, SND_PCM_FORMAT_S32, SND_PCM_FORMAT_U32} +}; + +static GstCaps * +gst_alsa_detect_formats (GstObject * obj, snd_pcm_hw_params_t * hw_params, + GstCaps * in_caps) +{ + snd_pcm_format_mask_t *mask; + GstStructure *s; + GstCaps *caps; + guint i; + + snd_pcm_format_mask_malloc (&mask); + snd_pcm_hw_params_get_format_mask (hw_params, mask); + + caps = gst_caps_new_empty (); + + for (i = 0; i < gst_caps_get_size (in_caps); ++i) { + GstStructure *scopy; + guint w; + gint width = 0, depth = 0; + + s = gst_caps_get_structure (in_caps, i); + if (!gst_structure_has_name (s, "audio/x-raw-int")) { + GST_WARNING_OBJECT (obj, "skipping non-int format"); + continue; + } + if (!gst_structure_get_int (s, "width", &width) || + !gst_structure_get_int (s, "depth", &depth)) + continue; + if (width == 0 || (width % 8) != 0) + continue; /* Only full byte widths are valid */ + for (w = 0; w < G_N_ELEMENTS (pcmformats); w++) + if (pcmformats[w].width == width && pcmformats[w].depth == depth) + break; + if (w == G_N_ELEMENTS (pcmformats)) + continue; /* Unknown format */ + + if (snd_pcm_format_mask_test (mask, pcmformats[w].sformat) && + snd_pcm_format_mask_test (mask, pcmformats[w].uformat)) { + /* template contains { true, false } or just one, leave it as it is */ + scopy = gst_structure_copy (s); + } else if (snd_pcm_format_mask_test (mask, pcmformats[w].sformat)) { + scopy = gst_structure_copy (s); + gst_structure_set (scopy, "signed", G_TYPE_BOOLEAN, TRUE, NULL); + } else if (snd_pcm_format_mask_test (mask, pcmformats[w].uformat)) { + scopy = gst_structure_copy (s); + gst_structure_set (scopy, "signed", G_TYPE_BOOLEAN, FALSE, NULL); + } else { + scopy = NULL; + } + if (scopy) { + if (width > 8) { + /* TODO: proper endianness detection, for now it's CPU endianness only */ + gst_structure_set (scopy, "endianness", G_TYPE_INT, G_BYTE_ORDER, NULL); + } + gst_caps_append_structure (caps, scopy); + } + } + + snd_pcm_format_mask_free (mask); + gst_caps_unref (in_caps); + return caps; +} + +/* we don't have channel mappings for more than this many channels */ +#define GST_ALSA_MAX_CHANNELS 8 + +static GstStructure * +get_channel_free_structure (const GstStructure * in_structure) +{ + GstStructure *s = gst_structure_copy (in_structure); + + gst_structure_remove_field (s, "channels"); + return s; +} + +static void +caps_add_channel_configuration (GstCaps * caps, + const GstStructure * in_structure, gint min_chans, gint max_chans) +{ + GstAudioChannelPosition pos[8] = { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_LFE, + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT + }; + GstStructure *s = NULL; + gint c; + + if (min_chans == max_chans && max_chans <= 2) { + s = get_channel_free_structure (in_structure); + gst_structure_set (s, "channels", G_TYPE_INT, max_chans, NULL); + gst_caps_append_structure (caps, s); + return; + } + + g_assert (min_chans >= 1); + + /* mono and stereo don't need channel configurations */ + if (min_chans == 2) { + s = get_channel_free_structure (in_structure); + gst_structure_set (s, "channels", G_TYPE_INT, 2, NULL); + gst_caps_append_structure (caps, s); + } else if (min_chans == 1 && max_chans >= 2) { + s = get_channel_free_structure (in_structure); + gst_structure_set (s, "channels", GST_TYPE_INT_RANGE, 1, 2, NULL); + gst_caps_append_structure (caps, s); + } + + /* don't know whether to use 2.1 or 3.0 here - but I suspect + * alsa might work around that/fix it somehow. Can we tell alsa + * what our channel layout is like? */ + if (max_chans >= 3 && min_chans <= 3) { + GstAudioChannelPosition pos_21[3] = { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_LFE + }; + + s = get_channel_free_structure (in_structure); + gst_structure_set (s, "channels", G_TYPE_INT, 3, NULL); + gst_audio_set_channel_positions (s, pos_21); + gst_caps_append_structure (caps, s); + } + + /* everything else (4, 6, 8 channels) needs a channel layout */ + for (c = MAX (4, min_chans); c <= 8; c += 2) { + if (max_chans >= c) { + s = get_channel_free_structure (in_structure); + gst_structure_set (s, "channels", G_TYPE_INT, c, NULL); + gst_audio_set_channel_positions (s, pos); + gst_caps_append_structure (caps, s); + } + } + + for (c = MAX (9, min_chans); c <= max_chans; ++c) { + GstAudioChannelPosition *ch_layout; + gint i; + + ch_layout = g_new (GstAudioChannelPosition, c); + for (i = 0; i < c; ++i) { + ch_layout[i] = GST_AUDIO_CHANNEL_POSITION_NONE; + } + s = get_channel_free_structure (in_structure); + gst_structure_set (s, "channels", G_TYPE_INT, c, NULL); + gst_audio_set_channel_positions (s, ch_layout); + gst_caps_append_structure (caps, s); + g_free (ch_layout); + } +} + +static GstCaps * +gst_alsa_detect_channels (GstObject * obj, snd_pcm_hw_params_t * hw_params, + GstCaps * in_caps) +{ + GstCaps *caps; + guint min, max; + gint min_chans, max_chans; + gint err; + guint i; + + GST_LOG_OBJECT (obj, "probing channels ..."); + + if ((err = snd_pcm_hw_params_get_channels_min (hw_params, &min)) < 0) + goto min_chan_error; + + if ((err = snd_pcm_hw_params_get_channels_max (hw_params, &max)) < 0) + goto max_chan_error; + + /* note: the above functions may return (guint) -1 */ + min_chans = min; + max_chans = max; + + if (min_chans < 0) { + min_chans = 1; + max_chans = GST_ALSA_MAX_CHANNELS; + } else if (max_chans < 0) { + max_chans = GST_ALSA_MAX_CHANNELS; + } + + if (min_chans > max_chans) { + gint temp; + + GST_WARNING_OBJECT (obj, "minimum channels > maximum channels (%d > %d), " + "please fix your soundcard drivers", min, max); + temp = min_chans; + min_chans = max_chans; + max_chans = temp; + } + + /* pro cards seem to return large numbers for min_channels */ + if (min_chans > GST_ALSA_MAX_CHANNELS) { + GST_DEBUG_OBJECT (obj, "min_chans = %u, looks like a pro card", min_chans); + if (max_chans < min_chans) { + max_chans = min_chans; + } else { + /* only support [max_chans; max_chans] for these cards for now + * to avoid inflating the source caps with loads of structures ... */ + min_chans = max_chans; + } + } else { + min_chans = MAX (min_chans, 1); + max_chans = MIN (GST_ALSA_MAX_CHANNELS, max_chans); + } + + GST_DEBUG_OBJECT (obj, "Min. channels = %d (%d)", min_chans, min); + GST_DEBUG_OBJECT (obj, "Max. channels = %d (%d)", max_chans, max); + + caps = gst_caps_new_empty (); + + for (i = 0; i < gst_caps_get_size (in_caps); ++i) { + GstStructure *s; + GType field_type; + gint c_min = min_chans; + gint c_max = max_chans; + + s = gst_caps_get_structure (in_caps, i); + /* the template caps might limit the number of channels (like alsasrc), + * in which case we don't want to return a superset, so hack around this + * for the two common cases where the channels are either a fixed number + * or a min/max range). Example: alsasrc template has channels = [1,2] and + * the detection will claim to support 8 channels for device 'plughw:0' */ + field_type = gst_structure_get_field_type (s, "channels"); + if (field_type == G_TYPE_INT) { + gst_structure_get_int (s, "channels", &c_min); + gst_structure_get_int (s, "channels", &c_max); + } else if (field_type == GST_TYPE_INT_RANGE) { + const GValue *val; + + val = gst_structure_get_value (s, "channels"); + c_min = CLAMP (gst_value_get_int_range_min (val), min_chans, max_chans); + c_max = CLAMP (gst_value_get_int_range_max (val), min_chans, max_chans); + } else { + c_min = min_chans; + c_max = max_chans; + } + + caps_add_channel_configuration (caps, s, c_min, c_max); + } + + gst_caps_unref (in_caps); + + return caps; + + /* ERRORS */ +min_chan_error: + { + GST_ERROR_OBJECT (obj, "failed to query minimum channel count: %s", + snd_strerror (err)); + return NULL; + } +max_chan_error: + { + GST_ERROR_OBJECT (obj, "failed to query maximum channel count: %s", + snd_strerror (err)); + return NULL; + } +} + +#ifndef GST_CHECK_VERSION +#define GST_CHECK_VERSION(major,minor,micro) \ + (GST_VERSION_MAJOR > (major) || \ + (GST_VERSION_MAJOR == (major) && GST_VERSION_MINOR > (minor)) || \ + (GST_VERSION_MAJOR == (major) && GST_VERSION_MINOR == (minor) && GST_VERSION_MICRO >= (micro))) +#endif + +#if GST_CHECK_VERSION(0, 10, 18) +snd_pcm_t * +gst_alsa_open_iec958_pcm (GstObject * obj) +{ + char *iec958_pcm_name = NULL; + snd_pcm_t *pcm = NULL; + int res; + char devstr[256]; /* Storage for local 'default' device string */ + + /* + * Try and open our default iec958 device. Fall back to searching on card x + * if this fails, which should only happen on older alsa setups + */ + + /* The string will be one of these: + * SPDIF_CON: Non-audio flag not set: + * spdif:{AES0 0x0 AES1 0x82 AES2 0x0 AES3 0x2} + * SPDIF_CON: Non-audio flag set: + * spdif:{AES0 0x2 AES1 0x82 AES2 0x0 AES3 0x2} + */ + sprintf (devstr, + "iec958:{AES0 0x%02x AES1 0x%02x AES2 0x%02x AES3 0x%02x}", + IEC958_AES0_CON_EMPHASIS_NONE | IEC958_AES0_NONAUDIO, + IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER, + 0, IEC958_AES3_CON_FS_48000); + + GST_DEBUG_OBJECT (obj, "Generated device string \"%s\"", devstr); + iec958_pcm_name = devstr; + + res = snd_pcm_open (&pcm, iec958_pcm_name, SND_PCM_STREAM_PLAYBACK, 0); + if (G_UNLIKELY (res < 0)) { + GST_DEBUG_OBJECT (obj, "failed opening IEC958 device: %s", + snd_strerror (res)); + pcm = NULL; + } + + return pcm; +} +#endif + + +/* + * gst_alsa_probe_supported_formats: + * + * Takes the template caps and returns the subset which is actually + * supported by this device. + * + */ + +GstCaps * +gst_alsa_probe_supported_formats (GstObject * obj, snd_pcm_t * handle, + const GstCaps * template_caps) +{ + snd_pcm_hw_params_t *hw_params; + snd_pcm_stream_t stream_type; + GstCaps *caps; + gint err; + + snd_pcm_hw_params_malloc (&hw_params); + if ((err = snd_pcm_hw_params_any (handle, hw_params)) < 0) + goto error; + + stream_type = snd_pcm_stream (handle); + + caps = gst_caps_copy (template_caps); + + if (!(caps = gst_alsa_detect_formats (obj, hw_params, caps))) + goto subroutine_error; + + if (!(caps = gst_alsa_detect_rates (obj, hw_params, caps))) + goto subroutine_error; + + if (!(caps = gst_alsa_detect_channels (obj, hw_params, caps))) + goto subroutine_error; + +#if GST_CHECK_VERSION(0, 10, 18) + /* Try opening IEC958 device to see if we can support that format (playback + * only for now but we could add SPDIF capture later) */ + if (stream_type == SND_PCM_STREAM_PLAYBACK) { + snd_pcm_t *pcm = gst_alsa_open_iec958_pcm (obj); + + if (G_LIKELY (pcm)) { + gst_caps_append (caps, gst_caps_new_simple ("audio/x-iec958", NULL)); + snd_pcm_close (pcm); + } + } +#endif + + snd_pcm_hw_params_free (hw_params); + return caps; + + /* ERRORS */ +error: + { + GST_ERROR_OBJECT (obj, "failed to query formats: %s", snd_strerror (err)); + snd_pcm_hw_params_free (hw_params); + return NULL; + } +subroutine_error: + { + GST_ERROR_OBJECT (obj, "failed to query formats"); + snd_pcm_hw_params_free (hw_params); + return NULL; + } +} + +static gchar * +gst_alsa_find_device_name_no_handle (GstObject * obj, const gchar * devcard, + gint device_num, snd_pcm_stream_t stream) +{ + snd_ctl_card_info_t *info = NULL; + snd_ctl_t *ctl = NULL; + gchar *ret = NULL; + gint dev = -1; + + GST_LOG_OBJECT (obj, "[%s] device=%d", devcard, device_num); + + if (snd_ctl_open (&ctl, devcard, 0) < 0) + return NULL; + + snd_ctl_card_info_malloc (&info); + if (snd_ctl_card_info (ctl, info) < 0) + goto done; + + while (snd_ctl_pcm_next_device (ctl, &dev) == 0 && dev >= 0) { + if (dev == device_num) { + snd_pcm_info_t *pcminfo; + + snd_pcm_info_malloc (&pcminfo); + snd_pcm_info_set_device (pcminfo, dev); + snd_pcm_info_set_subdevice (pcminfo, 0); + snd_pcm_info_set_stream (pcminfo, stream); + if (snd_ctl_pcm_info (ctl, pcminfo) < 0) { + snd_pcm_info_free (pcminfo); + break; + } + + ret = g_strdup (snd_pcm_info_get_name (pcminfo)); + snd_pcm_info_free (pcminfo); + GST_LOG_OBJECT (obj, "name from pcminfo: %s", GST_STR_NULL (ret)); + } + } + + if (ret == NULL) { + char *name = NULL; + gint card; + + GST_LOG_OBJECT (obj, "no luck so far, trying backup"); + card = snd_ctl_card_info_get_card (info); + snd_card_get_name (card, &name); + ret = g_strdup (name); + free (name); + } + +done: + snd_ctl_card_info_free (info); + snd_ctl_close (ctl); + + return ret; +} + +gchar * +gst_alsa_find_device_name (GstObject * obj, const gchar * device, + snd_pcm_t * handle, snd_pcm_stream_t stream) +{ + gchar *ret = NULL; + + if (device != NULL) { + gchar *dev, *comma; + gint devnum; + + GST_LOG_OBJECT (obj, "Trying to get device name from string '%s'", device); + + /* only want name:card bit, but not devices and subdevices */ + dev = g_strdup (device); + if ((comma = strchr (dev, ','))) { + *comma = '\0'; + devnum = atoi (comma + 1); + ret = gst_alsa_find_device_name_no_handle (obj, dev, devnum, stream); + } + g_free (dev); + } + + if (ret == NULL && handle != NULL) { + snd_pcm_info_t *info; + + GST_LOG_OBJECT (obj, "Trying to get device name from open handle"); + snd_pcm_info_malloc (&info); + snd_pcm_info (handle, info); + ret = g_strdup (snd_pcm_info_get_name (info)); + snd_pcm_info_free (info); + } + + GST_LOG_OBJECT (obj, "Device name for device '%s': %s", + GST_STR_NULL (device), GST_STR_NULL (ret)); + + return ret; +} + +/* elementfactory information */ +static const GstElementDetails gst_alsasink2_details = +GST_ELEMENT_DETAILS ("Audio sink (ALSA)", + "Sink/Audio", + "Output to a sound card via ALSA", + "Wim Taymans <wim@fluendo.com>"); + +#define DEFAULT_DEVICE "default" +#define DEFAULT_DEVICE_NAME "" +#define SPDIF_PERIOD_SIZE 1536 +#define SPDIF_BUFFER_SIZE 15360 + +enum +{ + PROP_0, + PROP_DEVICE, + PROP_DEVICE_NAME +}; + +static void gst_alsasink2_init_interfaces (GType type); + +GST_BOILERPLATE_FULL (_k_GstAlsaSink, gst_alsasink2, GstAudioSink, + GST_TYPE_AUDIO_SINK, gst_alsasink2_init_interfaces); + +static void gst_alsasink2_finalise (GObject * object); +static void gst_alsasink2_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_alsasink2_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +static GstCaps *gst_alsasink2_getcaps (GstBaseSink * bsink); + +static gboolean gst_alsasink2_open (GstAudioSink * asink); +static gboolean gst_alsasink2_prepare (GstAudioSink * asink, + GstRingBufferSpec * spec); +static gboolean gst_alsasink2_unprepare (GstAudioSink * asink); +static gboolean gst_alsasink2_close (GstAudioSink * asink); +static guint gst_alsasink2_write (GstAudioSink * asink, gpointer data, + guint length); +static guint gst_alsasink2_delay (GstAudioSink * asink); +static void gst_alsasink2_reset (GstAudioSink * asink); + +static gint output_ref; /* 0 */ +static snd_output_t *output; /* NULL */ +static GStaticMutex output_mutex = G_STATIC_MUTEX_INIT; + + +#if (G_BYTE_ORDER == G_LITTLE_ENDIAN) +# define ALSA_SINK2_FACTORY_ENDIANNESS "LITTLE_ENDIAN, BIG_ENDIAN" +#else +# define ALSA_SINK2_FACTORY_ENDIANNESS "BIG_ENDIAN, LITTLE_ENDIAN" +#endif + +static GstStaticPadTemplate alsasink2_sink_factory = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-int, " + "endianness = (int) { " ALSA_SINK2_FACTORY_ENDIANNESS " }, " + "signed = (boolean) { TRUE, FALSE }, " + "width = (int) 32, " + "depth = (int) 32, " + "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]; " + "audio/x-raw-int, " + "endianness = (int) { " ALSA_SINK2_FACTORY_ENDIANNESS " }, " + "signed = (boolean) { TRUE, FALSE }, " + "width = (int) 24, " + "depth = (int) 24, " + "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]; " + "audio/x-raw-int, " + "endianness = (int) { " ALSA_SINK2_FACTORY_ENDIANNESS " }, " + "signed = (boolean) { TRUE, FALSE }, " + "width = (int) 32, " + "depth = (int) 24, " + "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]; " + "audio/x-raw-int, " + "endianness = (int) { " ALSA_SINK2_FACTORY_ENDIANNESS " }, " + "signed = (boolean) { TRUE, FALSE }, " + "width = (int) 16, " + "depth = (int) 16, " + "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]; " + "audio/x-raw-int, " + "signed = (boolean) { TRUE, FALSE }, " + "width = (int) 8, " + "depth = (int) 8, " + "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ];" + "audio/x-iec958") + ); + +static void +gst_alsasink2_finalise (GObject * object) +{ + _k_GstAlsaSink *sink = GST_ALSA_SINK2 (object); + + g_free (sink->device); + g_mutex_free (sink->alsa_lock); + + g_static_mutex_lock (&output_mutex); + --output_ref; + if (output_ref == 0) { + snd_output_close (output); + output = NULL; + } + g_static_mutex_unlock (&output_mutex); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_alsasink2_init_interfaces (GType type) +{ + gst_alsa_type_add_device_property_probe_interface (type); +} + +static void +gst_alsasink2_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_set_details (element_class, &gst_alsasink2_details); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&alsasink2_sink_factory)); +} +static void +gst_alsasink2_class_init (_k_GstAlsaSinkClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseSinkClass *gstbasesink_class; + GstBaseAudioSinkClass *gstbaseaudiosink_class; + GstAudioSinkClass *gstaudiosink_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstbasesink_class = (GstBaseSinkClass *) klass; + gstbaseaudiosink_class = (GstBaseAudioSinkClass *) klass; + gstaudiosink_class = (GstAudioSinkClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_alsasink2_finalise); + gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_alsasink2_get_property); + gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_alsasink2_set_property); + + gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_alsasink2_getcaps); + + gstaudiosink_class->open = GST_DEBUG_FUNCPTR (gst_alsasink2_open); + gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_alsasink2_prepare); + gstaudiosink_class->unprepare = GST_DEBUG_FUNCPTR (gst_alsasink2_unprepare); + gstaudiosink_class->close = GST_DEBUG_FUNCPTR (gst_alsasink2_close); + gstaudiosink_class->write = GST_DEBUG_FUNCPTR (gst_alsasink2_write); + gstaudiosink_class->delay = GST_DEBUG_FUNCPTR (gst_alsasink2_delay); + gstaudiosink_class->reset = GST_DEBUG_FUNCPTR (gst_alsasink2_reset); + + g_object_class_install_property (gobject_class, PROP_DEVICE, + g_param_spec_string ("device", "Device", + "ALSA device, as defined in an asound configuration file", + DEFAULT_DEVICE, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_DEVICE_NAME, + g_param_spec_string ("device-name", "Device name", + "Human-readable name of the sound device", DEFAULT_DEVICE_NAME, + G_PARAM_READABLE)); +} + +static void +gst_alsasink2_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + _k_GstAlsaSink *sink; + + sink = GST_ALSA_SINK2 (object); + + switch (prop_id) { + case PROP_DEVICE: + g_free (sink->device); + sink->device = g_value_dup_string (value); + /* setting NULL restores the default device */ + if (sink->device == NULL) { + sink->device = g_strdup (DEFAULT_DEVICE); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_alsasink2_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + _k_GstAlsaSink *sink; + + sink = GST_ALSA_SINK2 (object); + + switch (prop_id) { + case PROP_DEVICE: + g_value_set_string (value, sink->device); + break; + case PROP_DEVICE_NAME: + g_value_take_string (value, + gst_alsa_find_device_name (GST_OBJECT_CAST (sink), + sink->device, sink->handle, SND_PCM_STREAM_PLAYBACK)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_alsasink2_init (_k_GstAlsaSink * alsasink2, _k_GstAlsaSinkClass * g_class) +{ + GST_DEBUG_OBJECT (alsasink2, "initializing alsasink2"); + + alsasink2->device = g_strdup (DEFAULT_DEVICE); + alsasink2->handle = NULL; + alsasink2->cached_caps = NULL; + alsasink2->alsa_lock = g_mutex_new (); + + g_static_mutex_lock (&output_mutex); + if (output_ref == 0) { + snd_output_stdio_attach (&output, stdout, 0); + ++output_ref; + } + g_static_mutex_unlock (&output_mutex); +} + +#define CHECK(call, error) \ +G_STMT_START { \ +if ((err = call) < 0) \ + goto error; \ +} G_STMT_END; + +static GstCaps * +gst_alsasink2_getcaps (GstBaseSink * bsink) +{ + GstElementClass *element_class; + GstPadTemplate *pad_template; + _k_GstAlsaSink *sink = GST_ALSA_SINK2 (bsink); + GstCaps *caps; + + if (sink->handle == NULL) { + GST_DEBUG_OBJECT (sink, "device not open, using template caps"); + return NULL; /* base class will get template caps for us */ + } + + if (sink->cached_caps) { + GST_LOG_OBJECT (sink, "Returning cached caps"); + return gst_caps_ref (sink->cached_caps); + } + + element_class = GST_ELEMENT_GET_CLASS (sink); + pad_template = gst_element_class_get_pad_template (element_class, "sink"); + g_return_val_if_fail (pad_template != NULL, NULL); + + caps = gst_alsa_probe_supported_formats (GST_OBJECT (sink), sink->handle, + gst_pad_template_get_caps (pad_template)); + + if (caps) { + sink->cached_caps = gst_caps_ref (caps); + } + + GST_INFO_OBJECT (sink, "returning caps %" GST_PTR_FORMAT, caps); + + return caps; +} + +static int +set_hwparams (_k_GstAlsaSink * alsa) +{ + guint rrate; + gint err, dir; + snd_pcm_hw_params_t *params; + guint period_time, buffer_time; + + snd_pcm_hw_params_malloc (¶ms); + + GST_DEBUG_OBJECT (alsa, "Negotiating to %d channels @ %d Hz (format = %s) " + "SPDIF (%d)", alsa->channels, alsa->rate, + snd_pcm_format_name (alsa->format), alsa->iec958); + + /* start with requested values, if we cannot configure alsa for those values, + * we set these values to -1, which will leave the default alsa values */ + buffer_time = alsa->buffer_time; + period_time = alsa->period_time; + +retry: + /* choose all parameters */ + CHECK (snd_pcm_hw_params_any (alsa->handle, params), no_config); + /* set the interleaved read/write format */ + CHECK (snd_pcm_hw_params_set_access (alsa->handle, params, alsa->access), + wrong_access); + /* set the sample format */ +#if GST_CHECK_VERSION(0, 10, 18) + if (alsa->iec958) { + /* Try to use big endian first else fallback to le and swap bytes */ + if (snd_pcm_hw_params_set_format (alsa->handle, params, alsa->format) < 0) { + alsa->format = SND_PCM_FORMAT_S16_LE; + alsa->need_swap = TRUE; + GST_DEBUG_OBJECT (alsa, "falling back to little endian with swapping"); + } else { + alsa->need_swap = FALSE; + } + } +#endif + CHECK (snd_pcm_hw_params_set_format (alsa->handle, params, alsa->format), + no_sample_format); + /* set the count of channels */ + CHECK (snd_pcm_hw_params_set_channels (alsa->handle, params, alsa->channels), + no_channels); + /* set the stream rate */ + rrate = alsa->rate; + CHECK (snd_pcm_hw_params_set_rate_near (alsa->handle, params, &rrate, NULL), + no_rate); + if (rrate != alsa->rate) + goto rate_match; + + /* get and dump some limits */ + { + guint min, max; + + snd_pcm_hw_params_get_buffer_time_min (params, &min, &dir); + snd_pcm_hw_params_get_buffer_time_max (params, &max, &dir); + + GST_DEBUG_OBJECT (alsa, "buffer time %u, min %u, max %u", + alsa->buffer_time, min, max); + + snd_pcm_hw_params_get_period_time_min (params, &min, &dir); + snd_pcm_hw_params_get_period_time_max (params, &max, &dir); + + GST_DEBUG_OBJECT (alsa, "period time %u, min %u, max %u", + alsa->period_time, min, max); + + snd_pcm_hw_params_get_periods_min (params, &min, &dir); + snd_pcm_hw_params_get_periods_max (params, &max, &dir); + + GST_DEBUG_OBJECT (alsa, "periods min %u, max %u", min, max); + } + + /* now try to configure the buffer time and period time, if one + * of those fail, we fall back to the defaults and emit a warning. */ + if (buffer_time != ~0u && !alsa->iec958) { + /* set the buffer time */ + if ((err = snd_pcm_hw_params_set_buffer_time_near (alsa->handle, params, + &buffer_time, &dir)) < 0) { + GST_ELEMENT_WARNING (alsa, RESOURCE, SETTINGS, (NULL), + ("Unable to set buffer time %i for playback: %s", + buffer_time, snd_strerror (err))); + /* disable buffer_time the next round */ + buffer_time = -1; + goto retry; + } + GST_DEBUG_OBJECT (alsa, "buffer time %u", buffer_time); + } + if (period_time != ~0u && !alsa->iec958) { + /* set the period time */ + if ((err = snd_pcm_hw_params_set_period_time_near (alsa->handle, params, + &period_time, &dir)) < 0) { + GST_ELEMENT_WARNING (alsa, RESOURCE, SETTINGS, (NULL), + ("Unable to set period time %i for playback: %s", + period_time, snd_strerror (err))); + /* disable period_time the next round */ + period_time = -1; + goto retry; + } + GST_DEBUG_OBJECT (alsa, "period time %u", period_time); + } + + /* Set buffer size and period size manually for SPDIF */ + if (G_UNLIKELY (alsa->iec958)) { + snd_pcm_uframes_t buffer_size = SPDIF_BUFFER_SIZE; + snd_pcm_uframes_t period_size = SPDIF_PERIOD_SIZE; + + CHECK (snd_pcm_hw_params_set_buffer_size_near (alsa->handle, params, + &buffer_size), buffer_size); + CHECK (snd_pcm_hw_params_set_period_size_near (alsa->handle, params, + &period_size, NULL), period_size); + } + + /* write the parameters to device */ + CHECK (snd_pcm_hw_params (alsa->handle, params), set_hw_params); + + /* now get the configured values */ + CHECK (snd_pcm_hw_params_get_buffer_size (params, &alsa->buffer_size), + buffer_size); + CHECK (snd_pcm_hw_params_get_period_size (params, &alsa->period_size, &dir), + period_size); + + GST_DEBUG_OBJECT (alsa, "buffer size %lu, period size %lu", alsa->buffer_size, + alsa->period_size); + + snd_pcm_hw_params_free (params); + return 0; + + /* ERRORS */ +no_config: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), + ("Broken configuration for playback: no configurations available: %s", + snd_strerror (err))); + snd_pcm_hw_params_free (params); + return err; + } +wrong_access: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), + ("Access type not available for playback: %s", snd_strerror (err))); + snd_pcm_hw_params_free (params); + return err; + } +no_sample_format: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), + ("Sample format not available for playback: %s", snd_strerror (err))); + snd_pcm_hw_params_free (params); + return err; + } +no_channels: + { + gchar *msg = NULL; + + if ((alsa->channels) == 1) + msg = g_strdup (_("Could not open device for playback in mono mode.")); + if ((alsa->channels) == 2) + msg = g_strdup (_("Could not open device for playback in stereo mode.")); + if ((alsa->channels) > 2) + msg = + g_strdup_printf (_ + ("Could not open device for playback in %d-channel mode."), + alsa->channels); + GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (msg), (snd_strerror (err))); + g_free (msg); + snd_pcm_hw_params_free (params); + return err; + } +no_rate: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), + ("Rate %iHz not available for playback: %s", + alsa->rate, snd_strerror (err))); + return err; + } +rate_match: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), + ("Rate doesn't match (requested %iHz, get %iHz)", alsa->rate, err)); + snd_pcm_hw_params_free (params); + return -EINVAL; + } +buffer_size: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), + ("Unable to get buffer size for playback: %s", snd_strerror (err))); + snd_pcm_hw_params_free (params); + return err; + } +period_size: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), + ("Unable to get period size for playback: %s", snd_strerror (err))); + snd_pcm_hw_params_free (params); + return err; + } +set_hw_params: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), + ("Unable to set hw params for playback: %s", snd_strerror (err))); + snd_pcm_hw_params_free (params); + return err; + } +} + +static int +set_swparams (_k_GstAlsaSink * alsa) +{ + int err; + snd_pcm_sw_params_t *params; + + snd_pcm_sw_params_malloc (¶ms); + + /* get the current swparams */ + CHECK (snd_pcm_sw_params_current (alsa->handle, params), no_config); + /* start the transfer when the buffer is almost full: */ + /* (buffer_size / avail_min) * avail_min */ + CHECK (snd_pcm_sw_params_set_start_threshold (alsa->handle, params, + (alsa->buffer_size / alsa->period_size) * alsa->period_size), + start_threshold); + + /* allow the transfer when at least period_size samples can be processed */ + CHECK (snd_pcm_sw_params_set_avail_min (alsa->handle, params, + alsa->period_size), set_avail); + +#if GST_CHECK_ALSA_VERSION(1,0,16) + /* snd_pcm_sw_params_set_xfer_align() is deprecated, alignment is always 1 */ +#else + /* align all transfers to 1 sample */ + CHECK (snd_pcm_sw_params_set_xfer_align (alsa->handle, params, 1), set_align); +#endif + + /* write the parameters to the playback device */ + CHECK (snd_pcm_sw_params (alsa->handle, params), set_sw_params); + + snd_pcm_sw_params_free (params); + return 0; + + /* ERRORS */ +no_config: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), + ("Unable to determine current swparams for playback: %s", + snd_strerror (err))); + snd_pcm_sw_params_free (params); + return err; + } +start_threshold: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), + ("Unable to set start threshold mode for playback: %s", + snd_strerror (err))); + snd_pcm_sw_params_free (params); + return err; + } +set_avail: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), + ("Unable to set avail min for playback: %s", snd_strerror (err))); + snd_pcm_sw_params_free (params); + return err; + } +#if !GST_CHECK_ALSA_VERSION(1,0,16) +set_align: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), + ("Unable to set transfer align for playback: %s", snd_strerror (err))); + snd_pcm_sw_params_free (params); + return err; + } +#endif +set_sw_params: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), + ("Unable to set sw params for playback: %s", snd_strerror (err))); + snd_pcm_sw_params_free (params); + return err; + } +} + +static gboolean +alsasink2_parse_spec (_k_GstAlsaSink * alsa, GstRingBufferSpec * spec) +{ + /* Initialize our boolean */ + alsa->iec958 = FALSE; + + switch (spec->type) { + case GST_BUFTYPE_LINEAR: + GST_DEBUG_OBJECT (alsa, + "Linear format : depth=%d, width=%d, sign=%d, bigend=%d", spec->depth, + spec->width, spec->sign, spec->bigend); + + alsa->format = snd_pcm_build_linear_format (spec->depth, spec->width, + spec->sign ? 0 : 1, spec->bigend ? 1 : 0); + break; + case GST_BUFTYPE_FLOAT: + switch (spec->format) { + case GST_FLOAT32_LE: + alsa->format = SND_PCM_FORMAT_FLOAT_LE; + break; + case GST_FLOAT32_BE: + alsa->format = SND_PCM_FORMAT_FLOAT_BE; + break; + case GST_FLOAT64_LE: + alsa->format = SND_PCM_FORMAT_FLOAT64_LE; + break; + case GST_FLOAT64_BE: + alsa->format = SND_PCM_FORMAT_FLOAT64_BE; + break; + default: + goto error; + } + break; + case GST_BUFTYPE_A_LAW: + alsa->format = SND_PCM_FORMAT_A_LAW; + break; + case GST_BUFTYPE_MU_LAW: + alsa->format = SND_PCM_FORMAT_MU_LAW; + break; +#if GST_CHECK_VERSION(0, 10, 18) + case GST_BUFTYPE_IEC958: + alsa->format = SND_PCM_FORMAT_S16_BE; + alsa->iec958 = TRUE; + break; +#endif + default: + goto error; + + } + alsa->rate = spec->rate; + alsa->channels = spec->channels; + alsa->buffer_time = spec->buffer_time; + alsa->period_time = spec->latency_time; + alsa->access = SND_PCM_ACCESS_RW_INTERLEAVED; + + return TRUE; + + /* ERRORS */ +error: + { + return FALSE; + } +} + +static gboolean +gst_alsasink2_open (GstAudioSink * asink) +{ + _k_GstAlsaSink *alsa; + gint err; + + alsa = GST_ALSA_SINK2 (asink); + + CHECK (snd_pcm_open (&alsa->handle, alsa->device, SND_PCM_STREAM_PLAYBACK, + SND_PCM_NONBLOCK), open_error); + GST_LOG_OBJECT (alsa, "Opened device %s", alsa->device); + + return TRUE; + + /* ERRORS */ +open_error: + { + if (err == -EBUSY) { + GST_ELEMENT_ERROR (alsa, RESOURCE, BUSY, + (_("Could not open audio device for playback. " + "Device is being used by another application.")), + ("Device '%s' is busy", alsa->device)); + } else { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_WRITE, + (_("Could not open audio device for playback.")), + ("Playback open error on device '%s': %s", alsa->device, + snd_strerror (err))); + } + return FALSE; + } +} + +static gboolean +gst_alsasink2_prepare (GstAudioSink * asink, GstRingBufferSpec * spec) +{ + _k_GstAlsaSink *alsa; + gint err; + + alsa = GST_ALSA_SINK2 (asink); + +#if GST_CHECK_VERSION(0, 10, 18) + if (spec->format == GST_IEC958) { + snd_pcm_close (alsa->handle); + alsa->handle = gst_alsa_open_iec958_pcm (GST_OBJECT (alsa)); + if (G_UNLIKELY (!alsa->handle)) { + goto no_iec958; + } + } +#endif + + if (!alsasink2_parse_spec (alsa, spec)) + goto spec_parse; + + CHECK (set_hwparams (alsa), hw_params_failed); + CHECK (set_swparams (alsa), sw_params_failed); + + alsa->bytes_per_sample = spec->bytes_per_sample; + spec->segsize = alsa->period_size * spec->bytes_per_sample; + spec->segtotal = alsa->buffer_size / alsa->period_size; + + { + snd_output_t *out_buf = NULL; + char *msg = NULL; + + snd_output_buffer_open (&out_buf); + snd_pcm_dump_hw_setup (alsa->handle, out_buf); + snd_output_buffer_string (out_buf, &msg); + GST_DEBUG_OBJECT (alsa, "Hardware setup: \n%s", msg); + snd_output_close (out_buf); + snd_output_buffer_open (&out_buf); + snd_pcm_dump_sw_setup (alsa->handle, out_buf); + snd_output_buffer_string (out_buf, &msg); + GST_DEBUG_OBJECT (alsa, "Software setup: \n%s", msg); + snd_output_close (out_buf); + } + + return TRUE; + + /* ERRORS */ +#if GST_CHECK_VERSION(0, 10, 18) +no_iec958: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, OPEN_WRITE, (NULL), + ("Could not open IEC958 (SPDIF) device for playback")); + return FALSE; + } +#endif +spec_parse: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), + ("Error parsing spec")); + return FALSE; + } +hw_params_failed: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), + ("Setting of hwparams failed: %s", snd_strerror (err))); + return FALSE; + } +sw_params_failed: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), + ("Setting of swparams failed: %s", snd_strerror (err))); + return FALSE; + } +} + +static gboolean +gst_alsasink2_unprepare (GstAudioSink * asink) +{ + _k_GstAlsaSink *alsa; + gint err; + + alsa = GST_ALSA_SINK2 (asink); + + CHECK (snd_pcm_drop (alsa->handle), drop); + + CHECK (snd_pcm_hw_free (alsa->handle), hw_free); + + return TRUE; + + /* ERRORS */ +drop: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), + ("Could not drop samples: %s", snd_strerror (err))); + return FALSE; + } +hw_free: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, SETTINGS, (NULL), + ("Could not free hw params: %s", snd_strerror (err))); + return FALSE; + } +} + +static gboolean +gst_alsasink2_close (GstAudioSink * asink) +{ + _k_GstAlsaSink *alsa = GST_ALSA_SINK2 (asink); + gint err; + + if (alsa->handle) { + CHECK (snd_pcm_close (alsa->handle), close_error); + alsa->handle = NULL; + } + gst_caps_replace (&alsa->cached_caps, NULL); + + return TRUE; + + /* ERRORS */ +close_error: + { + GST_ELEMENT_ERROR (alsa, RESOURCE, CLOSE, (NULL), + ("Playback close error: %s", snd_strerror (err))); + return FALSE; + } +} + + +/* + * Underrun and suspend recovery + */ +static gint +xrun_recovery (_k_GstAlsaSink * alsa, snd_pcm_t * handle, gint err) +{ + GST_DEBUG_OBJECT (alsa, "xrun recovery %d", err); + + if (err == -EPIPE) { /* under-run */ + err = snd_pcm_prepare (handle); + if (err < 0) { + GST_WARNING_OBJECT (alsa, + "Can't recovery from underrun, prepare failed: %s", + snd_strerror (err)); + } + return 0; + } else if (err == -ESTRPIPE) { + while ((err = snd_pcm_resume (handle)) == -EAGAIN) + g_usleep (100); /* wait until the suspend flag is released */ + + if (err < 0) { + err = snd_pcm_prepare (handle); + if (err < 0) { + GST_WARNING_OBJECT (alsa, + "Can't recovery from suspend, prepare failed: %s", + snd_strerror (err)); + } + } + return 0; + } + return err; +} + +static guint +gst_alsasink2_write (GstAudioSink * asink, gpointer data, guint length) +{ + _k_GstAlsaSink *alsa; + gint err; + gint cptr; + gint16 *ptr = data; + + alsa = GST_ALSA_SINK2 (asink); + + if (alsa->iec958 && alsa->need_swap) { + guint i; + + GST_DEBUG_OBJECT (asink, "swapping bytes"); + for (i = 0; i < length / 2; i++) { + ptr[i] = GUINT16_SWAP_LE_BE (ptr[i]); + } + } + + GST_LOG_OBJECT (asink, "received audio samples buffer of %u bytes", length); + + cptr = length / alsa->bytes_per_sample; + + GST_ALSA_SINK2_LOCK (asink); + while (cptr > 0) { + /* start by doing a blocking wait for free space. Set the timeout + * to 4 times the period time */ + err = snd_pcm_wait (alsa->handle, (4 * alsa->period_time / 1000)); + if (err < 0) { + GST_DEBUG_OBJECT (asink, "wait timeout, %d", err); + } else { + err = snd_pcm_writei (alsa->handle, ptr, cptr); + } + + GST_DEBUG_OBJECT (asink, "written %d frames out of %d", err, cptr); + if (err < 0) { + GST_DEBUG_OBJECT (asink, "Write error: %s", snd_strerror (err)); + if (err == -EAGAIN) { + continue; + } else if (xrun_recovery (alsa, alsa->handle, err) < 0) { + goto write_error; + } + continue; + } + + ptr += snd_pcm_frames_to_bytes (alsa->handle, err); + cptr -= err; + } + GST_ALSA_SINK2_UNLOCK (asink); + + return length - (cptr * alsa->bytes_per_sample); + +write_error: + { + GST_ALSA_SINK2_UNLOCK (asink); + return length; /* skip one period */ + } +} + +static guint +gst_alsasink2_delay (GstAudioSink * asink) +{ + _k_GstAlsaSink *alsa; + snd_pcm_sframes_t delay; + int res; + + alsa = GST_ALSA_SINK2 (asink); + + res = snd_pcm_delay (alsa->handle, &delay); + if (G_UNLIKELY (res < 0)) { + /* on errors, report 0 delay */ + GST_DEBUG_OBJECT (alsa, "snd_pcm_delay returned %d", res); + delay = 0; + } + if (G_UNLIKELY (delay < 0)) { + /* make sure we never return a negative delay */ + GST_WARNING_OBJECT (alsa, "snd_pcm_delay returned negative delay"); + delay = 0; + } + + return delay; +} + +static void +gst_alsasink2_reset (GstAudioSink * asink) +{ + _k_GstAlsaSink *alsa; + gint err; + + alsa = GST_ALSA_SINK2 (asink); + + GST_ALSA_SINK2_LOCK (asink); + GST_DEBUG_OBJECT (alsa, "drop"); + CHECK (snd_pcm_drop (alsa->handle), drop_error); + GST_DEBUG_OBJECT (alsa, "prepare"); + CHECK (snd_pcm_prepare (alsa->handle), prepare_error); + GST_DEBUG_OBJECT (alsa, "reset done"); + GST_ALSA_SINK2_UNLOCK (asink); + + return; + + /* ERRORS */ +drop_error: + { + GST_ERROR_OBJECT (alsa, "alsa-reset: pcm drop error: %s", + snd_strerror (err)); + GST_ALSA_SINK2_UNLOCK (asink); + return; + } +prepare_error: + { + GST_ERROR_OBJECT (alsa, "alsa-reset: pcm prepare error: %s", + snd_strerror (err)); + GST_ALSA_SINK2_UNLOCK (asink); + return; + } +} + +static void +gst_alsa_error_wrapper (const char *file, int line, const char *function, + int err, const char *fmt, ...) +{ +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + int err; + + if (!gst_element_register (plugin, "_k_alsasink", GST_RANK_PRIMARY, + GST_TYPE_ALSA_SINK2)) + return FALSE; + + err = snd_lib_error_set_handler (gst_alsa_error_wrapper); + if (err != 0) + GST_WARNING ("failed to set alsa error handler"); + + return TRUE; +} + +#define PACKAGE "" +GST_PLUGIN_DEFINE_STATIC (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "_k_alsa", + "ALSA plugin library (hotfixed)", + plugin_init, "0.1", "LGPL", "Phonon-GStreamer", "") +#undef PACKAGE diff --git a/src/3rdparty/phonon/gstreamer/alsasink2.h b/src/3rdparty/phonon/gstreamer/alsasink2.h new file mode 100644 index 0000000..bec9933 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/alsasink2.h @@ -0,0 +1,87 @@ +/* GStreamer + * Copyright (C) 2005 Wim Taymans <wim@fluendo.com> + * Copyright (C) 2008 Matthias Kretz <kretz@kde.org> + * + * gstalsasink2.h: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef ALSASINK2_H +#define ALSASINK2_H + +#include <gst/gst.h> +#include <gst/audio/gstaudiosink.h> +#include <alsa/asoundlib.h> + +G_BEGIN_DECLS + +#define GST_TYPE_ALSA_SINK2 (gst_alsasink2_get_type()) +#define GST_ALSA_SINK2(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ALSA_SINK2,_k_GstAlsaSink)) +#define GST_ALSA_SINK2_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ALSA_SINK2,_k_GstAlsaSinkClass)) +#define GST_IS_ALSA_SINK2(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ALSA_SINK2)) +#define GST_IS_ALSA_SINK2_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ALSA_SINK2)) +#define GST_ALSA_SINK2_CAST(obj) ((_k_GstAlsaSink *) (obj)) + +typedef struct _k_GstAlsaSink _k_GstAlsaSink; +typedef struct _k_GstAlsaSinkClass _k_GstAlsaSinkClass; + +#define GST_ALSA_SINK2_GET_LOCK(obj) (GST_ALSA_SINK2_CAST (obj)->alsa_lock) +#define GST_ALSA_SINK2_LOCK(obj) (g_mutex_lock (GST_ALSA_SINK2_GET_LOCK (obj))) +#define GST_ALSA_SINK2_UNLOCK(obj) (g_mutex_unlock (GST_ALSA_SINK2_GET_LOCK (obj))) + +/** + * _k_GstAlsaSink: + * + * Opaque data structure + */ +struct _k_GstAlsaSink { + GstAudioSink sink; + + gchar *device; + + snd_pcm_t *handle; + snd_pcm_hw_params_t *hwparams; + snd_pcm_sw_params_t *swparams; + + snd_pcm_access_t access; + snd_pcm_format_t format; + guint rate; + guint channels; + gint bytes_per_sample; + gboolean iec958; + gboolean need_swap; + + guint buffer_time; + guint period_time; + snd_pcm_uframes_t buffer_size; + snd_pcm_uframes_t period_size; + + GstCaps *cached_caps; + + GMutex *alsa_lock; +}; + +struct _k_GstAlsaSinkClass { + GstAudioSinkClass parent_class; +}; + +GType gst_alsasink2_get_type(void); + +G_END_DECLS + +#endif /* ALSASINK2_H */ diff --git a/src/3rdparty/phonon/gstreamer/artssink.cpp b/src/3rdparty/phonon/gstreamer/artssink.cpp new file mode 100644 index 0000000..ff56da9 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/artssink.cpp @@ -0,0 +1,277 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +/***************************************** + * + * This is an aRts plugin for GStreamer + * + ****************************************/ + +#include <gst/gst.h> +#include <gst/audio/audio.h> +#include <gst/audio/gstaudiosink.h> +#include "artssink.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ + +static GstStaticPadTemplate sinktemplate = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ( + "audio/x-raw-int, " + "width = (int) { 8, 16 }, " + "depth = (int) { 8, 16 }, " + "endianness = (int) BYTE_ORDER, " + "channels = (int) { 1, 2 }, " + "rate = (int) [ 8000, 96000 ]" + ) +); + +typedef int (*Ptr_arts_init)(); +typedef arts_stream_t (*Ptr_arts_play_stream)(int, int, int, const char*); +typedef int (*Ptr_arts_close_stream)(arts_stream_t); +typedef int (*Ptr_arts_stream_get)(arts_stream_t, arts_parameter_t_enum); +typedef int (*Ptr_arts_stream_set)(arts_stream_t, arts_parameter_t_enum, int value); +typedef int (*Ptr_arts_write)(arts_stream_t, const void *, int); +typedef int (*Ptr_arts_suspended)(); +typedef void (*Ptr_arts_free)(); + +static Ptr_arts_init p_arts_init = 0; +static Ptr_arts_play_stream p_arts_play_stream = 0; +static Ptr_arts_close_stream p_arts_close_stream = 0; +static Ptr_arts_stream_get p_arts_stream_get= 0; +static Ptr_arts_stream_set p_arts_stream_set= 0; +static Ptr_arts_write p_arts_write = 0; +static Ptr_arts_suspended p_arts_suspended = 0; +static Ptr_arts_free p_arts_free = 0; + +static void arts_sink_dispose (GObject * object); +static void arts_sink_reset (GstAudioSink * asink); +static void arts_sink_finalize (GObject * object); +static GstCaps *arts_sink_get_caps (GstBaseSink * bsink); +static gboolean arts_sink_open (GstAudioSink * asink); +static gboolean arts_sink_close (GstAudioSink * asink); +static gboolean arts_sink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec); +static gboolean arts_sink_unprepare (GstAudioSink * asink); +static guint arts_sink_write (GstAudioSink * asink, gpointer data, guint length); +static guint arts_sink_delay (GstAudioSink * asink); + +static gboolean connected = false; +static gboolean init = false; +static int sinkCount; + +GST_BOILERPLATE (ArtsSink, arts_sink, GstAudioSink, GST_TYPE_AUDIO_SINK) + +// ArtsSink args +enum +{ + ARG_0, + ARG_ARTSSINK +}; + +/* open the device with given specs */ +gboolean arts_sink_open(GstAudioSink *sink) +{ + Q_UNUSED(sink); + + // We already have an open connection to this device + if (!init) { + GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL), ("Could not connect to aRts", NULL)); + return false; + } else if (connected) { + GST_ELEMENT_ERROR (sink, RESOURCE, BUSY, (NULL), ("Device is busy", NULL)); + return false; + } + + // Check if all symbols were resolved + if (!(p_arts_init && p_arts_play_stream && p_arts_close_stream + && p_arts_stream_get && p_arts_stream_set && p_arts_write && p_arts_free)) + return FALSE; + + // Check if arts_init succeeded + if (!init) + return false; + + return true; +} + +/* prepare resources and state to operate with the given specs */ +static gboolean arts_sink_prepare(GstAudioSink *sink, GstRingBufferSpec *spec) +{ + ArtsSink *asink = (ArtsSink*)sink; + + if (!init) + return false; + + asink->samplerate = spec->rate; + asink->samplebits = spec->depth; + asink->channels = spec->channels; + asink->bytes_per_sample = spec->bytes_per_sample; + + static int id = 0; + asink->stream = p_arts_play_stream(spec->rate, spec->depth, spec->channels, + QString("gstreamer-%0").arg(id++).toLatin1().constData()); + if (asink->stream) + connected = true; + + return connected; +} + +/* undo anything that was done in prepare() */ +static gboolean arts_sink_unprepare(GstAudioSink *sink) +{ + Q_UNUSED(sink); + ArtsSink *asink = (ArtsSink*)sink; + if (init && connected) { + p_arts_close_stream(asink->stream); + connected = false; + } + return true; +} + +/* close the device */ +static gboolean arts_sink_close(GstAudioSink *sink) +{ + Q_UNUSED(sink); + return true; +} + +/* write samples to the device */ +static guint arts_sink_write(GstAudioSink *sink, gpointer data, guint length) +{ + ArtsSink *asink = (ArtsSink*)sink; + + if (!init) + return 0; + + int errorcode = p_arts_write(asink->stream, (char*)data, length); + + if (errorcode < 0) + GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL), ("Could not write to device.", NULL)); + + return errorcode > 0 ? errorcode : 0; +} + +/* get number of samples queued in the device */ +static guint arts_sink_delay(GstAudioSink *sink) +{ + ArtsSink *asink = (ArtsSink*)sink; + if (!init) + return 0; + + // We get results in millisecons so we have to caculate the approximate size in samples + guint delay = p_arts_stream_get(asink->stream, ARTS_P_SERVER_LATENCY) * (asink->samplerate / 1000); + return delay; +} + +/* reset the audio device, unblock from a write */ +static void arts_sink_reset(GstAudioSink *sink) +{ + // ### We are currently unable to gracefully recover + // after artsd has been restarted or killed. + Q_UNUSED(sink); +} + +// Register element details +static void arts_sink_base_init (gpointer g_class) { + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class); + static gchar longname[] = "Experimental aRts sink", + klass[] = "Sink/Audio", + description[] = "aRts Audio Output Device", + author[] = "Nokia Corporation and/or its subsidiary(-ies) <qt-info@nokia.com>"; + GstElementDetails details = GST_ELEMENT_DETAILS (longname, + klass, + description, + author); + gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get (&sinktemplate)); + gst_element_class_set_details (gstelement_class, &details); +} + +static void arts_sink_class_init (ArtsSinkClass * klass) +{ + parent_class = (GstAudioSinkClass*)g_type_class_peek_parent(klass); + + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = GST_DEBUG_FUNCPTR (arts_sink_finalize); + gobject_class->dispose = GST_DEBUG_FUNCPTR (arts_sink_dispose); + + GstBaseSinkClass *gstbasesink_class = (GstBaseSinkClass *) klass; + gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (arts_sink_get_caps); + + GstAudioSinkClass *gstaudiosink_class = (GstAudioSinkClass*)klass; + gstaudiosink_class->open = GST_DEBUG_FUNCPTR(arts_sink_open); + gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR(arts_sink_prepare); + gstaudiosink_class->unprepare = GST_DEBUG_FUNCPTR(arts_sink_unprepare); + gstaudiosink_class->close = GST_DEBUG_FUNCPTR(arts_sink_close); + gstaudiosink_class->write = GST_DEBUG_FUNCPTR(arts_sink_write); + gstaudiosink_class->delay = GST_DEBUG_FUNCPTR(arts_sink_delay); + gstaudiosink_class->reset = GST_DEBUG_FUNCPTR(arts_sink_reset); +} + +static void arts_sink_init (ArtsSink * src, ArtsSinkClass * g_class) +{ + Q_UNUSED(g_class); + GST_DEBUG_OBJECT (src, "initializing artssink"); + src->stream = 0; + + p_arts_init = (Ptr_arts_init)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_init"); + p_arts_play_stream = (Ptr_arts_play_stream)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_play_stream"); + p_arts_close_stream = (Ptr_arts_close_stream)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_close_stream"); + p_arts_stream_get = (Ptr_arts_stream_get)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_stream_get"); + p_arts_stream_set = (Ptr_arts_stream_set)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_stream_set"); + p_arts_write = (Ptr_arts_write)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_write"); + p_arts_suspended = (Ptr_arts_suspended)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_suspended"); + p_arts_free = (Ptr_arts_free)QLibrary::resolve(QLatin1String("artsc"), 0, "arts_free"); + + if (!sinkCount) { + int errorcode = p_arts_init(); + if (!errorcode) { + init = TRUE; + } + } + sinkCount ++; +} + +static void arts_sink_dispose (GObject * object) +{ + Q_UNUSED(object); + if (--sinkCount == 0) { + p_arts_free(); + } +} + +static void arts_sink_finalize (GObject * object) +{ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static GstCaps *arts_sink_get_caps (GstBaseSink * bsink) +{ + Q_UNUSED(bsink); + return NULL; +} + +} +} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/gstreamer/artssink.h b/src/3rdparty/phonon/gstreamer/artssink.h new file mode 100644 index 0000000..169e73e --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/artssink.h @@ -0,0 +1,91 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef __ARTS_SINK_H__ +#define __ARTS_SINK_H__ + +#include <QtCore> +#include <sys/types.h> +#include <gst/gst.h> +#include <gst/audio/gstaudiosink.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ + +G_BEGIN_DECLS + +extern "C" { + +#define GST_TYPE_ARTS_SINK (arts_sink_get_type()) +#define GST_ARTS_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ARTS_SINK,ArtsSink)) +#define GST_ARTS_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ARTS_SINK,ArtsSinkClass)) +#define GST_IS_ARTS_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ARTS_SINK)) +#define GST_IS_ARTS_SINK_CLASS(klass)(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ARTS_SINK)) + +typedef struct _ArtsSink ArtsSink; +typedef struct _ArtsSinkClass ArtsSinkClass; + +enum arts_parameter_t_enum { + ARTS_P_BUFFER_SIZE = 1, + ARTS_P_BUFFER_TIME = 2, + ARTS_P_BUFFER_SPACE = 3, + ARTS_P_SERVER_LATENCY = 4, + ARTS_P_TOTAL_LATENCY = 5, + ARTS_P_BLOCKING = 6, + ARTS_P_PACKET_SIZE = 7, + ARTS_P_PACKET_COUNT = 8, + ARTS_P_PACKET_SETTINGS = 9 +}; + +typedef void *arts_stream_t; + +struct _ArtsSink { + GstAudioSink sink; + arts_stream_t stream; + int samplerate; + int samplebits; + int channels; + int bytes_per_sample; +}; + +struct GConfClient; +struct GError; +typedef void (*Ptr_g_type_init)(); +typedef GConfClient* (*Ptr_gconf_client_get_default)(); +typedef char* (*Ptr_gconf_client_get_string)(GConfClient*, const char*, GError **); +typedef void (*Ptr_g_object_unref)(void *); +typedef void (*Ptr_g_error_free)(GError *); +typedef void (*Ptr_g_free)(void*); + +struct _ArtsSinkClass { + GstAudioSinkClass parent_class; +}; + +GType arts_sink_get_type (void); +} +G_END_DECLS + +} +} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE + +#endif // __ARTS_SINK_H__ diff --git a/src/3rdparty/phonon/gstreamer/audioeffect.cpp b/src/3rdparty/phonon/gstreamer/audioeffect.cpp new file mode 100644 index 0000000..db72c8b --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/audioeffect.cpp @@ -0,0 +1,78 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "common.h" +#include "backend.h" +#include "medianode.h" +#include "effectmanager.h" +#include "audioeffect.h" +#include "gsthelper.h" + +#include <gst/gst.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ +AudioEffect::AudioEffect(Backend *backend, int effectId, QObject *parent) + : Effect(backend, parent, AudioSource | AudioSink) +{ + static int count = 0; + m_name = "AudioEffect" + QString::number(count++); + QList<EffectInfo*> audioEffects = backend->effectManager()->audioEffects(); + if (effectId >= 0 && effectId < audioEffects.size()) { + m_effectName = audioEffects[effectId]->name(); + init(); + } else { + Q_ASSERT(0); // Effect ID out of range + } +} + +GstElement* AudioEffect::createEffectBin() +{ + GstElement *audioBin = gst_bin_new(NULL); + + // We need a queue to handle tee-connections from parent node + GstElement *queue= gst_element_factory_make ("queue", NULL); + gst_bin_add(GST_BIN(audioBin), queue); + + GstElement *mconv= gst_element_factory_make ("audioconvert", NULL); + gst_bin_add(GST_BIN(audioBin), mconv); + + m_effectElement = gst_element_factory_make (qPrintable(m_effectName), NULL); + gst_bin_add(GST_BIN(audioBin), m_effectElement); + + //Link src pad + GstPad *srcPad= gst_element_get_pad (m_effectElement, "src"); + gst_element_add_pad (audioBin, gst_ghost_pad_new ("src", srcPad)); + gst_object_unref (srcPad); + + //Link sink pad + gst_element_link_many(queue, mconv, m_effectElement, (const char*)NULL); + GstPad *sinkpad = gst_element_get_pad (queue, "sink"); + gst_element_add_pad (audioBin, gst_ghost_pad_new ("sink", sinkpad)); + gst_object_unref (sinkpad); + return audioBin; +} + +} +} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE +#include "moc_audioeffect.cpp" diff --git a/src/3rdparty/phonon/gstreamer/audioeffect.h b/src/3rdparty/phonon/gstreamer/audioeffect.h new file mode 100644 index 0000000..3a985e5 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/audioeffect.h @@ -0,0 +1,55 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_GSTREAMER_AUDIOEFFECT_H +#define Phonon_GSTREAMER_AUDIOEFFECT_H + +#include "common.h" +#include "effect.h" +#include "medianode.h" + +#include <phonon/effectparameter.h> +#include <phonon/effectinterface.h> + +#include <QtCore/QObject> + +#include <gst/gst.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ + class AudioOutput; + class EffectInfo; + + class AudioEffect : public Effect + { + Q_OBJECT + public: + AudioEffect (Backend *backend, int effectId, QObject *parent); + protected: + GstElement* createEffectBin(); + GstElement* audioElement() { return m_effectBin; } + QString m_effectName; + }; +}} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE + +#endif // Phonon_GSTREAMER_AUDIOEFFECT_H diff --git a/src/3rdparty/phonon/gstreamer/audiooutput.cpp b/src/3rdparty/phonon/gstreamer/audiooutput.cpp new file mode 100644 index 0000000..138a7e4 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/audiooutput.cpp @@ -0,0 +1,257 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + Copyright (C) 2008 Matthias Kretz <kretz@kde.org> + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "common.h" +#include "audiooutput.h" +#include "backend.h" +#include "mediaobject.h" +#include "gsthelper.h" +#include <phonon/audiooutput.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ +AudioOutput::AudioOutput(Backend *backend, QObject *parent) + : QObject(parent) + , MediaNode(backend, AudioSink) + , m_volumeLevel(1.0) + , m_device(0) // ### get from backend + , m_volumeElement(0) + , m_audioBin(0) + , m_audioSink(0) + , m_conv(0) +{ + static int count = 0; + m_name = "AudioOutput" + QString::number(count++); + if (m_backend->isValid()) { + g_set_application_name(qApp->applicationName().toUtf8()); + m_audioBin = gst_bin_new (NULL); + gst_object_ref (GST_OBJECT (m_audioBin)); + gst_object_sink (GST_OBJECT (m_audioBin)); + + m_conv = gst_element_factory_make ("audioconvert", NULL); + + // Get category from parent + Phonon::Category category = Phonon::NoCategory; + if (Phonon::AudioOutput *audioOutput = qobject_cast<Phonon::AudioOutput *>(parent)) + category = audioOutput->category(); + + m_audioSink = m_backend->deviceManager()->createAudioSink(category); + m_volumeElement = gst_element_factory_make ("volume", NULL); + GstElement *queue = gst_element_factory_make ("queue", NULL); + GstElement *audioresample = gst_element_factory_make ("audioresample", NULL); + + if (queue && m_audioBin && m_conv && audioresample && m_audioSink && m_volumeElement) { + gst_bin_add_many (GST_BIN (m_audioBin), queue, m_conv, audioresample, m_volumeElement, m_audioSink, (const char*)NULL); + + if (gst_element_link_many (queue, m_conv, audioresample, m_volumeElement, m_audioSink, (const char*)NULL)) { + // Add ghost sink for audiobin + GstPad *audiopad = gst_element_get_pad (queue, "sink"); + gst_element_add_pad (m_audioBin, gst_ghost_pad_new ("sink", audiopad)); + gst_object_unref (audiopad); + m_isValid = true; // Initialization ok, accept input + } + } + } +} + +void AudioOutput::mediaNodeEvent(const MediaNodeEvent *event) +{ + if (!m_audioBin) + return; + + switch (event->type()) { + + default: + break; + } +} + + +AudioOutput::~AudioOutput() +{ + if (m_audioBin) { + gst_element_set_state (m_audioBin, GST_STATE_NULL); + gst_object_unref (m_audioBin); + } +} + +qreal AudioOutput::volume() const +{ + return m_volumeLevel; +} + +int AudioOutput::outputDevice() const +{ + return m_device; +} + +void AudioOutput::setVolume(qreal newVolume) +{ + if (newVolume > 2.0 ) + newVolume = 2.0; + else if (newVolume < 0.0) + newVolume = 0.0; + + if (newVolume == m_volumeLevel) + return; + + m_volumeLevel = newVolume; + + if (m_volumeElement) { + g_object_set(G_OBJECT(m_volumeElement), "volume", newVolume, (const char*)NULL); + } + + emit volumeChanged(newVolume); +} + +bool AudioOutput::setOutputDevice(int newDevice) +{ + m_backend->logMessage(Q_FUNC_INFO + QString::number(newDevice), Backend::Info, this); + if (newDevice == m_device) + return true; + + if (root()) { + root()->saveState(); + if (gst_element_set_state(root()->pipeline(), GST_STATE_READY) == GST_STATE_CHANGE_FAILURE) + return false; + } + + bool success = false; + const QList<AudioDevice> deviceList = m_backend->deviceManager()->audioOutputDevices(); + if (m_audioSink && newDevice >= 0 && newDevice < deviceList.size()) { + // Save previous state + GstState oldState = GST_STATE(m_audioSink); + const QByteArray oldDeviceValue = GstHelper::property(m_audioSink, "device"); + const QByteArray deviceId = deviceList.at(newDevice).gstId; + m_device = newDevice; + + // We test if the device can be opened by checking if it can go from NULL to READY state + gst_element_set_state(m_audioSink, GST_STATE_NULL); + success = GstHelper::setProperty(m_audioSink, "device", deviceId); + if (success) { + success = (gst_element_set_state(m_audioSink, oldState) == GST_STATE_CHANGE_SUCCESS); + } + if (!success) { // Revert state + m_backend->logMessage(Q_FUNC_INFO + + QLatin1String(" Failed to change device ") + + deviceId, Backend::Info, this); + + GstHelper::setProperty(m_audioSink, "device", oldDeviceValue); + gst_element_set_state(m_audioSink, oldState); + } else { + m_backend->logMessage(Q_FUNC_INFO + + QLatin1String(" Successfully changed device ") + + deviceId, Backend::Info, this); + } + + // Note the stopped state should not really be neccessary, but seems to be required to + // properly reset after changing the audio state + if (root()) { + QMetaObject::invokeMethod(root(), "setState", Qt::QueuedConnection, Q_ARG(State, StoppedState)); + root()->resumeState(); + } + } + return success; +} + +#if (PHONON_VERSION >= PHONON_VERSION_CHECK(4, 2, 0)) +bool AudioOutput::setOutputDevice(const AudioOutputDevice &newDevice) +{ + m_backend->logMessage(Q_FUNC_INFO, Backend::Info, this); + if (!m_audioSink || !newDevice.isValid()) { + return false; + } + const QVariant driver = newDevice.property("driver"); + if (!driver.isValid()) { + return setOutputDevice(newDevice.index()); + } + if (newDevice.index() == m_device) { + return true; + } + + if (root()) { + root()->saveState(); + if (gst_element_set_state(root()->pipeline(), GST_STATE_READY) == GST_STATE_CHANGE_FAILURE) + return false; + } + + // Save previous state + const GstState oldState = GST_STATE(m_audioSink); + const QByteArray oldDeviceValue = GstHelper::property(m_audioSink, "device"); + + const QByteArray sinkName = GstHelper::property(m_audioSink, "name"); + if (sinkName == "alsasink" || sinkName == "alsasink2") { + if (driver.toByteArray() != "alsa") { + return false; + } + } + + const QVariant deviceIdsProperty = newDevice.property("deviceIds"); + QStringList deviceIds; + if (deviceIdsProperty.type() == QVariant::StringList) { + deviceIds = deviceIdsProperty.toStringList(); + } else if (deviceIdsProperty.type() == QVariant::String) { + deviceIds += deviceIdsProperty.toString(); + } + + // We test if the device can be opened by checking if it can go from NULL to READY state + foreach (const QString &deviceId, deviceIds) { + gst_element_set_state(m_audioSink, GST_STATE_NULL); + if (GstHelper::setProperty(m_audioSink, "device", deviceId.toUtf8())) { + m_backend->logMessage(Q_FUNC_INFO + QLatin1String("setProperty(device,") + + deviceId + QLatin1String(") succeeded"), Backend::Info, this); + if (gst_element_set_state(m_audioSink, oldState) == GST_STATE_CHANGE_SUCCESS) { + m_backend->logMessage(Q_FUNC_INFO + QLatin1String("go to old state on device") + + deviceId + QLatin1String(" succeeded"), Backend::Info, this); + m_device = newDevice.index(); + if (root()) { + QMetaObject::invokeMethod(root(), "setState", Qt::QueuedConnection, Q_ARG(State, StoppedState)); + root()->resumeState(); + } + return true; + } else { + m_backend->logMessage(Q_FUNC_INFO + QLatin1String("go to old state on device") + + deviceId + QLatin1String(" failed"), Backend::Info, this); + } + } else { + m_backend->logMessage(Q_FUNC_INFO + QLatin1String("setProperty(device,") + + deviceId + QLatin1String(") failed"), Backend::Info, this); + } + } + // Revert state + GstHelper::setProperty(m_audioSink, "device", oldDeviceValue); + gst_element_set_state(m_audioSink, oldState); + + if (root()) { + QMetaObject::invokeMethod(root(), "setState", Qt::QueuedConnection, Q_ARG(State, StoppedState)); + root()->resumeState(); + } + + return false; +} +#endif + +} +} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE +#include "moc_audiooutput.cpp" diff --git a/src/3rdparty/phonon/gstreamer/audiooutput.h b/src/3rdparty/phonon/gstreamer/audiooutput.h new file mode 100644 index 0000000..846fa7b --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/audiooutput.h @@ -0,0 +1,82 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + Copyright (C) 2008 Matthias Kretz <kretz@kde.org> + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_GSTREAMER_AUDIOOUTPUT_H +#define Phonon_GSTREAMER_AUDIOOUTPUT_H + +#include "common.h" +#include "medianode.h" + +#include <phonon/audiooutputinterface.h> +#include <phonon/phononnamespace.h> + +#include <QtCore/QFile> + +#include <gst/gst.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ +class AudioOutput : public QObject, public AudioOutputInterface, public MediaNode +{ + Q_OBJECT + Q_INTERFACES(Phonon::AudioOutputInterface Phonon::Gstreamer::MediaNode) +public: + AudioOutput(Backend *backend, QObject *parent); + ~AudioOutput(); + + qreal volume() const; + int outputDevice() const; + void setVolume(qreal newVolume); + bool setOutputDevice(int newDevice); +#if (PHONON_VERSION >= PHONON_VERSION_CHECK(4, 2, 0)) + bool setOutputDevice(const AudioOutputDevice &newDevice); +#endif + +public: + GstElement *audioElement() + { + Q_ASSERT(m_audioBin); + return m_audioBin; + } + + void mediaNodeEvent(const MediaNodeEvent *event); + +Q_SIGNALS: + void volumeChanged(qreal newVolume); + void audioDeviceFailed(); + +private: + + qreal m_volumeLevel; + int m_device; + + GstElement *m_volumeElement; + GstElement *m_audioBin; + GstElement *m_audioSink; + GstElement *m_conv; +}; +} +} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE + +#endif // Phonon_GSTREAMER_AUDIOOUTPUT_H diff --git a/src/3rdparty/phonon/gstreamer/backend.cpp b/src/3rdparty/phonon/gstreamer/backend.cpp new file mode 100644 index 0000000..d05f6a6 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/backend.cpp @@ -0,0 +1,455 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "common.h" +#include "backend.h" +#include "audiooutput.h" +#include "audioeffect.h" +#include "mediaobject.h" +#include "videowidget.h" +#include "devicemanager.h" +#include "effectmanager.h" +#include "message.h" +#include "volumefadereffect.h" +#include <gst/interfaces/propertyprobe.h> + +#include <QtCore/QSet> +#include <QtCore/QVariant> +#include <QtCore/QtPlugin> + +QT_BEGIN_NAMESPACE + +Q_EXPORT_PLUGIN2(phonon_gstreamer, Phonon::Gstreamer::Backend) + +namespace Phonon +{ +namespace Gstreamer +{ + +class MediaNode; + +Backend::Backend(QObject *parent, const QVariantList &) + : QObject(parent) + , m_deviceManager(0) + , m_effectManager(0) + , m_debugLevel(Warning) + , m_isValid(false) +{ + GError *err = 0; + bool wasInit = gst_init_check(0, 0, &err); //init gstreamer: must be called before any gst-related functions + if (err) + g_error_free(err); + + qRegisterMetaType<Message>("Message"); + + setProperty("identifier", QLatin1String("phonon_gstreamer")); + setProperty("backendName", QLatin1String("Gstreamer")); + setProperty("backendComment", QLatin1String("Gstreamer plugin for Phonon")); + setProperty("backendVersion", QLatin1String("0.2")); + setProperty("backendWebsite", QLatin1String("http://qtsoftware.com/")); + + //check if we should enable debug output + QString debugLevelString = qgetenv("PHONON_GST_DEBUG"); + int debugLevel = debugLevelString.toInt(); + if (debugLevel > 3) //3 is maximum + debugLevel = 3; + m_debugLevel = (DebugLevel)debugLevel; + + if (wasInit) { + m_isValid = checkDependencies(); + gchar *versionString = gst_version_string(); + logMessage(QString("Using %0").arg(versionString)); + g_free(versionString); + } + if (!m_isValid) + qWarning("Phonon::GStreamer::Backend: Failed to initialize GStreamer"); + + m_deviceManager = new DeviceManager(this); + m_effectManager = new EffectManager(this); +} + +Backend::~Backend() +{ + gst_deinit(); +} + +gboolean Backend::busCall(GstBus *bus, GstMessage *msg, gpointer data) +{ + Q_UNUSED(bus); + Q_ASSERT(msg); + + MediaObject *mediaObject = static_cast<MediaObject*>(data); + Q_ASSERT(mediaObject); + + Message message(msg, mediaObject); + QMetaObject::invokeMethod(mediaObject->backend(), "handleBusMessage", Qt::QueuedConnection, Q_ARG(Message, message)); + + return true; +} + +/*** + * !reimp + */ +QObject *Backend::createObject(BackendInterface::Class c, QObject *parent, const QList<QVariant> &args) +{ + // Return nothing if dependencies are not met + + switch (c) { + case MediaObjectClass: + return new MediaObject(this, parent); + + case AudioOutputClass: { + AudioOutput *ao = new AudioOutput(this, parent); + m_audioOutputs.append(ao); + return ao; + } + case EffectClass: + return new AudioEffect(this, args[0].toInt(), parent); + + case AudioDataOutputClass: + logMessage("createObject() : AudioDataOutput not implemented"); + break; + + case VideoDataOutputClass: + logMessage("createObject() : VideoDataOutput not implemented"); + break; + + case VideoWidgetClass: { + QWidget *widget = qobject_cast<QWidget*>(parent); + return new VideoWidget(this, widget); + } + + case VolumeFaderEffectClass: + return new VolumeFaderEffect(this, parent); + + case VisualizationClass: //Fall through + default: + logMessage("createObject() : Backend object not available"); + } + return 0; +} + +// Returns true if all dependencies are met +// and gstreamer is usable, otherwise false +bool Backend::isValid() const +{ + return m_isValid; +} + +bool Backend::supportsVideo() const +{ + return isValid(); +} + +bool Backend::checkDependencies() const +{ + bool success = false; + // Verify that gst-plugins-base is installed + GstElementFactory *acFactory = gst_element_factory_find ("audioconvert"); + if (acFactory) { + gst_object_unref(acFactory); + success = true; + // Check if gst-plugins-good is installed + GstElementFactory *csFactory = gst_element_factory_find ("videobalance"); + if (csFactory) { + gst_object_unref(csFactory); + } else { + QString message = tr("Warning: You do not seem to have the package gstreamer0.10-plugins-good installed.\n" + " Some video features have been disabled."); + qDebug() << message; + } + } else { + qWarning() << tr("Warning: You do not seem to have the base GStreamer plugins installed.\n" + " All audio and video support has been disabled"); + } + return success; +} + +/*** + * !reimp + */ +QStringList Backend::availableMimeTypes() const +{ + QStringList availableMimeTypes; + + if (!isValid()) + return availableMimeTypes; + + GstElementFactory *mpegFactory; + // Add mp3 as a separate mime type as people are likely to look for it. + if ((mpegFactory = gst_element_factory_find ("ffmpeg")) || + (mpegFactory = gst_element_factory_find ("mad"))) { + availableMimeTypes << QLatin1String("audio/x-mp3"); + gst_object_unref(GST_OBJECT(mpegFactory)); + } + + // Iterate over all audio and video decoders and extract mime types from sink caps + GList* factoryList = gst_registry_get_feature_list(gst_registry_get_default (), GST_TYPE_ELEMENT_FACTORY); + for (GList* iter = g_list_first(factoryList) ; iter != NULL ; iter = g_list_next(iter)) { + GstPluginFeature *feature = GST_PLUGIN_FEATURE(iter->data); + QString klass = gst_element_factory_get_klass(GST_ELEMENT_FACTORY(feature)); + + if (klass == QLatin1String("Codec/Decoder/Audio") || + klass == QLatin1String("Codec/Decoder/Video")) { + + const GList *static_templates; + GstElementFactory *factory = GST_ELEMENT_FACTORY(feature); + static_templates = gst_element_factory_get_static_pad_templates(factory); + + for (; static_templates != NULL ; static_templates = static_templates->next) { + GstStaticPadTemplate *padTemplate = (GstStaticPadTemplate *) static_templates->data; + if (padTemplate && padTemplate->direction == GST_PAD_SINK) { + GstCaps *caps = gst_static_pad_template_get_caps (padTemplate); + + if (caps) { + const GstStructure* capsStruct = gst_caps_get_structure (caps, 0); + QString mime = QString::fromUtf8(gst_structure_get_name (capsStruct)); + if (!availableMimeTypes.contains(mime)) + availableMimeTypes.append(mime); + } + } + } + } + } + g_list_free(factoryList); + availableMimeTypes.sort(); + return availableMimeTypes; +} + +/*** + * !reimp + */ +QList<int> Backend::objectDescriptionIndexes(ObjectDescriptionType type) const +{ + QList<int> list; + + if (!isValid()) + return list; + + switch (type) { + case Phonon::AudioOutputDeviceType: { + QList<AudioDevice> deviceList = deviceManager()->audioOutputDevices(); + for (int dev = 0 ; dev < deviceList.size() ; ++dev) + list.append(deviceList[dev].id); + break; + } + break; + + case Phonon::EffectType: { + QList<EffectInfo*> effectList = effectManager()->audioEffects(); + for (int eff = 0 ; eff < effectList.size() ; ++eff) + list.append(eff); + break; + } + break; + default: + break; + } + return list; +} + +/*** + * !reimp + */ +QHash<QByteArray, QVariant> Backend::objectDescriptionProperties(ObjectDescriptionType type, int index) const +{ + + QHash<QByteArray, QVariant> ret; + + if (!isValid()) + return ret; + + switch (type) { + case Phonon::AudioOutputDeviceType: { + QList<AudioDevice> audioDevices = deviceManager()->audioOutputDevices(); + if (index >= 0 && index < audioDevices.size()) { + ret.insert("name", audioDevices[index].gstId); + ret.insert("description", audioDevices[index].description); + ret.insert("icon", QLatin1String("audio-card")); + } + } + break; + + case Phonon::EffectType: { + QList<EffectInfo*> effectList = effectManager()->audioEffects(); + if (index >= 0 && index <= effectList.size()) { + const EffectInfo *effect = effectList[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 + } + default: + break; + } + return ret; +} + +/*** + * !reimp + */ +bool Backend::startConnectionChange(QSet<QObject *> objects) +{ + foreach (QObject *object, objects) { + MediaNode *sourceNode = qobject_cast<MediaNode *>(object); + MediaObject *media = sourceNode->root(); + if (media) { + media->saveState(); + return true; + } + } + return true; +} + +/*** + * !reimp + */ +bool Backend::connectNodes(QObject *source, QObject *sink) +{ + if (isValid()) { + MediaNode *sourceNode = qobject_cast<MediaNode *>(source); + MediaNode *sinkNode = qobject_cast<MediaNode *>(sink); + if (sourceNode && sinkNode) { + if (sourceNode->connectNode(sink)) { + sourceNode->root()->invalidateGraph(); + logMessage(QString("Backend connected %0 to %1").arg(source->metaObject()->className()).arg(sink->metaObject()->className())); + return true; + } + } + } + logMessage(QString("Linking %0 to %1 failed").arg(source->metaObject()->className()).arg(sink->metaObject()->className()), Warning); + return false; +} + +/*** + * !reimp + */ +bool Backend::disconnectNodes(QObject *source, QObject *sink) +{ + MediaNode *sourceNode = qobject_cast<MediaNode *>(source); + MediaNode *sinkNode = qobject_cast<MediaNode *>(sink); + + if (sourceNode && sinkNode) + return sourceNode->disconnectNode(sink); + else + return false; +} + +/*** + * !reimp + */ +bool Backend::endConnectionChange(QSet<QObject *> objects) +{ + foreach (QObject *object, objects) { + MediaNode *sourceNode = qobject_cast<MediaNode *>(object); + MediaObject *media = sourceNode->root(); + if (media) { + media->resumeState(); + return true; + } + } + return true; +} + +/*** + * Request bus messages for this mediaobject + */ +void Backend::addBusWatcher(MediaObject* node) +{ + Q_ASSERT(node); + GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE(node->pipeline())); + gst_bus_add_watch (bus, busCall, node); + gst_object_unref(bus); +} + +/*** + * Ignore bus messages for this mediaobject + */ +void Backend::removeBusWatcher(MediaObject* node) +{ + Q_ASSERT(node); + g_source_remove_by_user_data(node); +} + +/*** + * Polls each mediaobject's pipeline and delivers + * pending any pending messages + */ +void Backend::handleBusMessage(Message message) +{ + MediaObject *mediaObject = message.source(); + mediaObject->handleBusMessage(message); +} + +DeviceManager* Backend::deviceManager() const +{ + return m_deviceManager; +} + +EffectManager* Backend::effectManager() const +{ + return m_effectManager; +} + +/** + * Returns a debuglevel that is determined by the + * PHONON_GSTREAMER_DEBUG environment variable. + * + * Warning - important warnings + * Info - general info + * Debug - gives extra info + */ +Backend::DebugLevel Backend::debugLevel() const +{ + return m_debugLevel; +} + +/*** + * Prints a conditional debug message based on the current debug level + * If obj is provided, classname and objectname will be printed as well + * + * see debugLevel() + */ +void Backend::logMessage(const QString &message, int priority, QObject *obj) const +{ + if (debugLevel() > 0) { + QString output; + if (obj) { + // Strip away namespace from className + QString className(obj->metaObject()->className()); + int nameLength = className.length() - className.lastIndexOf(':') - 1; + className = className.right(nameLength); + output.sprintf("%s %s (%s %p)", message.toLatin1().constData(), + obj->objectName().toLatin1().constData(), + className.toLatin1().constData(), obj); + } + else { + output = message; + } + if (priority <= (int)debugLevel()) { + qDebug() << QString("PGST(%1): %2").arg(priority).arg(output); + } + } +} + +} +} + +QT_END_NAMESPACE + +#include "moc_backend.cpp" diff --git a/src/3rdparty/phonon/gstreamer/backend.h b/src/3rdparty/phonon/gstreamer/backend.h new file mode 100644 index 0000000..2aab6fa --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/backend.h @@ -0,0 +1,101 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_GSTREAMER_BACKEND_H +#define Phonon_GSTREAMER_BACKEND_H + +#include "common.h" +#include "devicemanager.h" +#include "medianode.h" + +#include <phonon/objectdescription.h> +#include <phonon/backendinterface.h> + +#include <QtCore/QList> +#include <QtCore/QPointer> +#include <QtCore/QStringList> +#include <QtCore/QTimer> + +#include <gst/gst.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ +class AudioOutput; +class MediaNode; +class MediaObject; +class EffectManager; + +class Backend : public QObject, public BackendInterface +{ + Q_OBJECT + Q_INTERFACES(Phonon::BackendInterface) + +public: + + enum DebugLevel {NoDebug, Warning, Info, Debug}; + Backend(QObject *parent = 0, const QVariantList & = QVariantList()); + virtual ~Backend(); + + DeviceManager* deviceManager() const; + EffectManager* effectManager() const; + + QObject *createObject(BackendInterface::Class, QObject *parent, const QList<QVariant> &args); + + bool isValid() const; + bool supportsVideo() const; + QStringList availableMimeTypes() const; + + QList<int> objectDescriptionIndexes(ObjectDescriptionType type) const; + QHash<QByteArray, QVariant> objectDescriptionProperties(ObjectDescriptionType type, int index) const; + + bool startConnectionChange(QSet<QObject *>); + bool connectNodes(QObject *, QObject *); + bool disconnectNodes(QObject *, QObject *); + bool endConnectionChange(QSet<QObject *>); + + DebugLevel debugLevel() const; + + void addBusWatcher(MediaObject* node); + void removeBusWatcher(MediaObject* node); + void logMessage(const QString &message, int priority = 2, QObject *obj=0) const; + bool checkDependencies() const; + +Q_SIGNALS: + void objectDescriptionChanged(ObjectDescriptionType); + +private Q_SLOTS: + void handleBusMessage(Message); + +private: + static gboolean busCall(GstBus *bus, GstMessage *msg, gpointer data); + QList<QPointer<AudioOutput> > m_audioOutputs; + + DeviceManager *m_deviceManager; + EffectManager *m_effectManager; + DebugLevel m_debugLevel; + bool m_isValid; +}; +} +} // namespace Phonon::Gstreamer + +QT_END_NAMESPACE + +#endif // Phonon_GSTREAMER_BACKEND_H diff --git a/src/3rdparty/phonon/gstreamer/common.h b/src/3rdparty/phonon/gstreamer/common.h new file mode 100644 index 0000000..355df50 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/common.h @@ -0,0 +1,51 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_GSTREAMER_COMMON_H +#define Phonon_GSTREAMER_COMMON_H + +#include <phonon/phonon_export.h> + +#include <QtCore/QDebug> + +#ifdef DEBUG_IMPLEMENTED +#define IMPLEMENTED qDebug() << "PGst:" << __FUNCTION__ << "(" << __FILE__ << "):" +#else +#define IMPLEMENTED if (1); else qDebug() +#endif + +#ifdef DEBUG_HALF_IMPLEMENTED +#define HALF_IMPLEMENTED qDebug() << "PGst: --- HALF IMPLEMENTED:" << __FUNCTION__ << "(" << __FILE__ << "):" +#else +#define HALF_IMPLEMENTED if (1); else qDebug() +#endif + +#ifdef DEBUG_NOT_IMPLEMENTED +#define NOT_IMPLEMENTED qDebug() << "PGst: *** NOT IMPLEMENTED:" << __FUNCTION__ << "(" << __FILE__ << "):" +#else +#define NOT_IMPLEMENTED if (1); else qDebug() +#endif + +#ifdef DEBUG_IMPLEMENTED_SILENT +#define IMPLEMENTED_SILENT qDebug() << "PGst: (silent)" << __FUNCTION__ << "(" << __FILE__ << "):" +#else +#define IMPLEMENTED_SILENT if (1); else qDebug() +#endif + +#define BACKEND_WARNING qDebug() << "PGst: ____ WARNING ____" << Q_FUNC_INFO << ":" + +#endif // Phonon_GSTREAMER_COMMON_H diff --git a/src/3rdparty/phonon/gstreamer/devicemanager.cpp b/src/3rdparty/phonon/gstreamer/devicemanager.cpp new file mode 100644 index 0000000..2240396 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/devicemanager.cpp @@ -0,0 +1,357 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include <gst/interfaces/propertyprobe.h> +#include "devicemanager.h" +#include "backend.h" +#include "gsthelper.h" +#include "videowidget.h" +#include "glrenderer.h" +#include "widgetrenderer.h" +#include "x11renderer.h" +#include "artssink.h" + +#ifdef USE_ALSASINK2 +#include "alsasink2.h" +#endif + +/* + * This class manages the list of currently + * active output devices + */ + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ + +AudioDevice::AudioDevice(DeviceManager *manager, const QByteArray &gstId) + : gstId(gstId) +{ + //get an id + static int counter = 0; + id = counter++; + //get name from device + if (gstId == "default") { + description = "Default audio device"; + } else { + GstElement *aSink= manager->createAudioSink(); + + if (aSink) { + gchar *deviceDescription = NULL; + + if (GST_IS_PROPERTY_PROBE(aSink) && gst_property_probe_get_property( GST_PROPERTY_PROBE(aSink), "device" ) ) { + g_object_set (G_OBJECT(aSink), "device", gstId.constData(), (const char*)NULL); + g_object_get (G_OBJECT(aSink), "device-name", &deviceDescription, (const char*)NULL); + description = QByteArray(deviceDescription); + g_free (deviceDescription); + gst_element_set_state(aSink, GST_STATE_NULL); + gst_object_unref (aSink); + } + } + } +} + +DeviceManager::DeviceManager(Backend *backend) + : QObject(backend) + , m_backend(backend) +{ + QSettings settings(QLatin1String("Trolltech")); + settings.beginGroup(QLatin1String("Qt")); + + m_audioSink = qgetenv("PHONON_GST_AUDIOSINK"); + if (m_audioSink.isEmpty()) { + m_audioSink = settings.value(QLatin1String("audiosink"), "Auto").toByteArray().toLower(); + } + + m_videoSinkWidget = qgetenv("PHONON_GST_VIDEOMODE"); + if (m_videoSinkWidget.isEmpty()) { + m_videoSinkWidget = settings.value(QLatin1String("videomode"), "Auto").toByteArray().toLower(); + } + + if (m_backend->isValid()) + updateDeviceList(); +} + +DeviceManager::~DeviceManager() +{ + m_audioDeviceList.clear(); +} + +/*** +* Returns a Gst Audiosink based on GNOME configuration settings, +* or 0 if the element is not available. +*/ +GstElement *DeviceManager::createGNOMEAudioSink(Category category) +{ + GstElement *sink = gst_element_factory_make ("gconfaudiosink", NULL); + + if (sink) { + + // set profile property on the gconfaudiosink to "music and movies" + if (g_object_class_find_property (G_OBJECT_GET_CLASS (sink), "profile")) { + switch (category) { + case NotificationCategory: + g_object_set (G_OBJECT (sink), "profile", 0, (const char*)NULL); // 0 = 'sounds' + break; + case CommunicationCategory: + g_object_set (G_OBJECT (sink), "profile", 2, (const char*)NULL); // 2 = 'chat' + break; + default: + g_object_set (G_OBJECT (sink), "profile", 1, (const char*)NULL); // 1 = 'music and movies' + break; + } + } + } + return sink; +} + + +bool DeviceManager::canOpenDevice(GstElement *element) const +{ + if (!element) + return false; + + if (gst_element_set_state(element, GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS) + return true; + + const QList<QByteArray> &list = GstHelper::extractProperties(element, "device"); + foreach (const QByteArray &gstId, list) { + GstHelper::setProperty(element, "device", gstId); + if (gst_element_set_state(element, GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS) { + return true; + } + } + // FIXME: the above can still fail for a valid alsasink because list only contains entries of + // the form "hw:X,Y". Would be better to use "default:X" or "dmix:X,Y" + + gst_element_set_state(element, GST_STATE_NULL); + return false; +} + +/* +* +* Returns a GstElement with a valid audio sink +* based on the current value of PHONON_GSTREAMER_DRIVER +* +* Allowed values are auto (default), alsa, oss, arts and ess +* does not exist +* +* If no real sound sink is available a fakesink will be returned +*/ +GstElement *DeviceManager::createAudioSink(Category category) +{ + GstElement *sink = 0; + + if (m_backend && m_backend->isValid()) + { + if (m_audioSink == "auto") //this is the default value + { + //### TODO : get equivalent KDE settings here + + if (!qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty()) { + sink = createGNOMEAudioSink(category); + if (canOpenDevice(sink)) + m_backend->logMessage("AudioOutput using gconf audio sink"); + else if (sink) { + gst_object_unref(sink); + sink = 0; + } + } + +#ifdef USE_ALSASINK2 + if (!sink) { + sink = gst_element_factory_make ("_k_alsasink", NULL); + if (canOpenDevice(sink)) + m_backend->logMessage("AudioOutput using alsa2 audio sink"); + else if (sink) { + gst_object_unref(sink); + sink = 0; + } + } +#endif + + if (!sink) { + sink = gst_element_factory_make ("alsasink", NULL); + if (canOpenDevice(sink)) + m_backend->logMessage("AudioOutput using alsa audio sink"); + else if (sink) { + gst_object_unref(sink); + sink = 0; + } + } + + if (!sink) { + sink = gst_element_factory_make ("autoaudiosink", NULL); + if (canOpenDevice(sink)) + m_backend->logMessage("AudioOutput using auto audio sink"); + else if (sink) { + gst_object_unref(sink); + sink = 0; + } + } + + if (!sink) { + sink = gst_element_factory_make ("osssink", NULL); + if (canOpenDevice(sink)) + m_backend->logMessage("AudioOutput using oss audio sink"); + else if (sink) { + gst_object_unref(sink); + sink = 0; + } + } + } else if (m_audioSink == "fake") { + //do nothing as a fakesink will be created by default + } else if (m_audioSink == "artssink") { + sink = GST_ELEMENT(g_object_new(arts_sink_get_type(), NULL)); + } else if (!m_audioSink.isEmpty()) { //Use a custom sink + sink = gst_element_factory_make (m_audioSink, NULL); + if (canOpenDevice(sink)) + m_backend->logMessage(QString("AudioOutput using %0").arg(QString::fromUtf8(m_audioSink))); + else if (sink) { + gst_object_unref(sink); + sink = 0; + } + } + } + + if (!sink) { //no suitable sink found so we'll make a fake one + sink = gst_element_factory_make("fakesink", NULL); + if (sink) { + m_backend->logMessage("AudioOutput Using fake audio sink"); + //without sync the sink will pull the pipeline as fast as the CPU allows + g_object_set (G_OBJECT (sink), "sync", TRUE, (const char*)NULL); + } + } + Q_ASSERT(sink); + return sink; +} + +AbstractRenderer *DeviceManager::createVideoRenderer(VideoWidget *parent) +{ +#if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES) + if (m_videoSinkWidget == "opengl") { + return new GLRenderer(parent); + } else +#endif + if (m_videoSinkWidget == "software") { + return new WidgetRenderer(parent); + } +#ifndef Q_WS_QWS + else if (m_videoSinkWidget == "xwindow") { + return new X11Renderer(parent); + } else { + GstElementFactory *srcfactory = gst_element_factory_find("ximagesink"); + if (srcfactory) { + return new X11Renderer(parent); + } + } +#endif + return new WidgetRenderer(parent); +} + +/* + * Returns a positive device id or -1 if device + * does not exist + * + * The gstId is typically in the format hw:1,0 + */ +int DeviceManager::deviceId(const QByteArray &gstId) const +{ + for (int i = 0 ; i < m_audioDeviceList.size() ; ++i) { + if (m_audioDeviceList[i].gstId == gstId) { + return m_audioDeviceList[i].id; + } + } + return -1; +} + +/** + * Get a human-readable description from a device id + */ +QByteArray DeviceManager::deviceDescription(int id) const +{ + for (int i = 0 ; i < m_audioDeviceList.size() ; ++i) { + if (m_audioDeviceList[i].id == id) { + return m_audioDeviceList[i].description; + } + } + return QByteArray(); +} + +/** + * Updates the current list of active devices + */ +void DeviceManager::updateDeviceList() +{ + //fetch list of current devices + GstElement *audioSink= createAudioSink(); + + QList<QByteArray> list; + + if (audioSink) { + list = GstHelper::extractProperties(audioSink, "device"); + list.prepend("default"); + + for (int i = 0 ; i < list.size() ; ++i) { + QByteArray gstId = list.at(i); + if (deviceId(gstId) == -1) { + // This is a new device, add it + m_audioDeviceList.append(AudioDevice(this, gstId)); + emit deviceAdded(deviceId(gstId)); + m_backend->logMessage(QString("Found new audio device %0").arg(QString::fromUtf8(gstId)), Backend::Debug, this); + } + } + + if (list.size() < m_audioDeviceList.size()) { + //a device was removed + for (int i = m_audioDeviceList.size() -1 ; i >= 0 ; --i) { + QByteArray currId = m_audioDeviceList[i].gstId; + bool found = false; + for (int k = list.size() -1 ; k >= 0 ; --k) { + if (currId == list[k]) { + found = true; + break; + } + } + if (!found) { + m_backend->logMessage(QString("Audio device lost %0").arg(QString::fromUtf8(currId)), Backend::Debug, this); + emit deviceRemoved(deviceId(currId)); + m_audioDeviceList.removeAt(i); + } + } + } + } + + gst_element_set_state (audioSink, GST_STATE_NULL); + gst_object_unref (audioSink); +} + +/** + * Returns a list of hardware id usable by gstreamer [i.e hw:1,0] + */ +const QList<AudioDevice> DeviceManager::audioOutputDevices() const +{ + return m_audioDeviceList; +} + +} +} + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/gstreamer/devicemanager.h b/src/3rdparty/phonon/gstreamer/devicemanager.h new file mode 100644 index 0000000..a5e8289 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/devicemanager.h @@ -0,0 +1,80 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_GSTREAMER_DEVICEMANAGER_H +#define Phonon_GSTREAMER_DEVICEMANAGER_H + +#include "common.h" + +#include <phonon/audiooutputinterface.h> + +#include <QtCore/QObject> +#include <QtCore/QTimer> + +#include <gst/gst.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon { +namespace Gstreamer { +class Backend; +class DeviceManager; +class AbstractRenderer; +class VideoWidget; + +class AudioDevice { +public : + AudioDevice(DeviceManager *s, const QByteArray &deviceId); + int id; + QByteArray gstId; + QByteArray description; +}; + +class DeviceManager : public QObject { + Q_OBJECT +public: + DeviceManager(Backend *parent); + virtual ~DeviceManager(); + const QList<AudioDevice> audioOutputDevices() const; + GstPad *requestPad(int device) const; + int deviceId(const QByteArray &gstId) const; + QByteArray deviceDescription(int id) const; + GstElement *createGNOMEAudioSink(Category category); + GstElement *createAudioSink(Category category = NoCategory); + AbstractRenderer *createVideoRenderer(VideoWidget *parent); + +signals: + void deviceAdded(int); + void deviceRemoved(int); + +public slots: + void updateDeviceList(); + +private: + bool canOpenDevice(GstElement *element) const; + Backend *m_backend; + QList <AudioDevice> m_audioDeviceList; + QTimer m_devicePollTimer; + QByteArray m_audioSink; + QByteArray m_videoSinkWidget; +}; +} +} // namespace Phonon::Gstreamer + +QT_END_NAMESPACE + +#endif // Phonon_GSTREAMER_DEVICEMANAGER_H diff --git a/src/3rdparty/phonon/gstreamer/effect.cpp b/src/3rdparty/phonon/gstreamer/effect.cpp new file mode 100644 index 0000000..f653535 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/effect.cpp @@ -0,0 +1,246 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "effect.h" +#include "common.h" +#include "audiooutput.h" +#include "backend.h" +#include "medianode.h" +#include "effectmanager.h" +#include "gsthelper.h" + +#include <gst/gst.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ +Effect::Effect(Backend *backend, QObject *parent, NodeDescription description) + : QObject(parent), + MediaNode(backend, description) + , m_effectBin(0) + , m_effectElement(0) +{ +} + +void Effect::init() +{ + m_effectBin = createEffectBin(); + if (m_effectBin) { + setupEffectParams(); + gst_object_ref (GST_OBJECT (m_effectBin)); // Take ownership + gst_object_sink (GST_OBJECT (m_effectBin)); + m_isValid = true; + } +} + +Effect::~Effect() +{ + if (m_effectBin) { + gst_element_set_state (m_effectBin, GST_STATE_NULL); + gst_object_unref (m_effectBin); + } +} + +void Effect::setupEffectParams() +{ + + Q_ASSERT(m_effectElement); + + //query and store parameters + if (m_effectElement) { + GParamSpec **property_specs; + guint propertyCount, i; + property_specs = g_object_class_list_properties(G_OBJECT_GET_CLASS (m_effectElement), &propertyCount); + for (i = 0; i < propertyCount; i++) { + GParamSpec *param = property_specs[i]; + if (param->flags & G_PARAM_WRITABLE) { + QString propertyName = g_param_spec_get_name (param); + + // These properties should not be exposed to the front-end + if (propertyName == "qos" || propertyName == "name" || propertyName == "async-handling") + continue; + + switch(param->value_type) { + case G_TYPE_UINT: + m_parameterList.append(Phonon::EffectParameter(i, propertyName, + 0, //hints + G_PARAM_SPEC_UINT(param)->default_value, + G_PARAM_SPEC_UINT(param)->minimum, + G_PARAM_SPEC_UINT(param)->maximum)); + break; + + case G_TYPE_STRING: + m_parameterList.append(Phonon::EffectParameter(i, propertyName, + 0, //hints + G_PARAM_SPEC_STRING(param)->default_value, + 0, + 0)); + break; + + case G_TYPE_INT: + m_parameterList.append(Phonon::EffectParameter(i, propertyName, + EffectParameter::IntegerHint, //hints + QVariant(G_PARAM_SPEC_INT(param)->default_value), + QVariant(G_PARAM_SPEC_INT(param)->minimum), + QVariant(G_PARAM_SPEC_INT(param)->maximum))); + break; + + case G_TYPE_FLOAT: + m_parameterList.append(Phonon::EffectParameter(i, propertyName, + 0, //hints + QVariant((double)G_PARAM_SPEC_FLOAT(param)->default_value), + QVariant((double)G_PARAM_SPEC_FLOAT(param)->minimum), + QVariant((double)G_PARAM_SPEC_FLOAT(param)->maximum))); + break; + + case G_TYPE_DOUBLE: + m_parameterList.append(Phonon::EffectParameter(i, propertyName, + 0, //hints + QVariant(G_PARAM_SPEC_DOUBLE(param)->default_value), + QVariant(G_PARAM_SPEC_DOUBLE(param)->minimum), + QVariant(G_PARAM_SPEC_DOUBLE(param)->maximum))); + break; + + case G_TYPE_BOOLEAN: + m_parameterList.append(Phonon::EffectParameter(i, propertyName, + Phonon::EffectParameter::ToggledHint, //hints + QVariant((bool)G_PARAM_SPEC_BOOLEAN(param)->default_value), + QVariant((bool)false), QVariant((bool)true))); + break; + + default: + break; + } + } + } + } +} + +QList<Phonon::EffectParameter> Effect::parameters() const +{ + return m_parameterList; +} + +QVariant Effect::parameterValue(const EffectParameter &p) const +{ + + Q_ASSERT(m_effectElement); + + QVariant returnVal; + switch (p.type()) { + case QVariant::Int: + { + gint val = 0; + g_object_get(G_OBJECT(m_effectElement), qPrintable(p.name()), &val, (const char*)NULL); + returnVal = val; + } + break; + + case QVariant::Bool: + { + gboolean val = 0; + g_object_get(G_OBJECT(m_effectElement), qPrintable(p.name()), &val, (const char*)NULL); + returnVal = val; + } + break; + + case QVariant::String: + { + gchar *val = 0; + g_object_get(G_OBJECT(m_effectElement), qPrintable(p.name()), &val, (const char*)NULL); + returnVal = QString::fromUtf8(val); + g_free(val); + } + break; + + case QVariant::Double: + { + GParamSpec* spec = g_object_class_find_property(G_OBJECT_GET_CLASS(m_effectElement), p.name().toLatin1().constData()); + Q_ASSERT(spec); + if (spec && spec->value_type == G_TYPE_FLOAT) { + gfloat val = 0; + g_object_get(G_OBJECT(m_effectElement), qPrintable(p.name()), &val, (const char*)NULL); + returnVal = QVariant((float)val); + } else { + gdouble val = 0; + g_object_get(G_OBJECT(m_effectElement), qPrintable(p.name()), &val, (const char*)NULL); + returnVal = QVariant((float)val); + } + } + break; + + default: + Q_ASSERT(0); //not a supported variant type + } + return returnVal; +} + + +void Effect::setParameterValue(const EffectParameter &p, const QVariant &v) +{ + Q_ASSERT(m_effectElement); + + // Note that the frontend currently calls this after creation with a null-value + // for all parameters. + + if (v.isValid()) { + + switch (p.type()) { + // ### range values should really be checked by the front end, why isnt it working? + case QVariant::Int: + if (v.toInt() >= p.minimumValue().toInt() && v.toInt() <= p.maximumValue().toInt()) + g_object_set(G_OBJECT(m_effectElement), qPrintable(p.name()), (gint)v.toInt(), (const char*)NULL); + break; + + case QVariant::Double: + if (v.toDouble() >= p.minimumValue().toDouble() && v.toDouble() <= p.maximumValue().toDouble()) { + GParamSpec* spec = g_object_class_find_property(G_OBJECT_GET_CLASS(m_effectElement), p.name().toLatin1().constData()); + Q_ASSERT(spec); + if (spec && spec->value_type == G_TYPE_FLOAT) + g_object_set(G_OBJECT(m_effectElement), qPrintable(p.name()), (gfloat)v.toDouble(), (const char*)NULL); + else + g_object_set(G_OBJECT(m_effectElement), qPrintable(p.name()), (gdouble)v.toDouble(), (const char*)NULL); + } + break; + + case QVariant::UInt: + if (v.toUInt() >= p.minimumValue().toUInt() && v.toUInt() <= p.maximumValue().toUInt()) + g_object_set(G_OBJECT(m_effectElement), qPrintable(p.name()), v.toUInt(), (const char*)NULL); + break; + + case QVariant::String: + g_object_set(G_OBJECT(m_effectElement), qPrintable(p.name()), v.toString().toUtf8().constData(), (const char*)NULL); + break; + + case QVariant::Bool: + g_object_set(G_OBJECT(m_effectElement), qPrintable(p.name()), (gboolean)v.toBool(), (const char*)NULL); + break; + + default: + Q_ASSERT(0); //not a supported variant type + } + } +} + +} +} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE +#include "moc_effect.cpp" diff --git a/src/3rdparty/phonon/gstreamer/effect.h b/src/3rdparty/phonon/gstreamer/effect.h new file mode 100644 index 0000000..dbbb457 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/effect.h @@ -0,0 +1,64 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_GSTREAMER_EFFECT_H +#define Phonon_GSTREAMER_EFFECT_H + +#include "common.h" +#include "medianode.h" + +#include <phonon/effectparameter.h> +#include <phonon/effectinterface.h> + +#include <QtCore/QObject> + +#include <gst/gst.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ + class EffectInfo; + + class Effect : public QObject, public Phonon::EffectInterface, public MediaNode + { + Q_OBJECT + Q_INTERFACES(Phonon::EffectInterface Phonon::Gstreamer::MediaNode) + public: + Effect (Backend *backend, QObject *parent, NodeDescription description); + virtual ~Effect (); + + virtual QList<EffectParameter> parameters() const; + virtual QVariant parameterValue(const EffectParameter &) const; + virtual void setParameterValue(const EffectParameter &, const QVariant &); + + virtual GstElement* createEffectBin() = 0; + virtual void init(); + virtual void setupEffectParams(); + + protected: + GstElement *m_effectBin; + GstElement *m_effectElement; + QList<Phonon::EffectParameter> m_parameterList; + }; +}} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE + +#endif // Phonon_GSTREAMER_EFFECT_H diff --git a/src/3rdparty/phonon/gstreamer/effectmanager.cpp b/src/3rdparty/phonon/gstreamer/effectmanager.cpp new file mode 100644 index 0000000..563e6fc --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/effectmanager.cpp @@ -0,0 +1,105 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include <gst/interfaces/propertyprobe.h> +#include "effectmanager.h" +#include "backend.h" +#include "gsthelper.h" + +/* + * This class manages the list of currently + * available audio effects. + */ + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ + +EffectInfo::EffectInfo(const QString &name, const QString&description, const QString&author) + : m_name(name) + , m_description(description) + , m_author(author) {} + +EffectManager::EffectManager(Backend *backend) + : QObject(backend) + , m_backend(backend) +{ + GList* factoryList = gst_registry_get_feature_list(gst_registry_get_default (), GST_TYPE_ELEMENT_FACTORY); + QString name, klass, description, author; + for (GList* iter = g_list_first(factoryList) ; iter != NULL ; iter = g_list_next(iter)) { + GstPluginFeature *feature = GST_PLUGIN_FEATURE(iter->data); + klass = gst_element_factory_get_klass(GST_ELEMENT_FACTORY(feature)); + if ( klass == "Filter/Effect/Audio" ) { + name = GST_PLUGIN_FEATURE_NAME(feature); + + // These plugins simply make no sense to the frontend: + // "audiorate" Should be internal + // "volume" not needed + // "equalizer-nbands" not really useful at the moment + + // These plugins simply dont work or have major stability issues: + // "iir" Does not seem to do much at the moment + // "audioinvert" Only works for some streams, should be invesigated + // "lpwsinc" Crashes for large values of filter kernel + // "name" Crashes for large values of filter kernel + + // Seems to be working, but not well tested: + // name == "rglimiter" Seems functional + // name == "rgvolume" Seems to be working + + QString pluginString = qgetenv("PHONON_GST_ALL_EFFECTS"); + bool acceptAll = pluginString.toInt(); + + if (acceptAll + // Plugins that have been accepted so far + || name == "audiopanorama" + || name == "audioamplify" + || name == "audiodynamic" + || name == "equalizer-10bands" + || name == "speed") + { + description = gst_element_factory_get_description (GST_ELEMENT_FACTORY(feature)); + author = gst_element_factory_get_author (GST_ELEMENT_FACTORY(feature)); + EffectInfo *effect = new EffectInfo(name, description, author); + m_audioEffectList.append(effect); + } + } + } + g_list_free(factoryList); +} + +EffectManager::~EffectManager() +{ + qDeleteAll(m_audioEffectList); + m_audioEffectList.clear(); +} + +/** + * Returns a list of available audio effects + */ +const QList<EffectInfo*> EffectManager::audioEffects() const +{ + return m_audioEffectList; +} + +} +} + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/gstreamer/effectmanager.h b/src/3rdparty/phonon/gstreamer/effectmanager.h new file mode 100644 index 0000000..f7d9c96 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/effectmanager.h @@ -0,0 +1,91 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_GSTREAMER_EFFECTMANAGER_H +#define Phonon_GSTREAMER_EFFECTMANAGER_H + +#include "common.h" + +#include <QtCore/QObject> +#include <QtCore/QTimer> +#include <QtCore/QStringList> + +#include <gst/gst.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ +class Backend; +class EffectManager; + +class EffectInfo +{ +public : + EffectInfo(const QString &name, + const QString &description, + const QString &author); + + QString name() const + { + return m_name; + } + QString description() const + { + return m_description; + } + QString author() const + { + return m_author; + } + QStringList properties() const + { + return m_properties; + } + void addProperty(QString propertyName) + { + m_properties.append(propertyName); + } + +private: + QString m_name; + QString m_description; + QString m_author; + QStringList m_properties; +}; + +class EffectManager : public QObject +{ + Q_OBJECT +public: + EffectManager(Backend *parent); + virtual ~EffectManager(); + const QList<EffectInfo*> audioEffects() const; + +private: + Backend *m_backend; + QList <EffectInfo*> m_audioEffectList; + QList <EffectInfo*> m_visualizationList; +}; +} +} // namespace Phonon::Gstreamer + +QT_END_NAMESPACE + +#endif // Phonon_GSTREAMER_EFFECTMANAGER_H diff --git a/src/3rdparty/phonon/gstreamer/glrenderer.cpp b/src/3rdparty/phonon/gstreamer/glrenderer.cpp new file mode 100644 index 0000000..6cf3459 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/glrenderer.cpp @@ -0,0 +1,339 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include <QtGui/QPainter> +#include <QtGui/QResizeEvent> + +#ifndef QT_NO_OPENGL + +#include "common.h" +#include "message.h" +#include "mediaobject.h" +#include "qwidgetvideosink.h" +#include "glrenderer.h" +#include "qrgb.h" + +#if !defined(QT_OPENGL_ES) + +#include <gst/gst.h> + +// support old OpenGL installations (1.2) +// assume that if TEXTURE0 isn't defined, none are +#ifndef GL_TEXTURE0 +# define GL_TEXTURE0 0x84C0 +# define GL_TEXTURE1 0x84C1 +# define GL_TEXTURE2 0x84C2 +#endif + +QT_BEGIN_NAMESPACE + +static void frameRendered() +{ + static QString displayFps = qgetenv("PHONON_GST_FPS"); + if (displayFps.isEmpty()) + return; + + static int frames = 0; + static QTime lastTime = QTime::currentTime(); + QTime time = QTime::currentTime(); + + int delta = lastTime.msecsTo(time); + if (delta > 2000) { + printf("FPS: %f\n", 1000.0 * frames / qreal(delta)); + lastTime = time; + frames = 0; + } + + ++frames; +} + +namespace Phonon +{ +namespace Gstreamer +{ + +GLRenderer::GLRenderer(VideoWidget* videoWidget) : + AbstractRenderer(videoWidget) + , m_glWindow(0) +{ + videoWidget->backend()->logMessage("Creating OpenGL renderer"); + QGLFormat format = QGLFormat::defaultFormat(); + format.setSwapInterval(1); // Enable vertical sync on draw to avoid tearing + m_glWindow = new GLRenderWidgetImplementation(videoWidget, format); + + if ((m_videoSink = m_glWindow->createVideoSink())) { //if ((m_videoSink = m_glWindow->createVideoSink())) { + gst_object_ref (GST_OBJECT (m_videoSink)); //Take ownership + gst_object_sink (GST_OBJECT (m_videoSink)); + + QWidgetVideoSinkBase* sink = reinterpret_cast<QWidgetVideoSinkBase*>(m_videoSink); + // Let the videosink know which widget to direct frame updates to + sink->renderWidget = videoWidget; + } +} + +GLRenderer::~GLRenderer() +{ + if (m_videoSink) { + gst_object_unref (GST_OBJECT (m_videoSink)); + m_videoSink = 0; + } +} + + +bool GLRenderer::eventFilter(QEvent * event) +{ + if (event->type() == QEvent::User) { + NewFrameEvent *frameEvent= static_cast <NewFrameEvent *>(event); + m_glWindow->setNextFrame(frameEvent->frame, frameEvent->width, frameEvent->height); + return true; + } + else if (event->type() == QEvent::Resize) { + m_glWindow->setGeometry(m_videoWidget->geometry()); + return true; + } + return false; +} + +void GLRenderer::handleMediaNodeEvent(const MediaNodeEvent *event) +{ + switch (event->type()) { + case MediaNodeEvent::SourceChanged: + { + Q_ASSERT(m_glWindow); + m_glWindow->clearFrame(); + break; + } + default: + break; + } +} + +GstElement* GLRenderWidgetImplementation::createVideoSink() +{ + if (hasYUVSupport()) + return GST_ELEMENT(g_object_new(get_type_YUV(), NULL)); + return 0; +} + +void GLRenderWidgetImplementation::setNextFrame(const QByteArray &array, int w, int h) +{ + if (m_videoWidget->root()->state() == Phonon::LoadingState) + return; + + m_frame = QImage(); + + if (hasYUVSupport()) + updateTexture(array, w, h); + else + m_frame = QImage((uchar *)array.constData(), w, h, QImage::Format_RGB32); + + m_array = array; + m_width = w; + m_height = h; + + update(); +} + +void GLRenderWidgetImplementation::clearFrame() +{ + m_frame = QImage(); + m_array = QByteArray(); + update(); +} + +bool GLRenderWidgetImplementation::hasYUVSupport() const +{ + return m_yuvSupport; +} + +static QImage convertFromYUV(const QByteArray &array, int w, int h) +{ + QImage result(w, h, QImage::Format_RGB32); + + // TODO: bilinearly interpolate the U and V channels for better result + + for (int y = 0; y < h; ++y) { + uint *sp = (uint *)result.scanLine(y); + + const uchar *yp = (const uchar *)(array.constData() + y * w); + const uchar *up = (const uchar *)(array.constData() + w * h + (y/2)*(w/2)); + const uchar *vp = (const uchar *)(array.constData() + w * h * 5/4 + (y/2)*(w/2)); + + for (int x = 0; x < w; ++x) { + const int sy = *yp; + const int su = *up; + const int sv = *vp; + + const int R = int(1.164 * (sy - 16) + 1.596 * (sv - 128)); + const int G = int(1.164 * (sy - 16) - 0.813 * (sv - 128) - 0.391 * (su - 128)); + const int B = int(1.164 * (sy - 16) + 2.018 * (su - 128)); + + *sp = qRgb(qBound(0, R, 255), + qBound(0, G, 255), + qBound(0, B, 255)); + + ++yp; + ++sp; + if (x & 1) { + ++up; + ++vp; + } + } + } + return result; +} + +const QImage &GLRenderWidgetImplementation::currentFrame() const +{ + if (m_frame.isNull() && !m_array.isNull()) + m_frame = convertFromYUV(m_array, m_width, m_height); + + return m_frame; +} + +#ifndef GL_FRAGMENT_PROGRAM_ARB +#define GL_FRAGMENT_PROGRAM_ARB 0x8804 +#define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875 +#endif + +// arbfp1 fragment program for converting yuv to rgb +const char *const yuvToRgb = + "!!ARBfp1.0" + "PARAM c[3] = { { 0.5, 0.0625 }," + "{ 1.164, 0, 1.596, 2.0179999 }," + "{ 1.164, -0.391, -0.81300002 } };" + "TEMP R0;" + "TEMP R1;" + "TEX R0.x, fragment.texcoord[0], texture[2], 2D;" + "ADD R1.z, R0.x, -c[0].x;" + "TEX R1.x, fragment.texcoord[0], texture[0], 2D;" + "TEX R0.x, fragment.texcoord[0], texture[1], 2D;" + "ADD R1.x, R1, -c[0].y;" + "ADD R1.y, R0.x, -c[0].x;" + "DP3 result.color.x, R1, c[1];" + "DP3 result.color.y, R1, c[2];" + "DP3 result.color.z, R1, c[1].xwyw;" + "END"; + +GLRenderWidgetImplementation::GLRenderWidgetImplementation(VideoWidget*videoWidget, const QGLFormat &format) : + QGLWidget(format, videoWidget) + , m_program(0) + , m_yuvSupport(false) + , m_videoWidget(videoWidget) +{ + makeCurrent(); + glGenTextures(3, m_texture); + + glProgramStringARB = (_glProgramStringARB) context()->getProcAddress(QLatin1String("glProgramStringARB")); + glBindProgramARB = (_glBindProgramARB) context()->getProcAddress(QLatin1String("glBindProgramARB")); + glDeleteProgramsARB = (_glDeleteProgramsARB) context()->getProcAddress(QLatin1String("glDeleteProgramsARB")); + glGenProgramsARB = (_glGenProgramsARB) context()->getProcAddress(QLatin1String("glGenProgramsARB")); + glActiveTexture = (_glActiveTexture) context()->getProcAddress(QLatin1String("glActiveTexture")); + + m_hasPrograms = glProgramStringARB && glBindProgramARB && glDeleteProgramsARB && glGenProgramsARB && glActiveTexture; + + if (m_hasPrograms) { + glGenProgramsARB(1, &m_program); + glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_program); + + const GLbyte *gl_src = reinterpret_cast<const GLbyte *>(yuvToRgb); + glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, + int(strlen(yuvToRgb)), gl_src); + + if (glGetError() != GL_NO_ERROR) { + glDeleteProgramsARB(1, &m_program); + m_hasPrograms = false; + } else { + m_yuvSupport = true; + } + } + + QPalette palette; + palette.setColor(QPalette::Background, Qt::black); + setPalette(palette); + setAutoFillBackground(true); + // Videowidget allways have this property to allow hiding the mouse cursor + setMouseTracking(true); +} + +void GLRenderWidgetImplementation::updateTexture(const QByteArray &array, int width, int height) +{ + m_width = width; + m_height = height; + + makeCurrent(); + + int w[3] = { width, width/2, width/2 }; + int h[3] = { height, height/2, height/2 }; + int offs[3] = { 0, width*height, width*height*5/4 }; + + for (int i = 0; i < 3; ++i) { + glBindTexture(GL_TEXTURE_2D, m_texture[i]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w[i], h[i], 0, + GL_LUMINANCE, GL_UNSIGNED_BYTE, array.data() + offs[i]); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + } +} + +void GLRenderWidgetImplementation::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + m_drawFrameRect = m_videoWidget->calculateDrawFrameRect(); + if (m_yuvSupport && frameIsSet()) { + glEnable(GL_FRAGMENT_PROGRAM_ARB); + glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_program); + const float tx_array[] = { 0, 0, 1, 0, 1, 1, 0, 1}; + const QRectF r = drawFrameRect(); + + const float v_array[] = { r.left(), r.top(), r.right(), r.top(), r.right(), r.bottom(), r.left(), r.bottom() }; + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_texture[0]); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, m_texture[1]); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, m_texture[2]); + glActiveTexture(GL_TEXTURE0); + + glVertexPointer(2, GL_FLOAT, 0, v_array); + glTexCoordPointer(2, GL_FLOAT, 0, tx_array); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glDrawArrays(GL_QUADS, 0, 4); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + glDisable(GL_FRAGMENT_PROGRAM_ARB); + } else { + painter.setRenderHint(QPainter::SmoothPixmapTransform); + painter.drawImage(drawFrameRect(), currentFrame()); + } + + frameRendered(); +} +} +} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE + +#endif // QT_OPENGL_ES +#endif // QT_NO_OPENGL diff --git a/src/3rdparty/phonon/gstreamer/glrenderer.h b/src/3rdparty/phonon/gstreamer/glrenderer.h new file mode 100644 index 0000000..68dd141 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/glrenderer.h @@ -0,0 +1,101 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_GSTREAMER_GLRENDERER_H +#define Phonon_GSTREAMER_GLRENDERER_H + +#include "videowidget.h" +#include "common.h" + +#ifndef QT_NO_OPENGL + +#include <QtOpenGL/QGLFormat> +#include <QtOpenGL/QGLWidget> + +#ifndef QT_OPENGL_ES +QT_BEGIN_NAMESPACE + +class QString; + +namespace Phonon +{ +namespace Gstreamer +{ +class GLRenderWidgetImplementation; + +class GLRenderer : public AbstractRenderer +{ +public: + GLRenderer(VideoWidget *control); + ~GLRenderer(); + void handleMediaNodeEvent(const MediaNodeEvent *event); + bool eventFilter(QEvent * event); + bool paintsOnWidget() { return false; } +private: + GLRenderWidgetImplementation *m_glWindow; +}; + +class GLRenderWidgetImplementation : public QGLWidget +{ + Q_OBJECT + + // ARB_fragment_program + typedef void (*_glProgramStringARB) (GLenum, GLenum, GLsizei, const GLvoid *); + typedef void (*_glBindProgramARB) (GLenum, GLuint); + typedef void (*_glDeleteProgramsARB) (GLsizei, const GLuint *); + typedef void (*_glGenProgramsARB) (GLsizei, GLuint *); + typedef void (*_glActiveTexture) (GLenum); +public: + GLRenderWidgetImplementation(VideoWidget *control, const QGLFormat &format); + void paintEvent(QPaintEvent *event); + GstElement *createVideoSink(); + void updateTexture(const QByteArray &array, int width, int height); + bool hasYUVSupport() const; + const QImage& currentFrame() const; + QRect drawFrameRect() const { return m_drawFrameRect; } + bool frameIsSet() const { return !m_array.isNull(); } + void setNextFrame(const QByteArray &array, int width, int height); + void clearFrame(); +private: + _glProgramStringARB glProgramStringARB; + _glBindProgramARB glBindProgramARB; + _glDeleteProgramsARB glDeleteProgramsARB; + _glGenProgramsARB glGenProgramsARB; + _glActiveTexture glActiveTexture; + + mutable QImage m_frame; + QByteArray m_array; + int m_width; + int m_height; + QRect m_drawFrameRect; + GLuint m_texture[3]; + + bool m_hasPrograms; + GLuint m_program; + bool m_yuvSupport; + VideoWidget *m_videoWidget; +}; + +} +} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE + +#endif //QT_OPENGL_ES +#endif // QT_NO_OPENGL + +#endif // Phonon_GSTREAMER_GLRENDERER_H diff --git a/src/3rdparty/phonon/gstreamer/gsthelper.cpp b/src/3rdparty/phonon/gstreamer/gsthelper.cpp new file mode 100644 index 0000000..34d99fa --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/gsthelper.cpp @@ -0,0 +1,170 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include <gst/interfaces/propertyprobe.h> +#include <gst/gst.h> +#include "common.h" +#include "gsthelper.h" + +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ + +/** + * Probes a gstElement for a list of settable string-property values + * + * @return a QStringList containing a list of allwed string values for the given + * element + */ +QList<QByteArray> GstHelper::extractProperties(GstElement *elem, const QByteArray &value) +{ + Q_ASSERT(elem); + QList<QByteArray> list; + + if (GST_IS_PROPERTY_PROBE(elem)) { + GstPropertyProbe *probe = GST_PROPERTY_PROBE(elem); + const GParamSpec *devspec = 0; + GValueArray *array = NULL; + + if ((devspec = gst_property_probe_get_property (probe, value))) { + if ((array = gst_property_probe_probe_and_get_values (probe, devspec))) { + for (unsigned int device = 0; device < array->n_values; device++) { + GValue *deviceId = g_value_array_get_nth (array, device); + list.append(g_value_get_string(deviceId)); + } + } + if (array) + g_value_array_free (array); + } + } + return list; +} + +/** + * Sets the string value of a GstElement's property + * + * @return false if the value could not be set. + */ +bool GstHelper::setProperty(GstElement *elem, const char *propertyName, const QByteArray &propertyValue) +{ + Q_ASSERT(elem); + Q_ASSERT(propertyName && strlen(propertyName)); + + if (GST_IS_PROPERTY_PROBE(elem) && gst_property_probe_get_property( GST_PROPERTY_PROBE( elem), propertyName ) ) { + g_object_set(G_OBJECT(elem), propertyName, propertyValue.constData(), (const char*)NULL); + return true; + } + return false; +} + +/** + * Queries an element for the value of an object property + */ +QByteArray GstHelper::property(GstElement *elem, const char *propertyName) +{ + Q_ASSERT(elem); + Q_ASSERT(propertyName && strlen(propertyName)); + QByteArray retVal; + + if (GST_IS_PROPERTY_PROBE(elem) && gst_property_probe_get_property( GST_PROPERTY_PROBE(elem), propertyName)) { + gchar *value = NULL; + g_object_get (G_OBJECT(elem), propertyName, &value, (const char*)NULL); + retVal = QByteArray(value); + g_free (value); + } + return retVal; +} + +/** + * Queries a GstObject for it's name + */ +QByteArray GstHelper::name(GstObject *obj) +{ + Q_ASSERT(obj); + QByteArray retVal; + gchar *value = NULL; + if ((value = gst_object_get_name (obj))) { + retVal = QByteArray(value); + g_free (value); + } + return retVal; +} + + +/*** + * Creates an instance of a playbin with "audio-src" and + * "video-src" ghost pads to allow redirected output streams. + * + * ### This function is probably not required now that MediaObject is based + * on decodebin directly. + */ +GstElement* GstHelper::createPluggablePlaybin() +{ + GstElement *playbin = 0; + //init playbin and add to our pipeline + playbin = gst_element_factory_make("playbin", NULL); + + //Create an identity element to redirect sound + GstElement *audioSinkBin = gst_bin_new (NULL); + GstElement *audioPipe = gst_element_factory_make("identity", NULL); + gst_bin_add(GST_BIN(audioSinkBin), audioPipe); + + //Create a sinkpad on the identity + GstPad *audiopad = gst_element_get_pad (audioPipe, "sink"); + gst_element_add_pad (audioSinkBin, gst_ghost_pad_new ("sink", audiopad)); + gst_object_unref (audiopad); + + //Create an "audio_src" source pad on the playbin + GstPad *audioPlaypad = gst_element_get_pad (audioPipe, "src"); + gst_element_add_pad (playbin, gst_ghost_pad_new ("audio_src", audioPlaypad)); + gst_object_unref (audioPlaypad); + + //Done with our audio redirection + g_object_set (G_OBJECT(playbin), "audio-sink", audioSinkBin, (const char*)NULL); + + // * * Redirect video to "video_src" pad : * * + + //Create an identity element to redirect sound + GstElement *videoSinkBin = gst_bin_new (NULL); + GstElement *videoPipe = gst_element_factory_make("identity", NULL); + gst_bin_add(GST_BIN(videoSinkBin), videoPipe); + + //Create a sinkpad on the identity + GstPad *videopad = gst_element_get_pad (videoPipe, "sink"); + gst_element_add_pad (videoSinkBin, gst_ghost_pad_new ("sink", videopad)); + gst_object_unref (videopad); + + //Create an "audio_src" source pad on the playbin + GstPad *videoPlaypad = gst_element_get_pad (videoPipe, "src"); + gst_element_add_pad (playbin, gst_ghost_pad_new ("video_src", videoPlaypad)); + gst_object_unref (videoPlaypad); + + //Done with our video redirection + g_object_set (G_OBJECT(playbin), "video-sink", videoSinkBin, (const char*)NULL); + return playbin; +} + + +} //namespace Gstreamer +} //namespace Phonon + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/gstreamer/gsthelper.h b/src/3rdparty/phonon/gstreamer/gsthelper.h new file mode 100644 index 0000000..ff342a4 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/gsthelper.h @@ -0,0 +1,49 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_GSTREAMER_GSTHELPER_H +#define Phonon_GSTREAMER_GSTHELPER_H + +#include "common.h" + +#include <gst/gst.h> + +QT_BEGIN_NAMESPACE + +template<class T> class QList; +class QByteArray; + +namespace Phonon +{ +namespace Gstreamer +{ +class GstHelper +{ +public: + static QList<QByteArray> extractProperties(GstElement *elem, const QByteArray &value); + static bool setProperty(GstElement *elem, const char *propertyName, const QByteArray &propertyValue); + static QByteArray property(GstElement *elem, const char *propertyName); + static QByteArray name(GstObject *elem); + static GstElement* createPluggablePlaybin(); +}; + +} // ns Gstreamer +} // ns Phonon + +QT_END_NAMESPACE + +#endif // Phonon_GSTREAMER_GSTHELPER_H diff --git a/src/3rdparty/phonon/gstreamer/gstreamer.desktop b/src/3rdparty/phonon/gstreamer/gstreamer.desktop new file mode 100644 index 0000000..b62472b --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/gstreamer.desktop @@ -0,0 +1,51 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=PhononBackend +MimeType=application/x-annodex;video/quicktime;video/x-quicktime;audio/x-m4a;application/x-quicktimeplayer;video/mkv;video/msvideo;video/x-msvideo;video/x-flic;audio/x-aiff;audio/aiff;audio/x-pn-aiff;audio/x-realaudio;audio/basic;audio/x-basic;audio/x-pn-au;audio/x-8svx;audio/8svx;audio/x-16sv;audio/168sv;image/x-ilbm;image/ilbm;video/x-anim;video/anim;image/png;image/x-png;video/mng;video/x-mng;audio/x-ogg;audio/x-speex+ogg;application/ogg;application/ogg;audio/vnd.rn-realaudio;audio/x-pn-realaudio-plugin;audio/x-real-audio;application/vnd.rn-realmedia;video/mpeg;video/x-mpeg;audio/x-wav;audio/wav;audio/x-pn-wav;audio/x-pn-windows-acm;audio/mpeg2;audio/x-mpeg2;audio/mpeg3;audio/x-mpeg3;audio/mpeg;audio/x-mpeg;x-mpegurl;audio/x-mpegurl;audio/mp3;audio/mpeg; +X-KDE-Library=phonon_gstreamer +X-KDE-PhononBackendInfo-InterfaceVersion=1 +X-KDE-PhononBackendInfo-Version=0.1 +X-KDE-PhononBackendInfo-Website=http://gstreamer.freedesktop.org/ +Icon=phonon-gstreamer +InitialPreference=10 + +Name=GStreamer +Name[pa]=ਜੀਸਟੀਰਮਰ +Name[sr]=Гстример +Name[sv]=Gstreamer +Name[x-test]=xxGStreamerxx + +Comment=Phonon GStreamer backend +Comment[bg]=Phonon GStreamer +Comment[ca]=Dorsal GStreamer del Phonon +Comment[da]=GStreamer-backend til Phonon +Comment[de]=Phonon-Treiber für GStreamer +Comment[el]=Σύστημα υποστήριξης GStreamer του Phonon +Comment[es]=Motor GStreamer para Phonon +Comment[et]=Phononi GStreameri taustaprogramm +Comment[fr]=Système de gestion GStreamer pour Phonon +Comment[ga]=Inneall GStreamer le haghaidh Phonon +Comment[gl]=Infraestrutura de GStreamer para Phonon +Comment[is]=Phonon GStreamer bakendi +Comment[it]=Motore Gstreamer di Phonon +Comment[ja]=Phonon GStreamer バックエンド +Comment[ko]=Phonon GStreamer 백엔드 +Comment[ku]=Binesaza Phonon GStreamer +Comment[lv]=Phonon GStreamer aizmugure +Comment[nds]=Phonon-Hülpprogramm GStreamer +Comment[nl]=GStreamer-backend (Phonon) +Comment[nn]=Phonon-motor for GStreamer +Comment[pa]=ਫੋਨੋਨ ਜਸਟੀਰਮਰ ਬੈਕਐਂਡ +Comment[pl]=Obsługa GStreamera przez Phonon +Comment[pt]=Infra-estrutura do GStreamer para o Phonon +Comment[pt_BR]=Infraestrutura Phonon GStreamer +Comment[sk]=GStreamer podsystém +Comment[sl]=Phononova hrbtenica GStreamer +Comment[sr]=Гстример као позадина Фонона +Comment[sr@latin]=GStreamer kao pozadina Phonona +Comment[sv]=Phonon Gstreamer-gränssnitt +Comment[tr]=Phonon GStreamer arka ucu +Comment[uk]=Сервер GStreamer для Phonon +Comment[x-test]=xxPhonon GStreamer backendxx +Comment[zh_CN]=Phonon GStreamer 后端 +Comment[zh_TW]=Phonon GStreamer 後端介面 diff --git a/src/3rdparty/phonon/gstreamer/lgpl-2.1.txt b/src/3rdparty/phonon/gstreamer/lgpl-2.1.txt new file mode 100644 index 0000000..5ab7695 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/lgpl-2.1.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[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 + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/src/3rdparty/phonon/gstreamer/lgpl-3.txt b/src/3rdparty/phonon/gstreamer/lgpl-3.txt new file mode 100644 index 0000000..fc8a5de --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/lgpl-3.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + 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 that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU 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 as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/src/3rdparty/phonon/gstreamer/medianode.cpp b/src/3rdparty/phonon/gstreamer/medianode.cpp new file mode 100644 index 0000000..7257972 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/medianode.cpp @@ -0,0 +1,456 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "common.h" +#include "medianode.h" +#include "mediaobject.h" +#include "message.h" +#include "backend.h" +#include "gsthelper.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ + +MediaNode::MediaNode(Backend *backend, NodeDescription description) : + m_isValid(false), + m_root(0), + m_audioTee(0), + m_videoTee(0), + m_fakeAudioSink(0), + m_fakeVideoSink(0), + m_backend(backend), + m_description(description) +{ + if ((description & AudioSink) && (description & VideoSink)) { + Q_ASSERT(0); // A node cannot accept both audio and video + } + + if (description & AudioSource) { + m_audioTee = gst_element_factory_make("tee", NULL); + gst_object_ref (GST_OBJECT (m_audioTee)); + gst_object_sink (GST_OBJECT (m_audioTee)); + + // Fake audio sink to swallow unconnected audio pads + m_fakeAudioSink = gst_element_factory_make("fakesink", NULL); + g_object_set (G_OBJECT (m_fakeAudioSink), "sync", TRUE, (const char*)NULL); + gst_object_ref (GST_OBJECT (m_fakeAudioSink)); + gst_object_sink (GST_OBJECT (m_fakeAudioSink)); + } + + if (description & VideoSource) { + m_videoTee = gst_element_factory_make("tee", NULL); + gst_object_ref (GST_OBJECT (m_videoTee)); + gst_object_sink (GST_OBJECT (m_videoTee)); + + // Fake video sink to swallow unconnected video pads + m_fakeVideoSink = gst_element_factory_make("fakesink", NULL); + g_object_set (G_OBJECT (m_fakeVideoSink), "sync", TRUE, (const char*)NULL); + gst_object_ref (GST_OBJECT (m_fakeVideoSink)); + gst_object_sink (GST_OBJECT (m_fakeVideoSink)); + } +} + +MediaNode::~MediaNode() +{ + if (m_videoTee) { + gst_element_set_state(m_videoTee, GST_STATE_NULL); + gst_object_unref(m_videoTee); + } + + if (m_audioTee) { + gst_element_set_state(m_audioTee, GST_STATE_NULL); + gst_object_unref(m_audioTee); + } + + if (m_fakeAudioSink) { + gst_element_set_state(m_fakeAudioSink, GST_STATE_NULL); + gst_object_unref(m_fakeAudioSink); + } + + if (m_fakeVideoSink) { + gst_element_set_state(m_fakeVideoSink, GST_STATE_NULL); + gst_object_unref(m_fakeVideoSink); + } +} + + +/** + * Connects children recursively from a mediaobject root + */ +bool MediaNode::buildGraph() +{ + Q_ASSERT(root()); //We cannot build the graph without a root element source + + bool success = link(); + + if (success) { + // connect children recursively + for (int i=0; i< m_audioSinkList.size(); ++i) { + if (MediaNode *node = qobject_cast<MediaNode*>(m_audioSinkList[i])) { + node->setRoot(root()); + if (!node->buildGraph()) + success = false; + } + } + + for (int i=0; i < m_videoSinkList.size(); ++i) { + if (MediaNode *node = qobject_cast<MediaNode*>(m_videoSinkList[i])) { + node->setRoot(root()); + if (!node->buildGraph()) + success = false; + } + } + } + + if (!success) + unlink(); + + return success; +} + +/** + * Disconnects children recursively + */ +bool MediaNode::breakGraph() +{ + for (int i=0; i<m_audioSinkList.size(); ++i) { + MediaNode *node = qobject_cast<MediaNode*>(m_audioSinkList[i]); + if (!node || !node->breakGraph()) + return false; + node->setRoot(0); + } + + for (int i=0; i <m_videoSinkList.size(); ++i) { + MediaNode *node = qobject_cast<MediaNode*>(m_videoSinkList[i]); + if (!node || !node->breakGraph()) + return false; + node->setRoot(0); + } + unlink(); + return true; +} + +bool MediaNode::connectNode(QObject *obj) +{ + MediaNode *sink = qobject_cast<MediaNode*>(obj); + + bool success = false; + + if (sink) { + + if (!sink->isValid()) { + m_backend->logMessage(QString("Trying to link to an invalid node (%0)").arg(sink->name()), Backend::Warning); + return false; + } + + if (sink->root()) { + m_backend->logMessage("Trying to link a node that is already linked to a different mediasource ", Backend::Warning); + return false; + } + + if ((m_description & AudioSource) && (sink->m_description & AudioSink)) { + m_audioSinkList << obj; + MediaNodeEvent event(MediaNodeEvent::AudioSinkAdded, sink); + root()->mediaNodeEvent(&event); + success = true; + } + + if ((m_description & VideoSource) && (sink->m_description & VideoSink)) { + m_videoSinkList << obj; + MediaNodeEvent event(MediaNodeEvent::VideoSinkAdded, sink); + root()->mediaNodeEvent(&event); + success = true; + } + + // If we have a root source, and we are connected + // try to link the gstreamer elements + if (success && root()) { + MediaNodeEvent mediaObjectConnected(MediaNodeEvent::MediaObjectConnected, root()); + notify(&mediaObjectConnected); + root()->buildGraph(); + } + } + return success; +} + +bool MediaNode::disconnectNode(QObject *obj) +{ + MediaNode *sink = qobject_cast<MediaNode*>(obj); + if (root()) { + // Disconnecting elements while playing or paused seems to cause + // potential deadlock. Hence we force the pipeline into ready state + // before any nodes are disconnected. + gst_element_set_state(root()->pipeline(), GST_STATE_READY); + + Q_ASSERT(sink->root()); //sink has to have a root since it is onnected + + if (sink->description() & (AudioSink)) { + GstPad *sinkPad = gst_element_get_pad(sink->audioElement(), "sink"); + // Release requested src pad from tee + GstPad *requestedPad = gst_pad_get_peer(sinkPad); + if (requestedPad) { + gst_element_release_request_pad(m_audioTee, requestedPad); + gst_object_unref(requestedPad); + } + if (GST_ELEMENT_PARENT(sink->audioElement())) + gst_bin_remove(GST_BIN(root()->audioGraph()), sink->audioElement()); + gst_object_unref(sinkPad); + } + + if (sink->description() & (VideoSink)) { + GstPad *sinkPad = gst_element_get_pad(sink->videoElement(), "sink"); + // Release requested src pad from tee + GstPad *requestedPad = gst_pad_get_peer(sinkPad); + if (requestedPad) { + gst_element_release_request_pad(m_videoTee, requestedPad); + gst_object_unref(requestedPad); + } + if (GST_ELEMENT_PARENT(sink->videoElement())) + gst_bin_remove(GST_BIN(root()->videoGraph()), sink->videoElement()); + gst_object_unref(sinkPad); + } + + sink->breakGraph(); + sink->setRoot(0); + } + + m_videoSinkList.removeAll(obj); + m_audioSinkList.removeAll(obj); + + if (sink->m_description & AudioSink) { + // Remove sink from graph + MediaNodeEvent event(MediaNodeEvent::AudioSinkRemoved, sink); + mediaNodeEvent(&event); + return true; + } + + if ((m_description & VideoSource) && (sink->m_description & VideoSink)) { + // Remove sink from graph + MediaNodeEvent event(MediaNodeEvent::VideoSinkRemoved, sink); + mediaNodeEvent(&event); + return true; + } + + return false; +} + +void MediaNode::mediaNodeEvent(const MediaNodeEvent *) {} + +/** + * Propagates an event down the graph + * sender is responsible for deleting the event + */ +void MediaNode::notify(const MediaNodeEvent *event) +{ + Q_ASSERT(event); + mediaNodeEvent(event); + for (int i=0; i<m_audioSinkList.size(); ++i) { + MediaNode *node = qobject_cast<MediaNode*>(m_audioSinkList[i]); + node->notify(event); + } + + for (int i=0; i<m_videoSinkList.size(); ++i) { + MediaNode *node = qobject_cast<MediaNode*>(m_videoSinkList[i]); + node->notify(event); + } +} + +/* + * Requests a new tee pad and connects a node to it + */ +bool MediaNode::addOutput(MediaNode *output, GstElement *tee) +{ + Q_ASSERT(root()); + + bool success = true; + + GstElement *sinkElement = 0; + if (output->description() & AudioSink) + sinkElement = output->audioElement(); + else if (output->description() & VideoSink) + sinkElement = output->videoElement(); + + Q_ASSERT(sinkElement); + + if (!sinkElement) + return false; + + GstState state = GST_STATE (root()->pipeline()); + GstPad *srcPad = gst_element_get_request_pad (tee, "src%d"); + GstPad *sinkPad = gst_element_get_pad (sinkElement, "sink"); + + if (!sinkPad) { + success = false; + } else if (gst_pad_is_linked(sinkPad)) { + gst_object_unref (GST_OBJECT (sinkPad)); + gst_object_unref (GST_OBJECT (srcPad)); + return true; + } + + if (success) { + if (output->description() & AudioSink) + gst_bin_add(GST_BIN(root()->audioGraph()), sinkElement); + else if (output->description() & VideoSink) + gst_bin_add(GST_BIN(root()->videoGraph()), sinkElement); + } + + if (success) { + gst_pad_link(srcPad, sinkPad); + gst_element_set_state(sinkElement, state); + } else { + gst_element_release_request_pad(tee, srcPad); + } + + gst_object_unref (GST_OBJECT (srcPad)); + gst_object_unref (GST_OBJECT (sinkPad)); + + return success; +} + +// Used to seal up unconnected source nodes by connecting unconnected src pads to fake sinks +bool MediaNode::connectToFakeSink(GstElement *tee, GstElement *sink, GstElement *bin) +{ + bool success = true; + GstPad *sinkPad = gst_element_get_pad (sink, "sink"); + + if (GST_PAD_IS_LINKED (sinkPad)) { + //This fakesink is already connected + gst_object_unref (sinkPad); + return true; + } + + GstPad *srcPad = gst_element_get_request_pad (tee, "src%d"); + gst_bin_add(GST_BIN(bin), sink); + if (success) + success = (gst_pad_link (srcPad, sinkPad) == GST_PAD_LINK_OK); + if (success) + success = (gst_element_set_state(sink, GST_STATE(bin)) != GST_STATE_CHANGE_FAILURE); + gst_object_unref (srcPad); + gst_object_unref (sinkPad); + return success; +} + +// Used to seal up unconnected source nodes by connecting unconnected src pads to fake sinks +bool MediaNode::releaseFakeSinkIfConnected(GstElement *tee, GstElement *fakesink, GstElement *bin) +{ + if (GST_ELEMENT_PARENT(fakesink) == GST_ELEMENT(bin)) { + GstPad *sinkPad = gst_element_get_pad(fakesink, "sink"); + + // Release requested src pad from tee + GstPad *requestedPad = gst_pad_get_peer(sinkPad); + if (requestedPad) { + gst_element_release_request_pad(tee, requestedPad); + gst_object_unref(requestedPad); + } + gst_object_unref(sinkPad); + + gst_element_set_state(fakesink, GST_STATE_NULL); + gst_bin_remove(GST_BIN(bin), fakesink); + Q_ASSERT(!GST_ELEMENT_PARENT(fakesink)); + } + return true; +} + +bool MediaNode::linkMediaNodeList(QList<QObject *> &list, GstElement *bin, GstElement *tee, GstElement *fakesink, GstElement *src) +{ + if (!GST_ELEMENT_PARENT(tee)) { + gst_bin_add(GST_BIN(bin), tee); + if (!gst_element_link_pads(src, "src", tee, "sink")) + return false; + gst_element_set_state(tee, GST_STATE(bin)); + } + if (list.isEmpty()) { + //connect node to a fake sink to avoid clogging the pipeline + if (!connectToFakeSink(tee, fakesink, bin)) + return false; + } else { + // Remove fake sink if previously connected + if (!releaseFakeSinkIfConnected(tee, fakesink, bin)) + return false; + + for (int i = 0 ; i < list.size() ; ++i) { + QObject *sink = list[i]; + if (MediaNode *output = qobject_cast<MediaNode*>(sink)) { + if (!addOutput(output, tee)) + return false; + } + } + } + return true; +} + +bool MediaNode::link() +{ + // Rewire everything + if ((description() & AudioSource)) { + if (!linkMediaNodeList(m_audioSinkList, root()->audioGraph(), m_audioTee, m_fakeAudioSink, audioElement())) + return false; + } + + if ((description() & VideoSource)) { + if (!linkMediaNodeList(m_videoSinkList, root()->videoGraph(), m_videoTee, m_fakeVideoSink, videoElement())) + return false; + } + return true; +} + +bool MediaNode::unlink() +{ + Q_ASSERT(root()); + if (description() & AudioSource) { + if (GST_ELEMENT_PARENT(m_audioTee) == GST_ELEMENT(root()->audioGraph())) { + gst_element_set_state(m_audioTee, GST_STATE_NULL); + gst_bin_remove(GST_BIN(root()->audioGraph()), m_audioTee); + } + for (int i=0; i<m_audioSinkList.size(); ++i) { + QObject *audioSink = m_audioSinkList[i]; + if (MediaNode *output = qobject_cast<MediaNode*>(audioSink)) { + GstElement *element = output->audioElement(); + if (GST_ELEMENT_PARENT(element) == GST_ELEMENT(root()->audioGraph())) { + gst_element_set_state(element, GST_STATE_NULL); + gst_bin_remove(GST_BIN(root()->audioGraph()), element); + } + } + } + } else if (description() & VideoSource) { + if (GST_ELEMENT_PARENT(m_videoTee) == GST_ELEMENT(root()->videoGraph())) { + gst_element_set_state(m_videoTee, GST_STATE_NULL); + gst_bin_remove(GST_BIN(root()->videoGraph()), m_videoTee); + } + for (int i=0; i <m_videoSinkList.size(); ++i) { + QObject *videoSink = m_videoSinkList[i]; + if (MediaNode *vw = qobject_cast<MediaNode*>(videoSink)) { + GstElement *element = vw->videoElement(); + if (GST_ELEMENT_PARENT(element) == GST_ELEMENT(root()->videoGraph())) { + gst_element_set_state(element, GST_STATE_NULL); + gst_bin_remove(GST_BIN(root()->videoGraph()), element); + } + } + } + } + return true; +} + + +} // ns Gstreamer +} // ns Phonon + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/gstreamer/medianode.h b/src/3rdparty/phonon/gstreamer/medianode.h new file mode 100644 index 0000000..6e3850a --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/medianode.h @@ -0,0 +1,128 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_GSTREAMER_MEDIANODE_H +#define Phonon_GSTREAMER_MEDIANODE_H + +#include "common.h" +#include "medianodeevent.h" + +#include <QtCore/QObject> +#include <QtCore/QList> +#include <QtCore/QSize> + +#include <gst/gst.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon { +namespace Gstreamer { + +class Message; +class MediaObject; +class Backend; + +class MediaNode { +public: + enum NodeDescriptionEnum { + AudioSource = 0x1, + AudioSink = 0x2, + VideoSource = 0x4, + VideoSink = 0x8 + }; + Q_DECLARE_FLAGS(NodeDescription, NodeDescriptionEnum) + + MediaNode(Backend *backend, NodeDescription description); + + virtual ~MediaNode(); + + bool connectNode(QObject *other); + bool disconnectNode(QObject *other); + + bool buildGraph(); + bool breakGraph(); + + virtual bool link(); + virtual bool unlink(); + + NodeDescription description() const { + return m_description; + } + + bool isValid() { + return m_isValid; + } + + MediaObject *root() { + return m_root; + } + + void setRoot(MediaObject *mediaObject) { + m_root = mediaObject; + } + + void notify(const MediaNodeEvent *event); + + Backend *backend() { + return m_backend; + } + + const QString &name() { + return m_name; + } + + virtual GstElement *audioElement() { + return m_audioTee; + } + + virtual GstElement *videoElement() { + return m_videoTee; + } + +protected: + bool connectToFakeSink(GstElement *tee, GstElement *sink, GstElement *bin); + bool releaseFakeSinkIfConnected(GstElement *tee, GstElement *sink, GstElement *bin); + bool linkMediaNodeList(QList<QObject *> &list, GstElement *bin, GstElement *tee, GstElement *sink, GstElement *src); + + virtual void mediaNodeEvent(const MediaNodeEvent *event); + QList<QObject *> m_audioSinkList; + QList<QObject *> m_videoSinkList; + + bool m_isValid; + MediaObject *m_root; + GstElement *m_audioTee; + GstElement *m_videoTee; + GstElement *m_fakeAudioSink; + GstElement *m_fakeVideoSink; + Backend *m_backend; + QString m_name; + +private: + bool addOutput(MediaNode *, GstElement *tee); + NodeDescription m_description; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(MediaNode::NodeDescription) + +} // ns Gstreamer +} // ns Phonon + +Q_DECLARE_INTERFACE(Phonon::Gstreamer::MediaNode, "org.phonon.gstreamer.MediaNode") + +QT_END_NAMESPACE + +#endif // Phonon_GSTREAMER_MEDIANODE_H diff --git a/src/3rdparty/phonon/gstreamer/medianodeevent.cpp b/src/3rdparty/phonon/gstreamer/medianodeevent.cpp new file mode 100644 index 0000000..20560ee --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/medianodeevent.cpp @@ -0,0 +1,38 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "medianodeevent.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ + +MediaNodeEvent::MediaNodeEvent(Type type, const void *data) : + eventType(type), + eventData(data) +{} + +MediaNodeEvent::~MediaNodeEvent() +{} + +} +} // namespace Phonon::Gstreamer + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/gstreamer/medianodeevent.h b/src/3rdparty/phonon/gstreamer/medianodeevent.h new file mode 100644 index 0000000..cacf4a5 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/medianodeevent.h @@ -0,0 +1,70 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_GSTREAMER_MEDIANODEEVENT_H +#define Phonon_GSTREAMER_MEDIANODEEVENT_H + +#include "common.h" + +#include <QtCore> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ +class MediaNodeEvent +{ +public: + enum Type { + VideoAvailable, + AudioAvailable, + SourceChanged, + MediaObjectConnected, + StateChanged, + VideoSinkAdded, + VideoSinkRemoved, + AudioSinkAdded, + AudioSinkRemoved, + VideoHandleRequest, + VideoSizeChanged + }; + + MediaNodeEvent(Type type, const void *data = 0); + virtual ~MediaNodeEvent(); + + inline Type type() const + { + return eventType; + }; + inline const void* data() const + { + return eventData; + }; + +private: + Type eventType; + const void *eventData; +}; + +} +} // namespace Phonon::Gstreamer + +QT_END_NAMESPACE + +#endif // Phonon_GSTREAMER_MEDIANODEEVENT_H diff --git a/src/3rdparty/phonon/gstreamer/mediaobject.cpp b/src/3rdparty/phonon/gstreamer/mediaobject.cpp new file mode 100644 index 0000000..74fc1b4 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/mediaobject.cpp @@ -0,0 +1,1479 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ +#include <cmath> +#include <gst/interfaces/propertyprobe.h> +#include "common.h" +#include "mediaobject.h" +#include "videowidget.h" +#include "message.h" +#include "backend.h" +#include "streamreader.h" +#include "phononsrc.h" +#include <QtCore> +#include <QtCore/QTimer> +#include <QtCore/QVector> +#include <QtCore/QFile> +#include <QtCore/QByteRef> +#include <QtCore/QStringList> +#include <QtCore/QEvent> +#include <QApplication> + +#define ABOUT_TO_FINNISH_TIME 2000 +#define MAX_QUEUE_TIME 20 * GST_SECOND + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ + +MediaObject::MediaObject(Backend *backend, QObject *parent) + : QObject(parent) + , MediaNode(backend, AudioSource | VideoSource) + , m_resumeState(false) + , m_oldState(Phonon::LoadingState) + , m_oldPos(0) + , m_state(Phonon::LoadingState) + , m_pendingState(Phonon::LoadingState) + , m_tickTimer(new QTimer(this)) + , m_prefinishMark(0) + , m_transitionTime(0) + , m_posAtSeek(-1) + , m_prefinishMarkReachedNotEmitted(true) + , m_aboutToFinishEmitted(false) + , m_loading(false) + , m_capsHandler(0) + , m_datasource(0) + , m_decodebin(0) + , m_audioPipe(0) + , m_videoPipe(0) + , m_totalTime(-1) + , m_bufferPercent(0) + , m_hasVideo(false) + , m_videoStreamFound(false) + , m_hasAudio(false) + , m_seekable(false) + , m_atEndOfStream(false) + , m_atStartOfStream(false) + , m_error(Phonon::NoError) + , m_pipeline(0) + , m_audioGraph(0) + , m_videoGraph(0) + , m_previousTickTime(-1) + , m_resetNeeded(false) + , m_autoplayTitles(true) + , m_availableTitles(0) + , m_currentTitle(1) +{ + qRegisterMetaType<GstCaps*>("GstCaps*"); + qRegisterMetaType<State>("State"); + + static int count = 0; + m_name = "MediaObject" + QString::number(count++); + + if (!m_backend->isValid()) { + setError(tr("Cannot start playback. \n\nCheck your Gstreamer installation and make sure you " + "\nhave libgstreamer-plugins-base installed."), Phonon::FatalError); + } else { + m_root = this; + createPipeline(); + m_backend->addBusWatcher(this); + connect(m_tickTimer, SIGNAL(timeout()), SLOT(emitTick())); + } + connect(this, SIGNAL(stateChanged(Phonon::State, Phonon::State)), + this, SLOT(notifyStateChange(Phonon::State, Phonon::State))); + +} + +MediaObject::~MediaObject() +{ + m_backend->removeBusWatcher(this); + if (m_pipeline) { + gst_element_set_state(m_pipeline, GST_STATE_NULL); + gst_object_unref(m_pipeline); + } + if (m_audioGraph) { + gst_element_set_state(m_audioGraph, GST_STATE_NULL); + gst_object_unref(m_audioGraph); + } + if (m_videoGraph) { + gst_element_set_state(m_videoGraph, GST_STATE_NULL); + gst_object_unref(m_videoGraph); + } +} + +QString stateString(const Phonon::State &state) +{ + switch (state) { + case Phonon::LoadingState: + return QString("LoadingState"); + case Phonon::StoppedState: + return QString("StoppedState"); + case Phonon::PlayingState: + return QString("PlayingState"); + case Phonon::BufferingState: + return QString("BufferingState"); + case Phonon::PausedState: + return QString("PausedState"); + case Phonon::ErrorState: + return QString("ErrorState"); + } + return QString(); +} + +void MediaObject::saveState() +{ + //Only first resumeState is respected + if (m_resumeState) + return; + + if (m_pendingState == Phonon::PlayingState || m_pendingState == Phonon::PausedState) { + m_resumeState = true; + m_oldState = m_pendingState; + m_oldPos = getPipelinePos(); + } +} + +void MediaObject::resumeState() +{ + if (m_resumeState) + QMetaObject::invokeMethod(this, "setState", Qt::QueuedConnection, Q_ARG(State, m_oldState)); +} + +void MediaObject::newPadAvailable (GstPad *pad) +{ + GstCaps *caps; + GstStructure *str; + caps = gst_pad_get_caps (pad); + if (caps) { + str = gst_caps_get_structure (caps, 0); + QString mediaString(gst_structure_get_name (str)); + + if (mediaString.startsWith("video")) { + connectVideo(pad); + } else if (mediaString.startsWith("audio")) { + connectAudio(pad); + } else { + m_backend->logMessage("Could not connect pad", Backend::Warning); + } + gst_caps_unref (caps); + } +} + +void MediaObject::cb_newpad (GstElement *decodebin, + GstPad *pad, + gboolean last, + gpointer data) +{ + Q_UNUSED(decodebin); + Q_UNUSED(pad); + Q_UNUSED(last); + Q_UNUSED(data); + + MediaObject *media = static_cast<MediaObject*>(data); + Q_ASSERT(media); + media->newPadAvailable(pad); +} + +void MediaObject::noMorePadsAvailable () +{ + if (m_missingCodecs.size() > 0) { + bool canPlay = (m_hasAudio || m_videoStreamFound); + Phonon::ErrorType error = canPlay ? Phonon::NormalError : Phonon::FatalError; + if (error == Phonon::NormalError && m_hasVideo && !m_videoStreamFound) { + m_hasVideo = false; + emit hasVideoChanged(false); + } + QString codecs = m_missingCodecs.join(", "); + setError(QString(tr("A required codec is missing. You need to install the following codec(s) to play this content: %0")).arg(codecs), error); + m_missingCodecs.clear(); + } +} + +void MediaObject::cb_no_more_pads (GstElement * decodebin, gpointer data) +{ + Q_UNUSED(decodebin); + MediaObject *media = static_cast<MediaObject*>(data); + Q_ASSERT(media); + QMetaObject::invokeMethod(media, "noMorePadsAvailable", Qt::QueuedConnection); +} + +typedef void (*Ptr_gst_pb_utils_init)(); +typedef gchar* (*Ptr_gst_pb_utils_get_codec_description)(const GstCaps *); + +void MediaObject::cb_unknown_type (GstElement *decodebin, GstPad *pad, GstCaps *caps, gpointer data) +{ + Q_UNUSED(decodebin); + Q_UNUSED(pad); + MediaObject *media = static_cast<MediaObject*>(data); + Q_ASSERT(media); + + QString value = "unknown codec"; + + // These functions require GStreamer > 0.10.12 + static Ptr_gst_pb_utils_init p_gst_pb_utils_init = 0; + static Ptr_gst_pb_utils_get_codec_description p_gst_pb_utils_get_codec_description = 0; + if (!p_gst_pb_utils_init) { + p_gst_pb_utils_init = (Ptr_gst_pb_utils_init)QLibrary::resolve(QLatin1String("gstpbutils-0.10"), 0, "gst_pb_utils_init"); + p_gst_pb_utils_get_codec_description = (Ptr_gst_pb_utils_get_codec_description)QLibrary::resolve(QLatin1String("gstpbutils-0.10"), 0, "gst_pb_utils_get_codec_description"); + if (p_gst_pb_utils_init) + p_gst_pb_utils_init(); + } + if (p_gst_pb_utils_get_codec_description) { + gchar *codecName = NULL; + codecName = p_gst_pb_utils_get_codec_description (caps); + value = QString::fromUtf8(codecName); + g_free (codecName); + } else { + // For GStreamer versions < 0.10.12 + GstStructure *str = gst_caps_get_structure (caps, 0); + value = QString::fromUtf8(gst_structure_get_name (str)); + } + media->addMissingCodecName(value); +} + +static void notifyVideoCaps(GObject *obj, GParamSpec *, gpointer data) +{ + GstPad *pad = GST_PAD(obj); + GstCaps *caps = gst_pad_get_caps (pad); + Q_ASSERT(caps); + MediaObject *media = static_cast<MediaObject*>(data); + + // We do not want any more notifications until the source changes + g_signal_handler_disconnect(pad, media->capsHandler()); + + // setVideoCaps calls loadingComplete(), meaning we cannot call it from + // the streaming thread + QMetaObject::invokeMethod(media, "setVideoCaps", Qt::QueuedConnection, Q_ARG(GstCaps *, caps)); +} + +void MediaObject::setVideoCaps(GstCaps *caps) +{ + GstStructure *str; + gint width, height; + + if ((str = gst_caps_get_structure (caps, 0))) { + if (gst_structure_get_int (str, "width", &width) && gst_structure_get_int (str, "height", &height)) { + gint aspectNum = 0; + gint aspectDenum = 0; + if (gst_structure_get_fraction(str, "pixel-aspect-ratio", &aspectNum, &aspectDenum)) { + if (aspectDenum > 0) + width = width*aspectNum/aspectDenum; + } + // Let child nodes know about our new video size + QSize size(width, height); + MediaNodeEvent event(MediaNodeEvent::VideoSizeChanged, &size); + notify(&event); + } + } + gst_caps_unref(caps); +} + +// Adds an element to the pipeline if not previously added +bool MediaObject::addToPipeline(GstElement *elem) +{ + bool success = true; + if (!GST_ELEMENT_PARENT(elem)) { // If not already in pipeline + success = gst_bin_add(GST_BIN(m_pipeline), elem); + } + return success; +} + +void MediaObject::connectVideo(GstPad *pad) +{ + GstState currentState = GST_STATE(m_pipeline); + if (addToPipeline(m_videoGraph)) { + GstPad *videopad = gst_element_get_pad (m_videoGraph, "sink"); + if (!GST_PAD_IS_LINKED (videopad) && (gst_pad_link (pad, videopad) == GST_PAD_LINK_OK)) { + gst_element_set_state(m_videoGraph, currentState == GST_STATE_PLAYING ? GST_STATE_PLAYING : GST_STATE_PAUSED); + m_videoStreamFound = true; + m_backend->logMessage("Video track connected", Backend::Info, this); + // Note that the notify::caps _must_ be installed after linking to work with Dapper + m_capsHandler = g_signal_connect(pad, "notify::caps", G_CALLBACK(notifyVideoCaps), this); + + if (!m_loading && !m_hasVideo) { + m_hasVideo = m_videoStreamFound; + emit hasVideoChanged(m_hasVideo); + } + } + gst_object_unref (videopad); + } else { + m_backend->logMessage("The video stream could not be plugged.", Backend::Info, this); + } +} + +void MediaObject::connectAudio(GstPad *pad) +{ + GstState currentState = GST_STATE(m_pipeline); + if (addToPipeline(m_audioGraph)) { + GstPad *audiopad = gst_element_get_pad (m_audioGraph, "sink"); + if (!GST_PAD_IS_LINKED (audiopad) && (gst_pad_link (pad, audiopad)==GST_PAD_LINK_OK)) { + gst_element_set_state(m_audioGraph, currentState == GST_STATE_PLAYING ? GST_STATE_PLAYING : GST_STATE_PAUSED); + m_hasAudio = true; + m_backend->logMessage("Audio track connected", Backend::Info, this); + } + gst_object_unref (audiopad); + } else { + m_backend->logMessage("The audio stream could not be plugged.", Backend::Info, this); + } +} + +void MediaObject::cb_pad_added(GstElement *decodebin, + GstPad *pad, + gpointer data) +{ + Q_UNUSED(decodebin); + GstPad *decodepad = static_cast<GstPad*>(data); + gst_pad_link (pad, decodepad); + gst_object_unref (decodepad); +} + +/** + * Create a media source from a given URL. + * + * returns true if successful + */ +bool MediaObject::createPipefromURL(const QUrl &url) +{ + // Remove any existing data source + if (m_datasource) { + gst_bin_remove(GST_BIN(m_pipeline), m_datasource); + // m_pipeline has the only ref to datasource + m_datasource = 0; + } + + // Verify that the uri can be parsed + if (!url.isValid()) { + m_backend->logMessage(QString("%1 is not a valid URI").arg(url.toString())); + return false; + } + + // Create a new datasource based on the input URL + QByteArray encoded_cstr_url = url.toEncoded(); + m_datasource = gst_element_make_from_uri(GST_URI_SRC, encoded_cstr_url.constData(), (const char*)NULL); + if (!m_datasource) + return false; + + // Link data source into pipeline + gst_bin_add(GST_BIN(m_pipeline), m_datasource); + if (!gst_element_link(m_datasource, m_decodebin)) { + // For sources with dynamic pads (such as RtspSrc) we need to connect dynamically + GstPad *decodepad = gst_element_get_pad (m_decodebin, "sink"); + g_signal_connect (m_datasource, "pad-added", G_CALLBACK (&cb_pad_added), decodepad); + } + + return true; +} + +/** + * Create a media source from a media stream + * + * returns true if successful + */ +bool MediaObject::createPipefromStream(const MediaSource &source) +{ + // Remove any existing data source + if (m_datasource) { + gst_bin_remove(GST_BIN(m_pipeline), m_datasource); + // m_pipeline has the only ref to datasource + m_datasource = 0; + } + + m_datasource = GST_ELEMENT(g_object_new(phonon_src_get_type(), NULL)); + if (!m_datasource) + return false; + + StreamReader *streamReader = new StreamReader(source); + g_object_set (G_OBJECT (m_datasource), "iodevice", streamReader, (const char*)NULL); + + // Link data source into pipeline + gst_bin_add(GST_BIN(m_pipeline), m_datasource); + if (!gst_element_link(m_datasource, m_decodebin)) { + gst_bin_remove(GST_BIN(m_pipeline), m_datasource); + return false; + } + return true; +} + +void MediaObject::createPipeline() +{ + m_pipeline = gst_pipeline_new (NULL); + gst_object_ref (GST_OBJECT (m_pipeline)); + gst_object_sink (GST_OBJECT (m_pipeline)); + + m_decodebin = gst_element_factory_make ("decodebin", NULL); + g_signal_connect (m_decodebin, "new-decoded-pad", G_CALLBACK (&cb_newpad), this); + g_signal_connect (m_decodebin, "unknown-type", G_CALLBACK (&cb_unknown_type), this); + g_signal_connect (m_decodebin, "no-more-pads", G_CALLBACK (&cb_no_more_pads), this); + + gst_bin_add(GST_BIN(m_pipeline), m_decodebin); + + // Create a bin to contain the gst elements for this medianode + + // Set up audio graph + m_audioGraph = gst_bin_new(NULL); + gst_object_ref (GST_OBJECT (m_audioGraph)); + gst_object_sink (GST_OBJECT (m_audioGraph)); + + // Note that these queues are only required for streaming content + // And should ideally be created on demand as they will disable + // pull-mode access. Also note that the max-size-time are increased to + // reduce buffer overruns as these are not gracefully handled at the moment. + m_audioPipe = gst_element_factory_make("queue", NULL); + g_object_set(G_OBJECT(m_audioPipe), "max-size-time", MAX_QUEUE_TIME, (const char*)NULL); + gst_bin_add(GST_BIN(m_audioGraph), m_audioPipe); + GstPad *audiopad = gst_element_get_pad (m_audioPipe, "sink"); + gst_element_add_pad (m_audioGraph, gst_ghost_pad_new ("sink", audiopad)); + gst_object_unref (audiopad); + + // Set up video graph + m_videoGraph = gst_bin_new(NULL); + gst_object_ref (GST_OBJECT (m_videoGraph)); + gst_object_sink (GST_OBJECT (m_videoGraph)); + + m_videoPipe = gst_element_factory_make("queue", NULL); + g_object_set(G_OBJECT(m_videoPipe), "max-size-time", MAX_QUEUE_TIME, (const char*)NULL); + gst_bin_add(GST_BIN(m_videoGraph), m_videoPipe); + GstPad *videopad = gst_element_get_pad (m_videoPipe, "sink"); + gst_element_add_pad (m_videoGraph, gst_ghost_pad_new ("sink", videopad)); + gst_object_unref (videopad); + + if (m_pipeline && m_decodebin && m_audioGraph && m_videoGraph && m_audioPipe && m_videoPipe) + m_isValid = true; + else + m_backend->logMessage("Could not create pipeline for media object", Backend::Warning); +} + +/** + * !reimp + */ +State MediaObject::state() const +{ + return m_state; +} + +/** + * !reimp + */ +bool MediaObject::hasVideo() const +{ + return m_hasVideo; +} + +/** + * !reimp + */ +bool MediaObject::isSeekable() const +{ + return m_seekable; +} + +/** + * !reimp + */ +qint64 MediaObject::currentTime() const +{ + if (m_resumeState) + return m_oldPos; + + switch (state()) { + case Phonon::PausedState: + case Phonon::BufferingState: + case Phonon::PlayingState: + return getPipelinePos(); + case Phonon::StoppedState: + case Phonon::LoadingState: + return 0; + case Phonon::ErrorState: + break; + } + return -1; +} + +/** + * !reimp + */ +qint32 MediaObject::tickInterval() const +{ + return m_tickInterval; +} + +/** + * !reimp + */ +void MediaObject::setTickInterval(qint32 newTickInterval) +{ + m_tickInterval = newTickInterval; + if (m_tickInterval <= 0) + m_tickTimer->setInterval(50); + else + m_tickTimer->setInterval(newTickInterval); +} + +/** + * !reimp + */ +void MediaObject::play() +{ + setState(Phonon::PlayingState); + m_resumeState = false; +} + +/** + * !reimp + */ +QString MediaObject::errorString() const +{ + return m_errorString; +} + +/** + * !reimp + */ +Phonon::ErrorType MediaObject::errorType() const +{ + return m_error; +} + +/** + * Set the current state of the mediaObject. + * + * !### Note that both Playing and Paused states are set immediately + * This should obviously be done in response to actual gstreamer state changes + */ +void MediaObject::setState(State newstate) +{ + if (!isValid()) + return; + + if (m_state == newstate) + return; + + if (m_loading) { + // We are still loading. The state will be requested + // when loading has completed. + m_pendingState = newstate; + return; + } + + GstState currentState; + gst_element_get_state (m_pipeline, ¤tState, NULL, 1000); + + switch (newstate) { + case Phonon::BufferingState: + m_backend->logMessage("phonon state request: buffering", Backend::Info, this); + break; + + case Phonon::PausedState: + m_backend->logMessage("phonon state request: paused", Backend::Info, this); + if (currentState == GST_STATE_PAUSED) { + changeState(Phonon::PausedState); + } else if (gst_element_set_state(m_pipeline, GST_STATE_PAUSED) != GST_STATE_CHANGE_FAILURE) { + m_pendingState = Phonon::PausedState; + } else { + m_backend->logMessage("phonon state request failed", Backend::Info, this); + } + break; + + case Phonon::StoppedState: + m_backend->logMessage("phonon state request: Stopped", Backend::Info, this); + if (currentState == GST_STATE_READY) { + changeState(Phonon::StoppedState); + } else if (gst_element_set_state(m_pipeline, GST_STATE_READY) != GST_STATE_CHANGE_FAILURE) { + m_pendingState = Phonon::StoppedState; + } else { + m_backend->logMessage("phonon state request failed", Backend::Info, this); + } + m_atEndOfStream = false; + break; + + case Phonon::PlayingState: + if (m_resetNeeded) { + // ### Note this is a workaround and it should really be gracefully + // handled by medianode when we implement live connections. + // This generally happens if medianodes have been connected after the MediaSource was set + // Note that a side-effect of this is that we resend all meta data. + gst_element_set_state(m_pipeline, GST_STATE_NULL); + m_resetNeeded = false; + // Send a source change so the X11 renderer + // will re-set the overlay + MediaNodeEvent event(MediaNodeEvent::SourceChanged); + notify(&event); + } + m_backend->logMessage("phonon state request: Playing", Backend::Info, this); + if (m_atEndOfStream) { + m_backend->logMessage("EOS already reached", Backend::Info, this); + } else if (currentState == GST_STATE_PLAYING) { + changeState(Phonon::PlayingState); + } else if (!m_atEndOfStream && gst_element_set_state(m_pipeline, GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE) { + m_pendingState = Phonon::PlayingState; + } else { + m_backend->logMessage("phonon state request failed", Backend::Info, this); + } + break; + + case Phonon::ErrorState: + m_backend->logMessage("phonon state request : Error", Backend::Warning, this); + m_backend->logMessage(QString("Last error : %0").arg(errorString()) , Backend::Warning, this); + changeState(Phonon::ErrorState); //immediately set error state + break; + + case Phonon::LoadingState: + m_backend->logMessage("phonon state request: Loading", Backend::Info, this); + changeState(Phonon::LoadingState); + break; + } +} + +/* + * Signals that the requested state has completed + * by emitting stateChanged and updates the internal state. + */ +void MediaObject::changeState(State newstate) +{ + if (newstate == m_state) + return; + + Phonon::State oldState = m_state; + m_state = newstate; // m_state must be set before emitting, since + // Error state requires that state() will return the new value + m_pendingState = newstate; + emit stateChanged(newstate, oldState); + + switch (newstate) { + case Phonon::PausedState: + m_backend->logMessage("phonon state changed: paused", Backend::Info, this); + break; + + case Phonon::BufferingState: + m_backend->logMessage("phonon state changed: buffering", Backend::Info, this); + break; + + case Phonon::PlayingState: + m_backend->logMessage("phonon state changed: Playing", Backend::Info, this); + break; + + case Phonon::StoppedState: + m_backend->logMessage("phonon state changed: Stopped", Backend::Info, this); + m_tickTimer->stop(); + break; + + case Phonon::ErrorState: + m_loading = false; + m_backend->logMessage("phonon state changed : Error", Backend::Info, this); + m_backend->logMessage(errorString(), Backend::Warning, this); + break; + + case Phonon::LoadingState: + m_backend->logMessage("phonon state changed: Loading", Backend::Info, this); + break; + } +} + +void MediaObject::setError(const QString &errorString, Phonon::ErrorType error) +{ + m_errorString = errorString; + m_error = error; + m_tickTimer->stop(); + + if (error == Phonon::FatalError) { + m_hasVideo = false; + emit hasVideoChanged(false); + gst_element_set_state(m_pipeline, GST_STATE_READY); + changeState(Phonon::ErrorState); + } else { + if (m_loading) //Flag error only after loading has completed + m_pendingState = Phonon::ErrorState; + else + changeState(Phonon::ErrorState); + } +} + +qint64 MediaObject::totalTime() const +{ + return m_totalTime; +} + +qint32 MediaObject::prefinishMark() const +{ + return m_prefinishMark; +} + +qint32 MediaObject::transitionTime() const +{ + return m_transitionTime; +} + +void MediaObject::setTransitionTime(qint32 time) +{ + m_transitionTime = time; +} + +qint64 MediaObject::remainingTime() const +{ + return totalTime() - currentTime(); +} + +MediaSource MediaObject::source() const +{ + return m_source; +} + +void MediaObject::setNextSource(const MediaSource &source) +{ + if (source.type() == MediaSource::Invalid && + source.type() == MediaSource::Empty) + return; + m_nextSource = source; +} + +/** + * Update total time value from the pipeline + */ +bool MediaObject::updateTotalTime() +{ + GstFormat format = GST_FORMAT_TIME; + gint64 duration = 0; + if (gst_element_query_duration (GST_ELEMENT(m_pipeline), &format, &duration)) { + setTotalTime(duration / GST_MSECOND); + return true; + } + return false; +} + +/** + * Checks if the current source is seekable + */ +void MediaObject::updateSeekable() +{ + if (!isValid()) + return; + + GstQuery *query; + gboolean result; + gint64 start, stop; + query = gst_query_new_seeking(GST_FORMAT_TIME); + result = gst_element_query (m_pipeline, query); + if (result) { + gboolean seekable; + GstFormat format; + gst_query_parse_seeking (query, &format, &seekable, &start, &stop); + + if (m_seekable != seekable) { + m_seekable = seekable; + emit seekableChanged(m_seekable); + } + + if (m_seekable) + m_backend->logMessage("Stream is seekable", Backend::Info, this); + else + m_backend->logMessage("Stream is non-seekable", Backend::Info, this); + } else { + m_backend->logMessage("updateSeekable query failed", Backend::Info, this); + } + gst_query_unref (query); +} + +qint64 MediaObject::getPipelinePos() const +{ + Q_ASSERT(m_pipeline); + + // Note some formats (usually mpeg) do not allow us to accurately seek to the + // beginning or end of the file so we 'fake' it here rather than exposing the front end to potential issues. + if (m_atEndOfStream) + return totalTime(); + if (m_atStartOfStream) + return 0; + if (m_posAtSeek >= 0) + return m_posAtSeek; + + gint64 pos = 0; + GstFormat format = GST_FORMAT_TIME; + gst_element_query_position (GST_ELEMENT(m_pipeline), &format, &pos); + return (pos / GST_MSECOND); +} + +/* + * Internal method to set a new total time for the media object + */ +void MediaObject::setTotalTime(qint64 newTime) +{ + + if (newTime == m_totalTime) + return; + + m_totalTime = newTime; + + emit totalTimeChanged(m_totalTime); +} + +/* + * !reimp + */ +void MediaObject::setSource(const MediaSource &source) +{ + if (!isValid()) + return; + + // We have to reset the state completely here, otherwise + // remnants of the old pipeline can result in strangenes + // such as failing duration queries etc + GstState state; + gst_element_set_state(m_pipeline, GST_STATE_NULL); + gst_element_get_state (m_pipeline, &state, NULL, 2000); + + m_source = source; + emit currentSourceChanged(m_source); + m_previousTickTime = -1; + m_missingCodecs.clear(); + + // Go into to loading state + changeState(Phonon::LoadingState); + m_loading = true; + m_resetNeeded = false; + m_resumeState = false; + m_pendingState = Phonon::StoppedState; + + // Make sure we start out unconnected + if (GST_ELEMENT_PARENT(m_audioGraph)) + gst_bin_remove(GST_BIN(m_pipeline), m_audioGraph); + if (GST_ELEMENT_PARENT(m_videoGraph)) + gst_bin_remove(GST_BIN(m_pipeline), m_videoGraph); + + // Clear any existing errors + m_aboutToFinishEmitted = false; + m_error = NoError; + m_errorString = QString(); + + m_bufferPercent = 0; + m_prefinishMarkReachedNotEmitted = true; + m_aboutToFinishEmitted = false; + m_hasAudio = false; + m_videoStreamFound = false; + setTotalTime(-1); + m_atEndOfStream = false; + + // Clear exising meta tags + m_metaData.clear(); + + switch (source.type()) { + case MediaSource::Url: { + if (createPipefromURL(source.url())) + m_loading = true; + else + setError(tr("Could not open media source.")); + } + break; + + case MediaSource::LocalFile: { + if (createPipefromURL(QUrl::fromLocalFile(source.fileName()))) + m_loading = true; + else + setError(tr("Could not open media source.")); + } + break; + + case MediaSource::Invalid: + setError(tr("Invalid source type."), Phonon::NormalError); + break; + + case MediaSource::Empty: + break; + + case MediaSource::Stream: + if (createPipefromStream(source)) + m_loading = true; + else + setError(tr("Could not open media source.")); + break; + + case MediaSource::Disc: // CD tracks can be specified by setting the url in the following way uri=cdda:4 + { + QUrl cdurl(QLatin1String("cdda://")); + if (createPipefromURL(cdurl)) + m_loading = true; + else + setError(tr("Could not open media source.")); + } + break; + + default: + m_backend->logMessage("Source type not currently supported", Backend::Warning, this); + setError(tr("Could not open media source."), Phonon::NormalError); + break; + } + + MediaNodeEvent event(MediaNodeEvent::SourceChanged); + notify(&event); + + // We need to link this node to ensure that fake sinks are connected + // before loading, otherwise the stream will be blocked + if (m_loading) + link(); + beginLoad(); +} + +void MediaObject::beginLoad() +{ + if (gst_element_set_state(m_pipeline, GST_STATE_PAUSED) != GST_STATE_CHANGE_FAILURE) { + m_backend->logMessage("Begin source load", Backend::Info, this); + } else { + setError(tr("Could not open media source.")); + } +} + +// Called when we are ready to leave the loading state +void MediaObject::loadingComplete() +{ + if (m_videoStreamFound) { + MediaNodeEvent event(MediaNodeEvent::VideoAvailable); + notify(&event); + } + getStreamInfo(); + m_loading = false; + + setState(m_pendingState); + emit metaDataChanged(m_metaData); +} + +void MediaObject::getStreamInfo() +{ + updateSeekable(); + updateTotalTime(); + + if (m_videoStreamFound != m_hasVideo) { + m_hasVideo = m_videoStreamFound; + emit hasVideoChanged(m_hasVideo); + } + + m_availableTitles = 1; + gint64 titleCount; + GstFormat format = gst_format_get_by_nick("track"); + if (gst_element_query_duration (m_pipeline, &format, &titleCount)) { + int oldAvailableTitles = m_availableTitles; + m_availableTitles = (int)titleCount; + if (m_availableTitles != oldAvailableTitles) { + emit availableTitlesChanged(m_availableTitles); + m_backend->logMessage(QString("Available titles changed: %0").arg(m_availableTitles), Backend::Info, this); + } + } + +} + +void MediaObject::setPrefinishMark(qint32 newPrefinishMark) +{ + m_prefinishMark = newPrefinishMark; + if (currentTime() < totalTime() - m_prefinishMark) // not about to finish + m_prefinishMarkReachedNotEmitted = true; +} + +void MediaObject::pause() +{ + m_backend->logMessage("pause()", Backend::Info, this); + if (state() != Phonon::PausedState) + setState(Phonon::PausedState); + m_resumeState = false; +} + +void MediaObject::stop() +{ + if (state() != Phonon::StoppedState) { + setState(Phonon::StoppedState); + m_prefinishMarkReachedNotEmitted = true; + } + m_resumeState = false; +} + +void MediaObject::seek(qint64 time) +{ + if (!isValid()) + return; + + if (isSeekable()) { + switch (state()) { + case Phonon::PlayingState: + case Phonon::StoppedState: + case Phonon::PausedState: + case Phonon::BufferingState: + m_backend->logMessage(QString("Seek to pos %0").arg(time), Backend::Info, this); + + if (time <= 0) + m_atStartOfStream = true; + else + m_atStartOfStream = false; + + m_posAtSeek = getPipelinePos(); + m_tickTimer->stop(); + + if (gst_element_seek(m_pipeline, 1.0, GST_FORMAT_TIME, + GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, + time * GST_MSECOND, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) + break; + case Phonon::LoadingState: + case Phonon::ErrorState: + return; + } + + quint64 current = currentTime(); + quint64 total = totalTime(); + + if (current < total - m_prefinishMark) + m_prefinishMarkReachedNotEmitted = true; + if (current < total - ABOUT_TO_FINNISH_TIME) + m_aboutToFinishEmitted = false; + m_atEndOfStream = false; + } +} + +void MediaObject::emitTick() +{ + if (m_resumeState) { + return; + } + + qint64 currentTime = getPipelinePos(); + qint64 totalTime = m_totalTime; + + if (m_tickInterval > 0 && currentTime != m_previousTickTime) { + emit tick(currentTime); + m_previousTickTime = currentTime; + } + if (m_state == Phonon::PlayingState) { + if (currentTime >= totalTime - m_prefinishMark) { + if (m_prefinishMarkReachedNotEmitted) { + m_prefinishMarkReachedNotEmitted = false; + emit prefinishMarkReached(totalTime - currentTime); + } + } + // Prepare load of next source + if (currentTime >= totalTime - ABOUT_TO_FINNISH_TIME) { + if (!m_aboutToFinishEmitted) { + m_aboutToFinishEmitted = true; // track is about to finish + emit aboutToFinish(); + } + } + } +} + + +/* + * Used to iterate through the gst_tag_list and extract values + */ +void foreach_tag_function(const GstTagList *list, const gchar *tag, gpointer user_data) +{ + TagMap *newData = static_cast<TagMap *>(user_data); + QString value; + GType type = gst_tag_get_type(tag); + switch (type) { + case G_TYPE_STRING: { + char *str = 0; + gst_tag_list_get_string(list, tag, &str); + value = QString::fromUtf8(str); + g_free(str); + } + break; + + case G_TYPE_BOOLEAN: { + int bval; + gst_tag_list_get_boolean(list, tag, &bval); + value = QString::number(bval); + } + break; + + case G_TYPE_INT: { + int ival; + gst_tag_list_get_int(list, tag, &ival); + value = QString::number(ival); + } + break; + + case G_TYPE_UINT: { + unsigned int uival; + gst_tag_list_get_uint(list, tag, &uival); + value = QString::number(uival); + } + break; + + case G_TYPE_FLOAT: { + float fval; + gst_tag_list_get_float(list, tag, &fval); + value = QString::number(fval); + } + break; + + case G_TYPE_DOUBLE: { + double dval; + gst_tag_list_get_double(list, tag, &dval); + value = QString::number(dval); + } + break; + + default: + //qDebug("Unsupported tag type: %s", g_type_name(type)); + break; + } + + QString key = QString(tag).toUpper(); + QString currVal = newData->value(key); + if (!value.isEmpty() && !(newData->contains(key) && currVal == value)) + newData->insert(key, value); +} + +/** + * Triggers playback after a song has completed in the current media queue + */ +void MediaObject::beginPlay() +{ + setSource(m_nextSource); + m_nextSource = MediaSource(); + m_pendingState = Phonon::PlayingState; +} + +/** + * Handle GStreamer bus messages + */ +void MediaObject::handleBusMessage(const Message &message) +{ + + if (!isValid()) + return; + + GstMessage *gstMessage = message.rawMessage(); + Q_ASSERT(m_pipeline); + + if (m_backend->debugLevel() >= Backend::Debug) { + int type = GST_MESSAGE_TYPE(gstMessage); + gchar* name = gst_element_get_name(gstMessage->src); + QString msgString = QString("Bus: %0 (%1)").arg(gst_message_type_get_name ((GstMessageType)type)).arg(name); + g_free(name); + m_backend->logMessage(msgString, Backend::Debug, this); + } + + switch (GST_MESSAGE_TYPE (gstMessage)) { + + case GST_MESSAGE_EOS: + m_backend->logMessage("EOS recieved", Backend::Info, this); + handleEndOfStream(); + break; + + case GST_MESSAGE_TAG: { + GstTagList* tag_list = 0; + gst_message_parse_tag(gstMessage, &tag_list); + if (tag_list) { + TagMap oldMap = m_metaData; // Keep a copy of the old one for reference + // Append any new meta tags to the existing tag list + gst_tag_list_foreach (tag_list, &foreach_tag_function, &m_metaData); + m_backend->logMessage("Meta tags found", Backend::Info, this); + if (oldMap != m_metaData && !m_loading) + emit metaDataChanged(m_metaData); + gst_tag_list_free(tag_list); + } + } + break; + + case GST_MESSAGE_STATE_CHANGED : { + + if (gstMessage->src != GST_OBJECT(m_pipeline)) + return; + + GstState oldState; + GstState newState; + GstState pendingState; + gst_message_parse_state_changed (gstMessage, &oldState, &newState, &pendingState); + + if (newState == pendingState) + return; + + m_posAtSeek = -1; + + switch (newState) { + + case GST_STATE_PLAYING : + m_atStartOfStream = false; + m_backend->logMessage("gstreamer: pipeline state set to playing", Backend::Info, this); + m_tickTimer->start(); + changeState(Phonon::PlayingState); + if (m_resumeState && m_oldState == Phonon::PlayingState) { + seek(m_oldPos); + m_resumeState = false; + } + break; + + case GST_STATE_NULL: + m_backend->logMessage("gstreamer: pipeline state set to null", Backend::Info, this); + m_tickTimer->stop(); + break; + + case GST_STATE_PAUSED : + m_backend->logMessage("gstreamer: pipeline state set to paused", Backend::Info, this); + m_tickTimer->start(); + if (state() == Phonon::LoadingState) { + // No_more_pads is not emitted from the decodebin in older versions (0.10.4) + noMorePadsAvailable(); + loadingComplete(); + } else if (m_resumeState && m_oldState == Phonon::PausedState) { + changeState(Phonon::PausedState); + m_resumeState = false; + break; + } else { + // A lot of autotests can break if we allow all paused changes through. + if (m_pendingState == Phonon::PausedState) { + changeState(Phonon::PausedState); + } + } + break; + + case GST_STATE_READY : + if (!m_loading && m_pendingState == Phonon::StoppedState) + changeState(Phonon::StoppedState); + m_backend->logMessage("gstreamer: pipeline state set to ready", Backend::Debug, this); + m_tickTimer->stop(); + break; + + case GST_STATE_VOID_PENDING : + m_backend->logMessage("gstreamer: pipeline state set to pending (void)", Backend::Debug, this); + m_tickTimer->stop(); + break; + } + break; + } + + case GST_MESSAGE_ERROR: { + gchar *debug; + GError *err; + QString logMessage; + gst_message_parse_error (gstMessage, &err, &debug); + gchar *errorMessage = gst_error_get_message (err->domain, err->code); + logMessage.sprintf("Error: %s Message:%s (%s) Code:%d", debug, err->message, errorMessage, err->code); + m_backend->logMessage(logMessage, Backend::Warning); + g_free(errorMessage); + g_free (debug); + + if (err->domain == GST_RESOURCE_ERROR) { + if (err->code == GST_RESOURCE_ERROR_NOT_FOUND) { + setError(tr("Could not locate media source."), Phonon::FatalError); + } else if (err->code == GST_RESOURCE_ERROR_OPEN_READ) { + setError(tr("Could not open media source."), Phonon::FatalError); + } else if (err->code == GST_RESOURCE_ERROR_BUSY) { + // We need to check if this comes from an audio device by looking at sink caps + GstPad* sinkPad = gst_element_get_static_pad(GST_ELEMENT(gstMessage->src), "sink"); + if (sinkPad) { + GstCaps *caps = gst_pad_get_caps (sinkPad); + GstStructure *str = gst_caps_get_structure (caps, 0); + if (g_strrstr (gst_structure_get_name (str), "audio")) + setError(tr("Could not open audio device. The device is already in use."), Phonon::NormalError); + else + setError(err->message, Phonon::FatalError); + gst_caps_unref (caps); + gst_object_unref (sinkPad); + } + } else { + setError(QString(err->message), Phonon::FatalError); + } + } else if (err->domain == GST_STREAM_ERROR) { + switch (err->code) { + case GST_STREAM_ERROR_WRONG_TYPE: + case GST_STREAM_ERROR_TYPE_NOT_FOUND: + setError(tr("Could not decode media source."), Phonon::FatalError); + break; + default: + setError(tr("Could not open media source."), Phonon::FatalError); + break; + } + } else { + setError(QString(err->message), Phonon::FatalError); + } + g_error_free (err); + break; + } + + case GST_MESSAGE_WARNING: { + gchar *debug; + GError *err; + gst_message_parse_warning(gstMessage, &err, &debug); + QString msgString; + msgString.sprintf("Warning: %s\nMessage:%s", debug, err->message); + m_backend->logMessage(msgString, Backend::Warning); + g_free (debug); + g_error_free (err); + break; + } + + case GST_MESSAGE_ELEMENT: { + GstMessage *gstMessage = message.rawMessage(); + const GstStructure *gstStruct = gst_message_get_structure(gstMessage); //do not free this + if (g_strrstr (gst_structure_get_name (gstStruct), "prepare-xwindow-id")) { + MediaNodeEvent videoHandleEvent(MediaNodeEvent::VideoHandleRequest); + notify(&videoHandleEvent); + } + break; + } + + case GST_MESSAGE_DURATION: { + m_backend->logMessage("GST_MESSAGE_DURATION", Backend::Debug, this); + updateTotalTime(); + break; + } + + case GST_MESSAGE_BUFFERING: { + gint percent = 0; + gst_structure_get_int (gstMessage->structure, "buffer-percent", &percent); //gst_message_parse_buffering was introduced in 0.10.11 + + if (m_bufferPercent != percent) { + emit bufferStatus(percent); + m_backend->logMessage(QString("Stream buffering %0").arg(percent), Backend::Debug, this); + m_bufferPercent = percent; + } + + if (m_state != Phonon::BufferingState) + emit stateChanged(m_state, Phonon::BufferingState); + else if (percent == 100) + emit stateChanged(Phonon::BufferingState, m_state); + break; + } + //case GST_MESSAGE_INFO: + //case GST_MESSAGE_STREAM_STATUS: + //case GST_MESSAGE_CLOCK_PROVIDE: + //case GST_MESSAGE_NEW_CLOCK: + //case GST_MESSAGE_STEP_DONE: + //case GST_MESSAGE_LATENCY: only from 0.10.12 + //case GST_MESSAGE_ASYNC_DONE: only from 0.10.13 + default: + break; + } +} + +void MediaObject::handleEndOfStream() +{ + // If the stream is not seekable ignore + // otherwise chained radio broadcasts would stop + + + if (m_atEndOfStream) + return; + + if (!m_seekable) + m_atEndOfStream = true; + + if (m_autoplayTitles && + m_availableTitles > 1 && + m_currentTitle < m_availableTitles) { + _iface_setCurrentTitle(m_currentTitle + 1); + return; + } + + if (m_nextSource.type() != MediaSource::Invalid + && m_nextSource.type() != MediaSource::Empty) { // We only emit finish when the queue is actually empty + QTimer::singleShot (qMax(0, transitionTime()), this, SLOT(beginPlay())); + } else { + m_pendingState = Phonon::PausedState; + emit finished(); + if (!m_seekable) { + setState(Phonon::StoppedState); + // Note the behavior for live streams is not properly defined + // But since we cant seek to 0, we don't have much choice other than stopping + // the stream + } else { + // Only emit paused if the finished signal + // did not result in a new state + if (m_pendingState == Phonon::PausedState) + setState(m_pendingState); + } + } +} + +// Notifes the pipeline about state changes in the media object +void MediaObject::notifyStateChange(Phonon::State newstate, Phonon::State oldstate) +{ + Q_UNUSED(oldstate); + MediaNodeEvent event(MediaNodeEvent::StateChanged, &newstate); + notify(&event); +} + +#ifndef QT_NO_PHONON_MEDIACONTROLLER +//interface management +bool MediaObject::hasInterface(Interface iface) const +{ + return iface == AddonInterface::TitleInterface; +} + +QVariant MediaObject::interfaceCall(Interface iface, int command, const QList<QVariant> ¶ms) +{ + if (hasInterface(iface)) { + + switch (iface) + { + case TitleInterface: + switch (command) + { + case availableTitles: + return _iface_availableTitles(); + case title: + return _iface_currentTitle(); + case setTitle: + _iface_setCurrentTitle(params.first().toInt()); + break; + case autoplayTitles: + return m_autoplayTitles; + case setAutoplayTitles: + m_autoplayTitles = params.first().toBool(); + break; + } + break; + default: + break; + } + } + return QVariant(); +} +#endif + +int MediaObject::_iface_availableTitles() const +{ + return m_availableTitles; +} + +int MediaObject::_iface_currentTitle() const +{ + return m_currentTitle; +} + +void MediaObject::_iface_setCurrentTitle(int title) +{ + GstFormat trackFormat = gst_format_get_by_nick("track"); + m_backend->logMessage(QString("setCurrentTitle %0").arg(title), Backend::Info, this); + if ((title == m_currentTitle) || (title < 1) || (title > m_availableTitles)) + return; + + m_currentTitle = title; + + //let's seek to the beginning of the song + if (gst_element_seek_simple(m_pipeline, trackFormat, GST_SEEK_FLAG_FLUSH, m_currentTitle - 1)) { + updateTotalTime(); + m_atEndOfStream = false; + emit titleChanged(title); + emit totalTimeChanged(totalTime()); + } +} + +} // ns Gstreamer +} // ns Phonon + +QT_END_NAMESPACE + +#include "moc_mediaobject.cpp" diff --git a/src/3rdparty/phonon/gstreamer/mediaobject.h b/src/3rdparty/phonon/gstreamer/mediaobject.h new file mode 100644 index 0000000..64b3510 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/mediaobject.h @@ -0,0 +1,294 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_GSTREAMER_MEDIAOBJECT_H +#define Phonon_GSTREAMER_MEDIAOBJECT_H + +#include "backend.h" +#include "common.h" +#include "medianode.h" +#include <phonon/mediaobjectinterface.h> +#include <phonon/addoninterface.h> + +#include <QtCore/QHash> +#include <QtCore/QString> +#include <QtCore/QVariant> +#include <QtCore/QObject> +#include <QtCore/QDate> +#include <QtCore/QEvent> +#include <QtCore/QUrl> +#include <gst/gst.h> + +QT_BEGIN_NAMESPACE + +class QTimer; +typedef QMultiMap<QString, QString> TagMap; + +namespace Phonon +{ +namespace Gstreamer +{ + +class VideoWidget; +class AudioPath; +class VideoPath; +class AudioOutput; + +class MediaObject : public QObject, public MediaObjectInterface +#ifndef QT_NO_PHONON_MEDIACONTROLLER + , public AddonInterface +#endif + , public MediaNode +{ + friend class Stream; + Q_OBJECT + Q_INTERFACES(Phonon::MediaObjectInterface +#ifndef QT_NO_PHONON_MEDIACONTROLLER + Phonon::AddonInterface +#endif + Phonon::Gstreamer::MediaNode + ) + +public: + + MediaObject(Backend *backend, QObject *parent); + ~MediaObject(); + Phonon::State state() const; + + bool hasVideo() const; + bool isSeekable() const; + + qint64 currentTime() const; + qint32 tickInterval() const; + + void setTickInterval(qint32 newTickInterval); + + void play(); + void pause(); + void stop(); + void seek(qint64 time); + + QString errorString() const; + Phonon::ErrorType errorType() const; + + QUrl url() const; + qint64 totalTime() const; + + qint32 prefinishMark() const; + void setPrefinishMark(qint32 newPrefinishMark); + + qint32 transitionTime() const; + void setTransitionTime(qint32); + qint64 remainingTime() const; + + void setSource(const MediaSource &source); + void setNextSource(const MediaSource &source); + MediaSource source() const; + + // No additional interfaces currently supported +#ifndef QT_NO_PHONON_MEDIACONTROLLER + bool hasInterface(Interface) const; + QVariant interfaceCall(Interface, int, const QList<QVariant> &); +#endif + bool isLoading() + { + return m_loading; + } + + bool audioAvailable() + { + return m_hasAudio; + } + + bool videoAvailable() + { + return m_hasVideo; + } + + GstElement *audioGraph() + { + return m_audioGraph; + } + + GstElement *videoGraph() + { + return m_videoGraph; + } + + GstElement *pipeline() + { + return m_pipeline; + }; + + gulong capsHandler() + { + return m_capsHandler; + }; + + void connectVideo(GstPad *videoPad); + void connectAudio(GstPad *audioPad); + void handleBusMessage(const Message &msg); + void handleEndOfStream(); + void addMissingCodecName(const QString &codec) { m_missingCodecs.append(codec); } + void invalidateGraph() { + m_resetNeeded = true; + if (m_state == Phonon::PlayingState || m_state == Phonon::PausedState) { + changeState(Phonon::StoppedState); + } + } + static void cb_newpad (GstElement *decodebin, GstPad *pad, gboolean last, gpointer data); + static void cb_pad_added (GstElement *decodebin, GstPad *pad, gpointer data); + static void cb_unknown_type (GstElement *decodebin, GstPad *pad, GstCaps *caps, gpointer data); + static void cb_no_more_pads (GstElement * decodebin, gpointer data); + void saveState(); + void resumeState(); + +public Q_SLOTS: + void setState(State); + +Q_SIGNALS: + void currentSourceChanged(const MediaSource &newSource); + void stateChanged(Phonon::State newstate, Phonon::State oldstate); + void tick(qint64 time); + void metaDataChanged(QMultiMap<QString, QString>); + void seekableChanged(bool); + void hasVideoChanged(bool); + + void finished(); + void prefinishMarkReached(qint32); + void aboutToFinish(); + void totalTimeChanged(qint64 length); + void bufferStatus(int percentFilled); + + QMultiMap<QString, QString> metaData(); + void setMetaData(QMultiMap<QString, QString> newData); + + // AddonInterface: + void titleChanged(int); + void availableTitlesChanged(int); + + // Not implemented + void chapterChanged(int); + void availableChaptersChanged(int); + void angleChanged(int); + void availableAnglesChanged(int); + + void availableSubtitlesChanged(); + void availableAudioChannelsChanged(); + +protected: + void beginLoad(); + void loadingComplete(); + void newPadAvailable (GstPad *pad); + void changeState(State); + void setError(const QString &errorString, Phonon::ErrorType error = NormalError); + /* + * @param encodedUrl percent-encoded QString for source compat reasons. Should change to QUrl + */ + bool createPipefromURL(const QUrl &url); + bool createPipefromStream(const MediaSource &); + +private Q_SLOTS: + void noMorePadsAvailable(); + void getStreamInfo(); + void emitTick(); + void beginPlay(); + void setVideoCaps(GstCaps *caps); + void notifyStateChange(Phonon::State newstate, Phonon::State oldstate); +protected: + GstElement *audioElement() + { + Q_ASSERT(m_audioPipe); + return m_audioPipe; + } + + GstElement *videoElement() + { + Q_ASSERT(m_videoPipe); + return m_videoPipe; + } + +private: + + // GStreamer specific : + void createPipeline(); + bool addToPipeline(GstElement *elem); + void setTotalTime(qint64 newTime); + void getStreamsInfo(); + bool updateTotalTime(); + void updateSeekable(); + qint64 getPipelinePos() const; + + int _iface_availableTitles() const; + int _iface_currentTitle() const; + void _iface_setCurrentTitle(int title); + + bool m_resumeState; + State m_oldState; + quint64 m_oldPos; + + State m_state; + State m_pendingState; + QTimer *m_tickTimer; + qint32 m_tickInterval; + + MediaSource m_source; + MediaSource m_nextSource; + qint32 m_prefinishMark; + qint32 m_transitionTime; + + qint64 m_posAtSeek; + + bool m_prefinishMarkReachedNotEmitted; + bool m_aboutToFinishEmitted; + bool m_loading; + gulong m_capsHandler; + + GstElement *m_datasource; + GstElement *m_decodebin; + + GstElement *m_audioPipe; + GstElement *m_videoPipe; + + qint64 m_totalTime; + int m_bufferPercent; + bool m_hasVideo; + bool m_videoStreamFound; + bool m_hasAudio; + bool m_seekable; + bool m_atEndOfStream; + bool m_atStartOfStream; + Phonon::ErrorType m_error; + QString m_errorString; + + GstElement *m_pipeline; + GstElement *m_audioGraph; + GstElement *m_videoGraph; + int m_previousTickTime; + bool m_resetNeeded; + QStringList m_missingCodecs; + QMultiMap<QString, QString> m_metaData; + bool m_autoplayTitles; + int m_availableTitles; + int m_currentTitle; +}; +} +} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE + +#endif // Phonon_GSTREAMER_MEDIAOBJECT_H diff --git a/src/3rdparty/phonon/gstreamer/message.cpp b/src/3rdparty/phonon/gstreamer/message.cpp new file mode 100644 index 0000000..154e0bb --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/message.cpp @@ -0,0 +1,75 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include <gst/gst.h> + +#include "message.h" + + +QT_BEGIN_NAMESPACE + +static int wuchi = qRegisterMetaType<Phonon::Gstreamer::Message>(); + +namespace Phonon +{ +namespace Gstreamer +{ + +/*! + \class gstreamer::Message + \internal +*/ +Message::Message(): + m_message(0), + m_source(0) +{} + +Message::Message(GstMessage* message, MediaObject *source): + m_message(message), + m_source(source) +{ + Q_ASSERT(m_message); + gst_message_ref(m_message); +} + +Message::Message(const Message &other) +{ + m_message = other.m_message; + gst_message_ref(m_message); + m_source = other.m_source; +} + +Message::~Message() +{ + gst_message_unref(m_message); +} + +GstMessage* Message::rawMessage() const +{ + return m_message; +} + +MediaObject *Message::source() const +{ + return m_source; +} + +} // ns gstreamer +} // ns phonon + +QT_END_NAMESPACE + diff --git a/src/3rdparty/phonon/gstreamer/message.h b/src/3rdparty/phonon/gstreamer/message.h new file mode 100644 index 0000000..cadb948 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/message.h @@ -0,0 +1,58 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_GSTREAMER_MESSAGE_H +#define Phonon_GSTREAMER_MESSAGE_H + +#include "common.h" + +#include <QtCore/QMetaType> + +#include <gst/gst.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ + +class MediaObject; +class Message +{ +public: + Message(); + Message(GstMessage* message, MediaObject *source); + ~Message(); + + GstMessage* rawMessage() const; + MediaObject *source() const; + Message(const Message &other); + +private: + GstMessage* m_message; + MediaObject *m_source; +}; + +} // ns gstreamer +} // ns phonon + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(Phonon::Gstreamer::Message) + +#endif // Phonon_GSTREAMER_MESSAGE_H diff --git a/src/3rdparty/phonon/gstreamer/phononsrc.cpp b/src/3rdparty/phonon/gstreamer/phononsrc.cpp new file mode 100644 index 0000000..f893fb5 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/phononsrc.cpp @@ -0,0 +1,257 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#include <gst/gst.h> +#include <gst/base/gstbasesrc.h> +#include "phononsrc.h" +#include "streamreader.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ + +static GstStaticPadTemplate srctemplate = + GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +GST_DEBUG_CATEGORY_STATIC (phonon_src_debug); + +// PhononSrc args +enum +{ + ARG_0, + ARG_PHONONSRC +}; + +static void phonon_src_finalize (GObject * object); + +static void phonon_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void phonon_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean phonon_src_start (GstBaseSrc * basesrc); +static gboolean phonon_src_stop (GstBaseSrc * basesrc); + +static gboolean phonon_src_is_seekable (GstBaseSrc * src); +static gboolean phonon_src_get_size (GstBaseSrc * src, guint64 * size); +static GstFlowReturn phonon_src_create (GstBaseSrc * src, guint64 offset, + guint length, GstBuffer ** buffer); + +static void _do_init (GType filesrc_type) +{ + Q_UNUSED(filesrc_type); + GST_DEBUG_CATEGORY_INIT (phonon_src_debug, "phononsrc", 0, "QIODevice element"); +} + +GST_BOILERPLATE_FULL (PhononSrc, phonon_src, GstBaseSrc, GST_TYPE_BASE_SRC, _do_init) + +// Register element details +static void phonon_src_base_init (gpointer g_class) { + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class); + static gchar longname[] = "Phonon Stream Source", + klass[] = "Source/File", + description[] = "Read from a Phonon StreamInterface", + author[] = "Nokia Corporation and/or its subsidiary(-ies) <qt-info@nokia.com>"; + GstElementDetails details = GST_ELEMENT_DETAILS (longname, + klass, + description, + author); + gst_element_class_set_details (gstelement_class, &details); + gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get (&srctemplate)); +} + +static void phonon_src_class_init (PhononSrcClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseSrcClass *gstbasesrc_class; + + gobject_class = G_OBJECT_CLASS (klass); + gstelement_class = GST_ELEMENT_CLASS (klass); + gstbasesrc_class = GST_BASE_SRC_CLASS (klass); + + gobject_class->set_property = phonon_src_set_property; + gobject_class->get_property = phonon_src_get_property; + + g_object_class_install_property (gobject_class, ARG_PHONONSRC, + g_param_spec_pointer ("iodevice", "A Phonon StreamReader", + "A Phonon::GStreamer::StreamReader to read from", GParamFlags(G_PARAM_READWRITE))); + + gobject_class->finalize = GST_DEBUG_FUNCPTR (phonon_src_finalize); + + gstbasesrc_class->start = GST_DEBUG_FUNCPTR (phonon_src_start); + gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (phonon_src_stop); + gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (phonon_src_is_seekable); + gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (phonon_src_get_size); + gstbasesrc_class->create = GST_DEBUG_FUNCPTR (phonon_src_create); +} + +static void phonon_src_init (PhononSrc * src, PhononSrcClass * g_class) +{ + Q_UNUSED(g_class); + src->device = 0; +} + +static void phonon_src_finalize (GObject * object) +{ + PhononSrc *src; + src = GST_PHONON_SRC (object); + delete src->device; + src->device = 0; + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean phonon_src_set_device(PhononSrc * src, StreamReader* device) +{ + GstState state; + // The element must be stopped in order to do this + GST_OBJECT_LOCK (src); + state = GST_STATE (src); + + if (state != GST_STATE_READY && state != GST_STATE_NULL) + goto wrong_state; + + GST_OBJECT_UNLOCK (src); + + src->device = device; + g_object_notify (G_OBJECT (src), "iodevice"); + return TRUE; + + // Error +wrong_state: + { + //GST_DEBUG_OBJECT (src, "setting location in wrong state"); + GST_OBJECT_UNLOCK (src); + return FALSE; + } +} + +static void phonon_src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) +{ + PhononSrc *src; + g_return_if_fail (GST_IS_PHONON_SRC (object)); + src = GST_PHONON_SRC (object); + + switch (prop_id) { + case ARG_PHONONSRC: + { + StreamReader *dev = (StreamReader*)(g_value_get_pointer(value)); + if (dev) + phonon_src_set_device(src, dev); + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void phonon_src_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + PhononSrc *src; + g_return_if_fail (GST_IS_PHONON_SRC (object)); + src = GST_PHONON_SRC (object); + + switch (prop_id) { + case ARG_PHONONSRC: + g_value_set_pointer(value, src->device); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstFlowReturn phonon_src_create_read (PhononSrc * src, guint64 offset, guint length, GstBuffer ** buffer) +{ + Q_ASSERT(src->device); + if (!src->device) + return GST_FLOW_ERROR; + + GstBuffer *buf = gst_buffer_new_and_alloc (length); + GST_BUFFER_SIZE (buf) = length; + GST_BUFFER_OFFSET (buf) = offset; + GST_BUFFER_OFFSET_END (buf) = offset + length; + + bool success = src->device->read(offset, length, (char*)GST_BUFFER_DATA (buf)); + //GST_LOG_OBJECT (src, "Reading %d bytes", length); + + if (success) { + *buffer = buf; + return GST_FLOW_OK; + } + + gst_mini_object_unref(GST_MINI_OBJECT(buf)); + return GST_FLOW_ERROR; +} + +static GstFlowReturn phonon_src_create (GstBaseSrc * basesrc, guint64 offset, guint length, GstBuffer ** buffer) +{ + PhononSrc *src; + GstFlowReturn ret; + src = GST_PHONON_SRC (basesrc); + ret = phonon_src_create_read (src, offset, length, buffer); + return ret; +} + +static gboolean phonon_src_is_seekable (GstBaseSrc * basesrc) +{ + PhononSrc *src = GST_PHONON_SRC (basesrc); + if (src->device) + return src->device->streamSeekable(); + return false; +} + +static gboolean phonon_src_get_size (GstBaseSrc * basesrc, guint64 * size) +{ + PhononSrc *src; + src = GST_PHONON_SRC (basesrc); + if (src->device && src->device->streamSeekable()) { + *size = src->device->streamSize(); + return TRUE; + } + *size = 0; + return FALSE; +} + +// Necessary to go to READY state +static gboolean phonon_src_start (GstBaseSrc * basesrc) +{ + Q_UNUSED(basesrc); + // Opening the device is handled by the frontend + // We can only assume it is already open + return TRUE; +} + +static gboolean phonon_src_stop (GstBaseSrc * basesrc) +{ + Q_UNUSED(basesrc); + // Closing the device is handled by the frontend + return TRUE; +} + +} +} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/gstreamer/phononsrc.h b/src/3rdparty/phonon/gstreamer/phononsrc.h new file mode 100644 index 0000000..a2cd8b3 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/phononsrc.h @@ -0,0 +1,69 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef __PHONON_SRC_H__ +#define __PHONON_SRC_H__ + +#include <sys/types.h> +#include <gst/gst.h> +#include <gst/base/gstbasesrc.h> +#include "streamreader.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ + +G_BEGIN_DECLS + +#define GST_TYPE_PHONON_SRC \ + (phonon_src_get_type()) +#define GST_PHONON_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PHONON_SRC,PhononSrc)) +#define GST_PHONON_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PHONON_SRC,PhononSrcClass)) +#define GST_IS_PHONON_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PHONON_SRC)) +#define GST_IS_PHONON_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PHONON_SRC)) + +typedef struct _PhononSrc PhononSrc; +typedef struct _PhononSrcClass PhononSrcClass; + +// PhononSrc: +struct _PhononSrc { + GstBaseSrc element; + StreamReader *device; +}; + +struct _PhononSrcClass { + GstBaseSrcClass parent_class; +}; + +GType phonon_src_get_type (void); + +G_END_DECLS + +} +} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE + + +#endif // __PHONON_SRC_H__ diff --git a/src/3rdparty/phonon/gstreamer/qwidgetvideosink.cpp b/src/3rdparty/phonon/gstreamer/qwidgetvideosink.cpp new file mode 100644 index 0000000..89d5a9d --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/qwidgetvideosink.cpp @@ -0,0 +1,221 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include <QApplication> +#include "videowidget.h" +#include "qwidgetvideosink.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ + +static GstVideoSinkClass* parentClass; + +/*! + \class gstreamer::QWidgetVideoSink + \internal +*/ + +template <VideoFormat FMT> +GstCaps* QWidgetVideoSink<FMT>::get_caps(GstBaseSink* sink) +{ + Q_UNUSED(sink); + return 0; +} + +template <> +const char* QWidgetVideoSinkClass<VideoFormat_YUV>::get_name() +{ + return "QWidgetVideoSinkYUV"; +} + +template <> +const char* QWidgetVideoSinkClass<VideoFormat_RGB>::get_name() +{ + return "QWidgetVideoSinkRGB"; +} + +template <VideoFormat FMT> +gboolean QWidgetVideoSink<FMT>::set_caps(GstBaseSink* sink, GstCaps* caps) +{ + GstStructure* data; + QWidgetVideoSink<FMT> *self = G_TYPE_CHECK_INSTANCE_CAST(sink, QWidgetVideoSinkClass<FMT>::get_type(), QWidgetVideoSink<FMT>); + + data = gst_caps_get_structure(caps, 0); + + gst_structure_get_int(data, "width", &self->width); + gst_structure_get_int(data, "height", &self->height); + gst_structure_get_int(data, "bpp", &self->bpp); + gst_structure_get_int(data, "depth", &self->depth); + return TRUE; +} + +template <VideoFormat FMT> +GstStateChangeReturn QWidgetVideoSink<FMT>::change_state(GstElement* element, GstStateChange transition) +{ + return GST_ELEMENT_CLASS(parentClass)->change_state(element, transition); +} + +template <VideoFormat FMT> +GstFlowReturn QWidgetVideoSink<FMT>::render(GstBaseSink* sink, GstBuffer* buf) +{ + GstFlowReturn rc = GST_FLOW_OK; + + if (buf != 0) + { + QWidgetVideoSink<FMT> *self = G_TYPE_CHECK_INSTANCE_CAST(sink, QWidgetVideoSinkClass<FMT>::get_type(), QWidgetVideoSink<FMT>); + QByteArray frame; + frame.resize(buf->size); + memcpy(frame.data(), buf->data, buf->size); + NewFrameEvent *frameEvent = new NewFrameEvent(frame, self->width, self->height); + QApplication::postEvent(self->renderWidget, frameEvent); + } + else + rc = GST_FLOW_ERROR; + return rc; +} + +static GstStaticPadTemplate template_factory_yuv = + GST_STATIC_PAD_TEMPLATE("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS("video/x-raw-yuv, " + "framerate = (fraction) [ 0, MAX ], " + "width = (int) [ 1, MAX ], " + "height = (int) [ 1, MAX ]," + "bpp = (int) 32")); + +static GstStaticPadTemplate template_factory_rgb = + GST_STATIC_PAD_TEMPLATE("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS("video/x-raw-rgb, " + "framerate = (fraction) [ 0, MAX ], " + "width = (int) [ 1, MAX ], " + "height = (int) [ 1, MAX ]," + "bpp = (int) 32")); + +template <VideoFormat FMT> +struct template_factory; + + +template <> +struct template_factory<VideoFormat_YUV> +{ + static GstStaticPadTemplate *getFactory() + { + return &template_factory_yuv; + } +}; + +template <> +struct template_factory<VideoFormat_RGB> +{ + static GstStaticPadTemplate *getFactory() + { + return &template_factory_rgb; + } +}; + +template <VideoFormat FMT> +void QWidgetVideoSink<FMT>::base_init(gpointer g_class) +{ + gst_element_class_add_pad_template(GST_ELEMENT_CLASS(g_class), + gst_static_pad_template_get(template_factory<FMT>::getFactory())); +} + +template <VideoFormat FMT> +void QWidgetVideoSink<FMT>::instance_init(GTypeInstance *instance, gpointer g_class) +{ + Q_UNUSED(g_class); + + QWidgetVideoSink<FMT>* self = reinterpret_cast<QWidgetVideoSink<FMT>*>(instance); + + self->renderWidget = 0; + self->width = 0; + self->height = 0; + self->bpp = 0; + self->depth = 0; +} + +// QWidgetVideoSinkClass +template <VideoFormat FMT> +void QWidgetVideoSinkClass<FMT>::class_init(gpointer g_class, gpointer class_data) +{ + Q_UNUSED(class_data); + GstBaseSinkClass* gstBaseSinkClass = (GstBaseSinkClass*)g_class; + GstElementClass* gstElementClass = (GstElementClass*)g_class; + + parentClass = reinterpret_cast<GstVideoSinkClass*>(g_type_class_peek_parent(g_class)); + + // base + gstBaseSinkClass->set_caps = QWidgetVideoSink<FMT>::set_caps; + gstBaseSinkClass->preroll = QWidgetVideoSink<FMT>::render; + gstBaseSinkClass->render = QWidgetVideoSink<FMT>::render; + + // element + gstElementClass->change_state = QWidgetVideoSink<FMT>::change_state; +} + +template <VideoFormat FMT> +GType QWidgetVideoSinkClass<FMT>::get_type() +{ + static GType type = 0; + + if (type == 0) + { + static const GTypeInfo info = + { + sizeof(QWidgetVideoSinkClass<FMT>), // class_size + QWidgetVideoSink<FMT>::base_init, // base init + NULL, // base_finalize + + QWidgetVideoSinkClass<FMT>::class_init, // class_init + NULL, // class_finalize + NULL, // class_data + + sizeof(QWidgetVideoSink<FMT>), // instance_size + 0, // n_preallocs + QWidgetVideoSink<FMT>::instance_init, // instance_init + 0 // value_table + }; + + type = g_type_register_static(GST_TYPE_VIDEO_SINK, + QWidgetVideoSinkClass<FMT>::get_name(), + &info, + GTypeFlags(0)); + } + return type; +} + +GType get_type_YUV() +{ + return QWidgetVideoSinkClass<VideoFormat_YUV>::get_type(); +} + +GType get_type_RGB() +{ + return QWidgetVideoSinkClass<VideoFormat_RGB>::get_type(); +} + +} +} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/gstreamer/qwidgetvideosink.h b/src/3rdparty/phonon/gstreamer/qwidgetvideosink.h new file mode 100644 index 0000000..73a494a --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/qwidgetvideosink.h @@ -0,0 +1,97 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_GSTREAMER_VIDEOSINK_H +#define Phonon_GSTREAMER_VIDEOSINK_H + +#include "common.h" + +#include <QtCore/QByteArray> +#include <QtCore/QEvent> + +#include <gst/video/gstvideosink.h> + +QT_BEGIN_NAMESPACE + +class NewFrameEvent : public QEvent +{ +public: + NewFrameEvent(const QByteArray &newFrame, int w, int h) : + QEvent(QEvent::User), + frame(newFrame), + width(w), + height(h) + { + } + + QByteArray frame; + int width; + int height; +}; + +namespace Phonon +{ +namespace Gstreamer +{ + +enum VideoFormat { + VideoFormat_YUV, + VideoFormat_RGB +}; + +class QWidgetVideoSinkBase +{ +public: + GstVideoSink videoSink; + + QWidget * renderWidget; + gint width; + gint height; + gint bpp; + gint depth; +}; + +template <VideoFormat FMT> +class QWidgetVideoSink : public QWidgetVideoSinkBase +{ +public: + static GstCaps* get_caps(GstBaseSink* sink); + static gboolean set_caps(GstBaseSink* sink, GstCaps* caps); + static GstStateChangeReturn change_state(GstElement* element, GstStateChange transition); + static GstFlowReturn render(GstBaseSink* sink, GstBuffer* buf); + static void base_init(gpointer g_class); + static void instance_init(GTypeInstance *instance, gpointer g_class); +}; + +template <VideoFormat FMT> +struct QWidgetVideoSinkClass +{ + GstVideoSinkClass parent_class; + static void class_init(gpointer g_class, gpointer class_data); + static GType get_type(); + static const char* get_name(); +}; + +GType get_type_YUV(); +GType get_type_RGB(); + +} +} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE + +#endif // Phonon_GSTREAMER_VIDEOSINK_H diff --git a/src/3rdparty/phonon/gstreamer/streamreader.cpp b/src/3rdparty/phonon/gstreamer/streamreader.cpp new file mode 100644 index 0000000..04fa6cc --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/streamreader.cpp @@ -0,0 +1,53 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#include "streamreader.h" +#include <QtCore/QMutex> +#include <phonon/streaminterface.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ + +bool StreamReader::read(quint64 pos, int length, char * buffer) +{ + if (currentPos() - currentBufferSize() != pos) { + if (!streamSeekable()) + return false; + setCurrentPos(pos); + } + + while (currentBufferSize() < length) { + int oldSize = currentBufferSize(); + needData(); + if (oldSize == currentBufferSize()) + return false; // We didn't get any data + } + + qMemCopy(buffer, m_buffer.data(), length); + //truncate the buffer + m_buffer = m_buffer.mid(pos); + return true; +} + +} +} + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/gstreamer/streamreader.h b/src/3rdparty/phonon/gstreamer/streamreader.h new file mode 100644 index 0000000..c2e61c8 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/streamreader.h @@ -0,0 +1,96 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_IODEVICEREADER_H +#define PHONON_IODEVICEREADER_H + +#include <phonon/mediasource.h> +#include <phonon/streaminterface.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + class MediaSource; + namespace Gstreamer + { + class StreamReader : public Phonon::StreamInterface + { + public: + + StreamReader(const Phonon::MediaSource &source) + : m_pos(0) + , m_size(0) + , m_seekable(false) + { + connectToSource(source); + } + + int currentBufferSize() const + { + return m_buffer.size(); + } + + void writeData(const QByteArray &data) { + m_pos += data.size(); + m_buffer += data; + } + + void setCurrentPos(qint64 pos) + { + m_pos = pos; + seekStream(pos); + m_buffer.clear(); + } + + quint64 currentPos() const + { + return m_pos; + } + + bool read(quint64 offset, int length, char * buffer); + + void endOfData() {} + + void setStreamSize(qint64 newSize) { + m_size = newSize; + } + + qint64 streamSize() const { + return m_size; + } + + void setStreamSeekable(bool s) { + m_seekable = s; + } + + bool streamSeekable() const { + return m_seekable; + } + +private: + QByteArray m_buffer; + quint64 m_pos; + quint64 m_size; + bool m_seekable; + }; + } +} + +QT_END_NAMESPACE + +#endif diff --git a/src/3rdparty/phonon/gstreamer/videowidget.cpp b/src/3rdparty/phonon/gstreamer/videowidget.cpp new file mode 100644 index 0000000..efc750a --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/videowidget.cpp @@ -0,0 +1,387 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "videowidget.h" +#include <QtCore/QEvent> +#include <QtGui/QResizeEvent> +#include <QtGui/QPalette> +#include <QtGui/QImage> +#include <QtGui/QPainter> +#include <QtGui/QBoxLayout> +#include <QApplication> +#include <gst/gst.h> +#include <gst/interfaces/propertyprobe.h> +#include "mediaobject.h" +#include "message.h" +#include "common.h" + +#include "glrenderer.h" +#include "widgetrenderer.h" +#include "x11renderer.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ + +VideoWidget::VideoWidget(Backend *backend, QWidget *parent) : + QWidget(parent), + MediaNode(backend, VideoSink), + m_videoBin(0), + m_renderer(0), + m_aspectRatio(Phonon::VideoWidget::AspectRatioAuto), + m_brightness(0.0), + m_hue(0.0), + m_contrast(0.0), + m_saturation(0.0), + m_scaleMode(Phonon::VideoWidget::FitInView), + m_videoBalance(0), + m_colorspace(0), + m_videoplug(0) +{ + setupVideoBin(); +} + +VideoWidget::~VideoWidget() +{ + if (m_videoBin) { + gst_element_set_state (m_videoBin, GST_STATE_NULL); + gst_object_unref (m_videoBin); + } + + if (m_renderer) + delete m_renderer; +} + + +void VideoWidget::setupVideoBin() +{ + + m_renderer = m_backend->deviceManager()->createVideoRenderer(this); + GstElement *videoSink = m_renderer->videoSink(); + + m_videoBin = gst_bin_new (NULL); + Q_ASSERT(m_videoBin); + gst_object_ref (GST_OBJECT (m_videoBin)); //Take ownership + gst_object_sink (GST_OBJECT (m_videoBin)); + + //The videoplug element is the final element before the pluggable videosink + m_videoplug = gst_element_factory_make ("identity", NULL); + + //Colorspace ensures that the output of the stream matches the input format accepted by our video sink + m_colorspace = gst_element_factory_make ("ffmpegcolorspace", NULL); + + //Video scale is used to prepare the correct aspect ratio and scale. + GstElement *videoScale = gst_element_factory_make ("videoscale", NULL); + + //We need a queue to support the tee from parent node + GstElement *queue = gst_element_factory_make ("queue", NULL); + + if (queue && m_videoBin && videoScale && m_colorspace && videoSink && m_videoplug) { + //Ensure that the bare essentials are prepared + gst_bin_add_many (GST_BIN (m_videoBin), queue, m_colorspace, m_videoplug, videoScale, videoSink, (const char*)NULL); + bool success = false; + //Video balance controls color/sat/hue in the YUV colorspace + m_videoBalance = gst_element_factory_make ("videobalance", NULL); + if (m_videoBalance) { + // For video balance to work we have to first ensure that the video is in YUV colorspace, + // then hand it off to the videobalance filter before finally converting it back to RGB. + // Hence we nede a videoFilter to convert the colorspace before and after videobalance + GstElement *m_colorspace2 = gst_element_factory_make ("ffmpegcolorspace", NULL); + gst_bin_add_many(GST_BIN(m_videoBin), m_videoBalance, m_colorspace2, (const char*)NULL); + success = gst_element_link_many(queue, m_colorspace, m_videoBalance, m_colorspace2, videoScale, m_videoplug, videoSink, (const char*)NULL); + } else { + //If video balance is not available, just connect to sink directly + success = gst_element_link_many(queue, m_colorspace, videoScale, m_videoplug, videoSink, (const char*)NULL); + } + + if (success) { + GstPad *videopad = gst_element_get_pad (queue, "sink"); + gst_element_add_pad (m_videoBin, gst_ghost_pad_new ("sink", videopad)); + gst_object_unref (videopad); + QWidget *parentWidget = qobject_cast<QWidget*>(parent()); + if (parentWidget) + parentWidget->winId(); // Due to some existing issues with alien in 4.4, + // we must currently force the creation of a parent widget. + m_isValid = true; //initialization ok, accept input + } + } +} + +void VideoWidget::paintEvent(QPaintEvent *event) +{ + Q_ASSERT(m_renderer); + m_renderer->handlePaint(event); +} + +void VideoWidget::setVisible(bool val) { + Q_ASSERT(m_renderer); + + // Disable overlays for graphics view + if (root() && window() && window()->testAttribute(Qt::WA_DontShowOnScreen) && !m_renderer->paintsOnWidget()) { + m_backend->logMessage(QString("Widget rendering forced"), Backend::Info, this); + GstElement *videoSink = m_renderer->videoSink(); + Q_ASSERT(videoSink); + + gst_element_set_state (videoSink, GST_STATE_NULL); + gst_bin_remove(GST_BIN(m_videoBin), videoSink); + delete m_renderer; + m_renderer = 0; + + // Use widgetRenderer as a fallback + m_renderer = new WidgetRenderer(this); + videoSink = m_renderer->videoSink(); + gst_bin_add(GST_BIN(m_videoBin), videoSink); + gst_element_link(m_videoplug, videoSink); + gst_element_set_state (videoSink, GST_STATE_PAUSED); + + // Request return to current state + root()->invalidateGraph(); + root()->setState(root()->state()); + } + QWidget::setVisible(val); +} + +bool VideoWidget::event(QEvent *event) +{ + if (m_renderer && m_renderer->eventFilter(event)) + return true; + return QWidget::event(event); +} + +Phonon::VideoWidget::AspectRatio VideoWidget::aspectRatio() const +{ + return m_aspectRatio; +} + +QSize VideoWidget::sizeHint() const +{ + if (!m_movieSize.isEmpty()) + return m_movieSize; + else + return QSize(640, 480); +} + +void VideoWidget::setAspectRatio(Phonon::VideoWidget::AspectRatio aspectRatio) +{ + m_aspectRatio = aspectRatio; + if (m_renderer) + m_renderer->aspectRatioChanged(aspectRatio); +} + +Phonon::VideoWidget::ScaleMode VideoWidget::scaleMode() const +{ + return m_scaleMode; +} + +QRect VideoWidget::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); +} + +/*** + * Calculates the actual rectangle the movie will be presented with + **/ +QRect VideoWidget::calculateDrawFrameRect() const +{ + QRect widgetRect = rect(); + QRect drawFrameRect; + // Set m_drawFrameRect to be the size of the smallest possible + // rect conforming to the aspect and containing the whole frame: + switch (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: + default: + drawFrameRect = QRect(0, 0, movieSize().width(), movieSize().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 (scaleMode()) { + case Phonon::VideoWidget::ScaleAndCrop: + if (frameHeight < widgetHeight) { + frameWidth *= float(widgetHeight) / float(frameHeight); + frameHeight = widgetHeight; + } + break; + case Phonon::VideoWidget::FitInView: + default: + 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; +} + +void VideoWidget::setScaleMode(Phonon::VideoWidget::ScaleMode scaleMode) +{ + m_scaleMode = scaleMode; + if (m_renderer) + m_renderer->scaleModeChanged(scaleMode); +} + +qreal VideoWidget::brightness() const +{ + return m_brightness; +} + +qreal clampedValue(qreal val) +{ + if (val > 1.0 ) + return 1.0; + else if (val < -1.0) + return -1.0; + else return val; +} + +void VideoWidget::setBrightness(qreal newValue) +{ + newValue = clampedValue(newValue); + + if (newValue == m_brightness) + return; + + m_brightness = newValue; + + if (m_videoBalance) + g_object_set(G_OBJECT(m_videoBalance), "brightness", newValue, (const char*)NULL); //gstreamer range is [-1, 1] + +} + +qreal VideoWidget::contrast() const +{ + return m_contrast; +} + +void VideoWidget::setContrast(qreal newValue) +{ + newValue = clampedValue(newValue); + + if (newValue == m_contrast) + return; + + m_contrast = newValue; + + if (m_videoBalance) + g_object_set(G_OBJECT(m_videoBalance), "contrast", (newValue + 1.0), (const char*)NULL); //gstreamer range is [0-2] +} + +qreal VideoWidget::hue() const +{ + return m_hue; +} + +void VideoWidget::setHue(qreal newValue) +{ + if (newValue == m_hue) + return; + + newValue = clampedValue(newValue); + + m_hue = newValue; + + if (m_videoBalance) + g_object_set(G_OBJECT(m_videoBalance), "hue", newValue, (const char*)NULL); //gstreamer range is [-1, 1] +} + +qreal VideoWidget::saturation() const +{ + return m_saturation; +} + +void VideoWidget::setSaturation(qreal newValue) +{ + newValue = clampedValue(newValue); + + if (newValue == m_saturation) + return; + + m_saturation = newValue; + + if (m_videoBalance) + g_object_set(G_OBJECT(m_videoBalance), "saturation", newValue + 1.0, (const char*)NULL); //gstreamer range is [0, 2] +} + + +void VideoWidget::setMovieSize(const QSize &size) +{ + m_backend->logMessage(QString("New video size %0 x %1").arg(size.width()).arg(size.height()), Backend::Info); + if (size == m_movieSize) + return; + m_movieSize = size; + widget()->updateGeometry(); + widget()->update(); + + if (m_renderer) + m_renderer->movieSizeChanged(m_movieSize); +} + +void VideoWidget::mediaNodeEvent(const MediaNodeEvent *event) +{ + switch (event->type()) { + case MediaNodeEvent::VideoSizeChanged: { + const QSize *size = static_cast<const QSize*>(event->data()); + setMovieSize(*size); + } + break; + default: + break; + } + + // Forward events to renderer + if (m_renderer) + m_renderer->handleMediaNodeEvent(event); +} + +} +} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE + +#include "moc_videowidget.cpp" diff --git a/src/3rdparty/phonon/gstreamer/videowidget.h b/src/3rdparty/phonon/gstreamer/videowidget.h new file mode 100644 index 0000000..a0ebe5f --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/videowidget.h @@ -0,0 +1,106 @@ +/* This file is part of the KDE project. + + Copyright (C) 2 //Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).007 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_GSTREAMER_VIDEOWIDGET_H +#define Phonon_GSTREAMER_VIDEOWIDGET_H + +#include <phonon/videowidget.h> +#include <phonon/videowidgetinterface.h> + +#include "backend.h" +#include "common.h" +#include "medianode.h" +#include "abstractrenderer.h" + +#include <gst/gst.h> + +QT_BEGIN_NAMESPACE + +class QString; + +namespace Phonon +{ +namespace Gstreamer +{ + +class VideoWidget : public QWidget, public Phonon::VideoWidgetInterface, public MediaNode +{ + Q_OBJECT + Q_INTERFACES(Phonon::VideoWidgetInterface Phonon::Gstreamer::MediaNode) +public: + VideoWidget(Backend *backend, QWidget *parent = 0); + ~VideoWidget(); + + void setupVideoBin(); + void paintEvent(QPaintEvent *event); + void mediaNodeEvent(const MediaNodeEvent *event); + void setVisible(bool); + + Phonon::VideoWidget::AspectRatio aspectRatio() const; + void setAspectRatio(Phonon::VideoWidget::AspectRatio aspectRatio); + Phonon::VideoWidget::ScaleMode scaleMode() const; + void setScaleMode(Phonon::VideoWidget::ScaleMode); + qreal brightness() const; + void setBrightness(qreal); + qreal contrast() const; + void setContrast(qreal); + qreal hue() const; + void setHue(qreal); + qreal saturation() const; + void setSaturation(qreal); + void setMovieSize(const QSize &size); + QSize sizeHint() const; + QRect scaleToAspect(QRect srcRect, int w, int h) const; + QRect calculateDrawFrameRect() const; + + GstElement *videoElement() + { + Q_ASSERT(m_videoBin); + return m_videoBin; + } + + QSize movieSize() const { + return m_movieSize; + } + + bool event(QEvent *); + + QWidget *widget() { + return this; + } + +protected: + GstElement *m_videoBin; + QSize m_movieSize; + AbstractRenderer *m_renderer; + +private: + Phonon::VideoWidget::AspectRatio m_aspectRatio; + qreal m_brightness, m_hue, m_contrast, m_saturation; + Phonon::VideoWidget::ScaleMode m_scaleMode; + + GstElement *m_videoBalance; + GstElement *m_colorspace; + GstElement *m_videoplug; +}; + +} +} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE + +#endif // Phonon_GSTREAMER_VIDEOWIDGET_H diff --git a/src/3rdparty/phonon/gstreamer/volumefadereffect.cpp b/src/3rdparty/phonon/gstreamer/volumefadereffect.cpp new file mode 100644 index 0000000..d7ee11b --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/volumefadereffect.cpp @@ -0,0 +1,162 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "volumefadereffect.h" +#include "common.h" +#include <QtCore> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ + +VolumeFaderEffect::VolumeFaderEffect(Backend *backend, QObject *parent) + : Effect(backend, parent, AudioSource | AudioSink) + , m_fadeCurve(Phonon::VolumeFaderEffect::Fade3Decibel) + , m_fadeTimer(0) + , m_fadeDuration(0) + , m_fadeFromVolume(0) + , m_fadeToVolume(0) +{ + m_effectElement = gst_element_factory_make ("volume", NULL); + if (m_effectElement) + init(); +} + +VolumeFaderEffect::~VolumeFaderEffect() +{ + if (m_fadeTimer) + killTimer(m_fadeTimer); +} + +GstElement* VolumeFaderEffect::createEffectBin() +{ + GstElement *audioBin = gst_bin_new(NULL); + + // We need a queue to handle tee-connections from parent node + GstElement *queue= gst_element_factory_make ("queue", NULL); + gst_bin_add(GST_BIN(audioBin), queue); + + GstElement *mconv= gst_element_factory_make ("audioconvert", NULL); + gst_bin_add(GST_BIN(audioBin), mconv); + gst_bin_add(GST_BIN(audioBin), m_effectElement); + + // Link src pad + GstPad *srcPad= gst_element_get_pad (m_effectElement, "src"); + gst_element_add_pad (audioBin, gst_ghost_pad_new ("src", srcPad)); + gst_object_unref (srcPad); + + // Link sink pad + gst_element_link_many(queue, mconv, m_effectElement, (const char*)NULL); + GstPad *sinkpad = gst_element_get_pad (queue, "sink"); + gst_element_add_pad (audioBin, gst_ghost_pad_new ("sink", sinkpad)); + gst_object_unref (sinkpad); + return audioBin; +} + +float VolumeFaderEffect::volume() const +{ + gdouble val = 0.0; + if (m_effectElement) + g_object_get(G_OBJECT(m_effectElement), "volume", &val, (const char*)NULL); + return (float)val; +} + +void VolumeFaderEffect::setVolume(float volume) +{ + g_object_set(G_OBJECT(m_effectElement), "volume", volume, (const char*)NULL); +} + +Phonon::VolumeFaderEffect::FadeCurve VolumeFaderEffect::fadeCurve() const +{ + return m_fadeCurve; +} + +void VolumeFaderEffect::setFadeCurve(Phonon::VolumeFaderEffect::FadeCurve fadeCurve) +{ + m_fadeCurve = fadeCurve; +} + +void VolumeFaderEffect::fadeTo(float targetVolume, int fadeTime) +{ + m_fadeToVolume = targetVolume; + m_fadeDuration = fadeTime; + m_fadeFromVolume = volume(); + m_fadeStartTime.start(); + + if (m_fadeTimer) + killTimer(m_fadeTimer); + m_fadeTimer = startTimer(30); +} + +void VolumeFaderEffect::updateFade() +{ + double currVal = 0.0; + float step = float(m_fadeStartTime.elapsed()) / float(m_fadeDuration); + if (step > 1){ + step = 1; + if (m_fadeTimer) { + killTimer(m_fadeTimer); + m_fadeTimer = 0; + } + } + // This is a very loose and interpretation of the API + // But in fact when fading between arbitrary values, the decibel values make no sense + // Note : seems like we will change the API to re-use names from QTimeline for this + switch (fadeCurve()) { + case Phonon::VolumeFaderEffect::Fade3Decibel: // Slow in the beginning + currVal = step * step; + break; + case Phonon::VolumeFaderEffect::Fade6Decibel: // Linear fade + currVal = step; + break; + case Phonon::VolumeFaderEffect::Fade9Decibel: // Fast in the beginning / Linear + currVal = step * 0.5 + (1.0-(1.0-step)*(1.0-step)) * 0.5; + break; + case Phonon::VolumeFaderEffect::Fade12Decibel: // Fast in the beginning + currVal = 1.0 - (1.0-step) * (1.0-step); + break; + default: + break; + } + const double volume = (1.0 - currVal) * m_fadeFromVolume + currVal * m_fadeToVolume; + setVolume(volume); +} + +bool VolumeFaderEffect::event(QEvent *event) +{ + switch (event->type()){ + case QEvent::Timer: + { + QTimerEvent *timerEvent = static_cast<QTimerEvent *>(event); + if (timerEvent->timerId() == m_fadeTimer) + updateFade(); + break; + } + default: + break; + } + return QObject::event(event); +} + +}} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE + +#include "moc_volumefadereffect.cpp" diff --git a/src/3rdparty/phonon/gstreamer/volumefadereffect.h b/src/3rdparty/phonon/gstreamer/volumefadereffect.h new file mode 100644 index 0000000..d74014c --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/volumefadereffect.h @@ -0,0 +1,70 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_GSTREAMER_VOLUMEFADEREFFECT_H +#define Phonon_GSTREAMER_VOLUMEFADEREFFECT_H + +#include "medianode.h" +#include "effect.h" + +#include <phonon/effectinterface.h> +#include <phonon/effectparameter.h> +#include <phonon/volumefaderinterface.h> + +#include <QtCore> + +#include <gst/gst.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ + class VolumeFaderEffect : public Effect, public VolumeFaderInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::VolumeFaderInterface) + + public: + VolumeFaderEffect(Backend *backend, QObject *parent = 0); + ~VolumeFaderEffect(); + + GstElement* createEffectBin(); + GstElement *audioElement() { return m_effectBin; } + bool event(QEvent *); + void updateFade(); + + // VolumeFaderInterface: + float volume() const; + void setVolume(float volume); + Phonon::VolumeFaderEffect::FadeCurve fadeCurve() const; + void setFadeCurve(Phonon::VolumeFaderEffect::FadeCurve fadeCurve); + void fadeTo(float volume, int fadeTime); + + Phonon::VolumeFaderEffect::FadeCurve m_fadeCurve; + int m_fadeTimer; + int m_fadeDuration; + float m_fadeFromVolume; + float m_fadeToVolume; + QTime m_fadeStartTime; + }; +}} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE + +#endif // Phonon_GSTREAMER_VOLUMEFADEREFFECT_H diff --git a/src/3rdparty/phonon/gstreamer/widgetrenderer.cpp b/src/3rdparty/phonon/gstreamer/widgetrenderer.cpp new file mode 100644 index 0000000..d4a411f --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/widgetrenderer.cpp @@ -0,0 +1,150 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include <QtGui/QPainter> +#include <gst/gst.h> +#include "common.h" +#include "message.h" +#include "mediaobject.h" +#include "qwidgetvideosink.h" +#include "widgetrenderer.h" +#include "qrgb.h" + +// support old OpenGL installations (1.2) +// assume that if TEXTURE0 isn't defined, none are +#ifndef GL_TEXTURE0 +# define GL_TEXTURE0 0x84C0 +# define GL_TEXTURE1 0x84C1 +# define GL_TEXTURE2 0x84C2 +#endif + +QT_BEGIN_NAMESPACE + +static void frameRendered() +{ + static QString displayFps = qgetenv("PHONON_GST_FPS"); + if (displayFps.isEmpty()) + return; + + static int frames = 0; + static QTime lastTime = QTime::currentTime(); + QTime time = QTime::currentTime(); + + int delta = lastTime.msecsTo(time); + if (delta > 2000) { + printf("FPS: %f\n", 1000.0 * frames / qreal(delta)); + lastTime = time; + frames = 0; + } + + ++frames; +} + +namespace Phonon +{ +namespace Gstreamer +{ + +WidgetRenderer::WidgetRenderer(VideoWidget *videoWidget) + : AbstractRenderer(videoWidget) + , m_width(0) + , m_height(0) +{ + videoWidget->backend()->logMessage("Creating QWidget renderer"); + if ((m_videoSink = GST_ELEMENT(g_object_new(get_type_RGB(), NULL)))) { + gst_object_ref (GST_OBJECT (m_videoSink)); //Take ownership + gst_object_sink (GST_OBJECT (m_videoSink)); + + QWidgetVideoSinkBase* sink = reinterpret_cast<QWidgetVideoSinkBase*>(m_videoSink); + // Let the videosink know which widget to direct frame updates to + sink->renderWidget = videoWidget; + } + + // Clear the background with black by default + QPalette palette; + palette.setColor(QPalette::Background, Qt::black); + m_videoWidget->setPalette(palette); + m_videoWidget->setAutoFillBackground(true); + m_videoWidget->setAttribute(Qt::WA_NoSystemBackground, false); + m_videoWidget->setAttribute(Qt::WA_PaintOnScreen, false); +} + +void WidgetRenderer::setNextFrame(const QByteArray &array, int w, int h) +{ + if (m_videoWidget->root()->state() == Phonon::LoadingState) + return; + + m_frame = QImage(); + { + m_frame = QImage((uchar *)array.constData(), w, h, QImage::Format_RGB32); + } + + m_array = array; + m_width = w; + m_height = h; + + m_videoWidget->update(); +} + +void WidgetRenderer::handleMediaNodeEvent(const MediaNodeEvent *event) +{ + switch (event->type()) { + case MediaNodeEvent::SourceChanged: + { + clearFrame(); + break; + } + default: + break; + } +} + +void WidgetRenderer::clearFrame() +{ + m_frame = QImage(); + m_array = QByteArray(); + m_videoWidget->update(); +} + +const QImage &WidgetRenderer::currentFrame() const +{ + return m_frame; +} + +void WidgetRenderer::handlePaint(QPaintEvent *event) +{ + Q_UNUSED(event); + QPainter painter(m_videoWidget); + m_drawFrameRect = m_videoWidget->calculateDrawFrameRect(); + painter.drawImage(drawFrameRect(), currentFrame()); + frameRendered(); +} + +bool WidgetRenderer::eventFilter(QEvent * event) +{ + if (event->type() == QEvent::User) { + NewFrameEvent *frameEvent= static_cast <NewFrameEvent *>(event); + setNextFrame(frameEvent->frame, frameEvent->width, frameEvent->height); + return true; + } + return false; +} + +} +} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/gstreamer/widgetrenderer.h b/src/3rdparty/phonon/gstreamer/widgetrenderer.h new file mode 100644 index 0000000..ff64fa7 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/widgetrenderer.h @@ -0,0 +1,63 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_GSTREAMER_WIDGETRENDERER_H +#define Phonon_GSTREAMER_WIDGETRENDERER_H + +#include "videowidget.h" +#include "common.h" + +#ifndef QT_NO_OPENGL +#include <QtOpenGL/QGLFormat> +#include <QtOpenGL/QGLWidget> +#endif + +QT_BEGIN_NAMESPACE + +class QString; + +namespace Phonon +{ +namespace Gstreamer +{ + +class WidgetRenderer : public AbstractRenderer +{ +public: + WidgetRenderer(VideoWidget *videoWidget); + bool eventFilter(QEvent * event); + void handlePaint(QPaintEvent *paintEvent); + void handleMediaNodeEvent(const MediaNodeEvent *event); + const QImage& currentFrame() const; + QRect drawFrameRect() const { return m_drawFrameRect; } + void setNextFrame(const QByteArray &array, int width, int height); + bool frameIsSet() { return !m_array.isNull(); } + void clearFrame(); +private: + mutable QImage m_frame; + QByteArray m_array; + int m_width; + int m_height; + QRect m_drawFrameRect; +}; + +} +} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE + +#endif // Phonon_GSTREAMER_WIDGETRENDERER_H diff --git a/src/3rdparty/phonon/gstreamer/x11renderer.cpp b/src/3rdparty/phonon/gstreamer/x11renderer.cpp new file mode 100644 index 0000000..73877a8 --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/x11renderer.cpp @@ -0,0 +1,194 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "videowidget.h" +#include "x11renderer.h" + +#ifndef Q_WS_QWS + +#include <QtGui/QPalette> +#include <QtGui/QApplication> +#include <QtGui/QPainter> +#include <X11/Xlib.h> +#include <gst/gst.h> +#include <gst/interfaces/xoverlay.h> +#include <gst/interfaces/propertyprobe.h> +#include "common.h" +#include "mediaobject.h" +#include "message.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace Gstreamer +{ + +class OverlayWidget : public QWidget +{ +public: + OverlayWidget(VideoWidget *videoWidget, X11Renderer *renderer) : + QWidget(videoWidget), + m_videoWidget(videoWidget), + m_renderer(renderer) { } + void paintEvent(QPaintEvent *) { + Phonon::State state = m_videoWidget->root() ? m_videoWidget->root()->state() : Phonon::LoadingState; + if (state == Phonon::PlayingState || state == Phonon::PausedState) { + m_renderer->windowExposed(); + } else { + QPainter painter(this); + painter.fillRect(m_videoWidget->rect(), m_videoWidget->palette().background()); + } + } +private: + VideoWidget *m_videoWidget; + X11Renderer *m_renderer; +}; + +X11Renderer::X11Renderer(VideoWidget *videoWidget) + : AbstractRenderer(videoWidget) +{ + m_renderWidget = new OverlayWidget(videoWidget, this); + videoWidget->backend()->logMessage("Creating X11 overlay renderer"); + QPalette palette; + palette.setColor(QPalette::Background, Qt::black); + m_videoWidget->setPalette(palette); + m_videoWidget->setAutoFillBackground(true); + m_renderWidget->setMouseTracking(true); + m_videoSink = createVideoSink(); + aspectRatioChanged(videoWidget->aspectRatio()); + setOverlay(); +} + +X11Renderer::~X11Renderer() +{ + m_renderWidget->setAttribute(Qt::WA_PaintOnScreen, false); + m_renderWidget->setAttribute(Qt::WA_NoSystemBackground, false); + delete m_renderWidget; +} + +GstElement* X11Renderer::createVideoSink() +{ + GstElement *videoSink = gst_element_factory_make ("xvimagesink", NULL); + if (videoSink) { + // Check if the xv sink is usable + if (gst_element_set_state(videoSink, GST_STATE_READY) != GST_STATE_CHANGE_SUCCESS) { + gst_object_unref(GST_OBJECT(videoSink)); + videoSink = 0; + } else { + // Note that this should not really be neccessary as these are + // default values, though under certain conditions values are retained + // even between application instances. (reproducible on 0.10.16/Gutsy) + g_object_set(G_OBJECT(videoSink), "brightness", 0, (const char*)NULL); + g_object_set(G_OBJECT(videoSink), "contrast", 0, (const char*)NULL); + g_object_set(G_OBJECT(videoSink), "hue", 0, (const char*)NULL); + g_object_set(G_OBJECT(videoSink), "saturation", 0, (const char*)NULL); + } + } + + if (!videoSink) + videoSink = gst_element_factory_make ("ximagesink", NULL); + + gst_object_ref (GST_OBJECT (videoSink)); //Take ownership + gst_object_sink (GST_OBJECT (videoSink)); + + return videoSink; +} + +void X11Renderer::handleMediaNodeEvent(const MediaNodeEvent *event) +{ + switch (event->type()) { + case MediaNodeEvent::SourceChanged: + setOverlay(); // We need to do this whenever the pipeline is reset + break; // otherwise the videosink will open in its own window + default: + break; + } +} + + +void X11Renderer::aspectRatioChanged(Phonon::VideoWidget::AspectRatio) +{ + if (m_renderWidget) { + m_renderWidget->setGeometry(m_videoWidget->calculateDrawFrameRect()); + } +} + +void X11Renderer::scaleModeChanged(Phonon::VideoWidget::ScaleMode) +{ + if (m_renderWidget) { + m_renderWidget->setGeometry(m_videoWidget->calculateDrawFrameRect()); + } +} + +void X11Renderer::movieSizeChanged(const QSize &movieSize) +{ + Q_UNUSED(movieSize); + if (m_renderWidget) { + m_renderWidget->setGeometry(m_videoWidget->calculateDrawFrameRect()); + } +} + +bool X11Renderer::eventFilter(QEvent *e) +{ + if (e->type() == QEvent::Show) { + // Setting these values ensures smooth resizing since it + // will prevent the system from clearing the background + m_renderWidget->setAttribute(Qt::WA_NoSystemBackground, true); + m_renderWidget->setAttribute(Qt::WA_PaintOnScreen, true); + setOverlay(); + } else if (e->type() == QEvent::Resize) { + // This is a workaround for missing background repaints + // when reducing window size + m_renderWidget->setGeometry(m_videoWidget->calculateDrawFrameRect()); + windowExposed(); + } + return false; +} + +void X11Renderer::handlePaint(QPaintEvent *) +{ + QPainter painter(m_videoWidget); + painter.fillRect(m_videoWidget->rect(), m_videoWidget->palette().background()); +} + +void X11Renderer::setOverlay() +{ + if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { + WId windowId = m_renderWidget->winId(); + // Even if we have created a winId at this point, other X applications + // need to be aware of it. + QApplication::syncX(); + gst_x_overlay_set_xwindow_id ( GST_X_OVERLAY(m_videoSink) , windowId ); + } + windowExposed(); + m_overlaySet = true; +} + +void X11Renderer::windowExposed() +{ + QApplication::syncX(); + if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) + gst_x_overlay_expose(GST_X_OVERLAY(m_videoSink)); +} + +} +} //namespace Phonon::Gstreamer + +QT_END_NAMESPACE + +#endif // Q_WS_QWS diff --git a/src/3rdparty/phonon/gstreamer/x11renderer.h b/src/3rdparty/phonon/gstreamer/x11renderer.h new file mode 100644 index 0000000..f7140da --- /dev/null +++ b/src/3rdparty/phonon/gstreamer/x11renderer.h @@ -0,0 +1,68 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_GSTREAMER_X11RENDERER_H +#define Phonon_GSTREAMER_X11RENDERER_H + +#include "videowidget.h" +#include "common.h" + +#include <QtGui/QWidget> + +#include <gst/gst.h> + +QT_BEGIN_NAMESPACE + +#ifndef Q_WS_QWS + +class QString; + +namespace Phonon +{ +namespace Gstreamer +{ + +class OverlayWidget; +class X11Renderer : public AbstractRenderer +{ +public: + X11Renderer(VideoWidget *videoWidget); + ~X11Renderer(); + void handlePaint(QPaintEvent *event); + void aspectRatioChanged(Phonon::VideoWidget::AspectRatio aspectRatio); + void scaleModeChanged(Phonon::VideoWidget::ScaleMode scaleMode); + void movieSizeChanged(const QSize &movieSize); + void handleMediaNodeEvent(const MediaNodeEvent *event); + bool eventFilter(QEvent *); + bool paintsOnWidget() { return false; } + bool overlaySet() const { return m_overlaySet; } + void setOverlay(); + void windowExposed(); + GstElement *createVideoSink(); +private: + OverlayWidget *m_renderWidget; + bool m_overlaySet; +}; + +} +} //namespace Phonon::Gstreamer + +#endif // Q_WS_QWS + +QT_END_NAMESPACE + +#endif // Phonon_GSTREAMER_X11RENDERER_H diff --git a/src/3rdparty/phonon/includes/CMakeLists.txt b/src/3rdparty/phonon/includes/CMakeLists.txt new file mode 100644 index 0000000..349acd8 --- /dev/null +++ b/src/3rdparty/phonon/includes/CMakeLists.txt @@ -0,0 +1,49 @@ +install( FILES + Phonon/AbstractAudioOutput + Phonon/AbstractMediaStream + Phonon/AbstractVideoOutput + Phonon/AddonInterface + Phonon/AudioDevice + Phonon/AudioDeviceEnumerator + Phonon/AudioOutput + Phonon/AudioOutputDevice + Phonon/AudioOutputDeviceModel + Phonon/AudioOutputInterface + Phonon/BackendCapabilities + Phonon/BackendInterface + Phonon/Effect + Phonon/EffectDescription + Phonon/EffectDescriptionModel + Phonon/EffectInterface + Phonon/EffectParameter + Phonon/EffectWidget + Phonon/Global + Phonon/MediaController + Phonon/MediaNode + Phonon/MediaObject + Phonon/MediaObjectInterface + Phonon/MediaSource + Phonon/ObjectDescription + Phonon/ObjectDescriptionModel + Phonon/Path + Phonon/PlatformPlugin + Phonon/SeekSlider + Phonon/StreamInterface + Phonon/VideoPlayer + Phonon/VideoWidget + Phonon/VideoWidgetInterface + Phonon/VolumeFaderEffect + Phonon/VolumeFaderInterface + Phonon/VolumeSlider +DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/Phonon) + +install(FILES + Phonon/Experimental/AbstractVideoDataOutput + Phonon/Experimental/AudioDataOutput + Phonon/Experimental/SnapshotInterface + Phonon/Experimental/VideoDataOutput + Phonon/Experimental/VideoDataOutputInterface + Phonon/Experimental/VideoFrame + Phonon/Experimental/VideoFrame2 + Phonon/Experimental/Visualization +DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/Phonon/Experimental) diff --git a/src/3rdparty/phonon/includes/Phonon/AbstractAudioOutput b/src/3rdparty/phonon/includes/Phonon/AbstractAudioOutput new file mode 100644 index 0000000..46d7668 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/AbstractAudioOutput @@ -0,0 +1 @@ +#include "../../phonon/abstractaudiooutput.h" diff --git a/src/3rdparty/phonon/includes/Phonon/AbstractMediaStream b/src/3rdparty/phonon/includes/Phonon/AbstractMediaStream new file mode 100644 index 0000000..77837cf --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/AbstractMediaStream @@ -0,0 +1 @@ +#include "../../phonon/abstractmediastream.h" diff --git a/src/3rdparty/phonon/includes/Phonon/AbstractVideoOutput b/src/3rdparty/phonon/includes/Phonon/AbstractVideoOutput new file mode 100644 index 0000000..da05eb0 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/AbstractVideoOutput @@ -0,0 +1 @@ +#include "../../phonon/abstractvideooutput.h" diff --git a/src/3rdparty/phonon/includes/Phonon/AddonInterface b/src/3rdparty/phonon/includes/Phonon/AddonInterface new file mode 100644 index 0000000..448993a --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/AddonInterface @@ -0,0 +1 @@ +#include "../../phonon/addoninterface.h" diff --git a/src/3rdparty/phonon/includes/Phonon/AudioDevice b/src/3rdparty/phonon/includes/Phonon/AudioDevice new file mode 100644 index 0000000..b1b5162 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/AudioDevice @@ -0,0 +1 @@ +#include "../../phonon/audiodevice.h" diff --git a/src/3rdparty/phonon/includes/Phonon/AudioDeviceEnumerator b/src/3rdparty/phonon/includes/Phonon/AudioDeviceEnumerator new file mode 100644 index 0000000..d971a3d --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/AudioDeviceEnumerator @@ -0,0 +1 @@ +#include "../../phonon/audiodeviceenumerator.h" diff --git a/src/3rdparty/phonon/includes/Phonon/AudioOutput b/src/3rdparty/phonon/includes/Phonon/AudioOutput new file mode 100644 index 0000000..d67ac91 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/AudioOutput @@ -0,0 +1 @@ +#include "../../phonon/audiooutput.h" diff --git a/src/3rdparty/phonon/includes/Phonon/AudioOutputDevice b/src/3rdparty/phonon/includes/Phonon/AudioOutputDevice new file mode 100644 index 0000000..3f75a1d --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/AudioOutputDevice @@ -0,0 +1 @@ +#include "../../phonon/objectdescription.h" diff --git a/src/3rdparty/phonon/includes/Phonon/AudioOutputDeviceModel b/src/3rdparty/phonon/includes/Phonon/AudioOutputDeviceModel new file mode 100644 index 0000000..eae8aec --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/AudioOutputDeviceModel @@ -0,0 +1 @@ +#include "../../phonon/objectdescriptionmodel.h" diff --git a/src/3rdparty/phonon/includes/Phonon/AudioOutputInterface b/src/3rdparty/phonon/includes/Phonon/AudioOutputInterface new file mode 100644 index 0000000..b0ef2e7 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/AudioOutputInterface @@ -0,0 +1 @@ +#include "../../phonon/audiooutputinterface.h" diff --git a/src/3rdparty/phonon/includes/Phonon/BackendCapabilities b/src/3rdparty/phonon/includes/Phonon/BackendCapabilities new file mode 100644 index 0000000..b123613 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/BackendCapabilities @@ -0,0 +1 @@ +#include "../../phonon/backendcapabilities.h" diff --git a/src/3rdparty/phonon/includes/Phonon/BackendInterface b/src/3rdparty/phonon/includes/Phonon/BackendInterface new file mode 100644 index 0000000..389c124 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/BackendInterface @@ -0,0 +1 @@ +#include "../../phonon/backendinterface.h" diff --git a/src/3rdparty/phonon/includes/Phonon/Effect b/src/3rdparty/phonon/includes/Phonon/Effect new file mode 100644 index 0000000..009eb15 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/Effect @@ -0,0 +1 @@ +#include "../../phonon/effect.h" diff --git a/src/3rdparty/phonon/includes/Phonon/EffectDescription b/src/3rdparty/phonon/includes/Phonon/EffectDescription new file mode 100644 index 0000000..3f75a1d --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/EffectDescription @@ -0,0 +1 @@ +#include "../../phonon/objectdescription.h" diff --git a/src/3rdparty/phonon/includes/Phonon/EffectDescriptionModel b/src/3rdparty/phonon/includes/Phonon/EffectDescriptionModel new file mode 100644 index 0000000..eae8aec --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/EffectDescriptionModel @@ -0,0 +1 @@ +#include "../../phonon/objectdescriptionmodel.h" diff --git a/src/3rdparty/phonon/includes/Phonon/EffectInterface b/src/3rdparty/phonon/includes/Phonon/EffectInterface new file mode 100644 index 0000000..d5a5bc3 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/EffectInterface @@ -0,0 +1 @@ +#include "../../phonon/effectinterface.h" diff --git a/src/3rdparty/phonon/includes/Phonon/EffectParameter b/src/3rdparty/phonon/includes/Phonon/EffectParameter new file mode 100644 index 0000000..301fe16 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/EffectParameter @@ -0,0 +1 @@ +#include "../../phonon/effectparameter.h" diff --git a/src/3rdparty/phonon/includes/Phonon/EffectWidget b/src/3rdparty/phonon/includes/Phonon/EffectWidget new file mode 100644 index 0000000..72e687f --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/EffectWidget @@ -0,0 +1 @@ +#include "../../phonon/effectwidget.h" diff --git a/src/3rdparty/phonon/includes/Phonon/Experimental/AbstractVideoDataOutput b/src/3rdparty/phonon/includes/Phonon/Experimental/AbstractVideoDataOutput new file mode 100644 index 0000000..c019215 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/Experimental/AbstractVideoDataOutput @@ -0,0 +1 @@ +#include "../../../phonon/experimental/abstractvideodataoutput.h" diff --git a/src/3rdparty/phonon/includes/Phonon/Experimental/AudioDataOutput b/src/3rdparty/phonon/includes/Phonon/Experimental/AudioDataOutput new file mode 100644 index 0000000..06d181d --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/Experimental/AudioDataOutput @@ -0,0 +1 @@ +#include "../../../phonon/experimental/audiodataoutput.h" diff --git a/src/3rdparty/phonon/includes/Phonon/Experimental/SnapshotInterface b/src/3rdparty/phonon/includes/Phonon/Experimental/SnapshotInterface new file mode 100644 index 0000000..5e59306 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/Experimental/SnapshotInterface @@ -0,0 +1 @@ +#include "../../../phonon/experimental/snapshotinterface.h" diff --git a/src/3rdparty/phonon/includes/Phonon/Experimental/VideoDataOutput b/src/3rdparty/phonon/includes/Phonon/Experimental/VideoDataOutput new file mode 100644 index 0000000..9878ccf --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/Experimental/VideoDataOutput @@ -0,0 +1 @@ +#include "../../../phonon/experimental/videodataoutput.h" diff --git a/src/3rdparty/phonon/includes/Phonon/Experimental/VideoDataOutputInterface b/src/3rdparty/phonon/includes/Phonon/Experimental/VideoDataOutputInterface new file mode 100644 index 0000000..870f683 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/Experimental/VideoDataOutputInterface @@ -0,0 +1 @@ +#include "../../../phonon/experimental/videodataoutputinterface.h" diff --git a/src/3rdparty/phonon/includes/Phonon/Experimental/VideoFrame b/src/3rdparty/phonon/includes/Phonon/Experimental/VideoFrame new file mode 100644 index 0000000..97c137e --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/Experimental/VideoFrame @@ -0,0 +1 @@ +#include "../../../phonon/experimental/videoframe.h" diff --git a/src/3rdparty/phonon/includes/Phonon/Experimental/VideoFrame2 b/src/3rdparty/phonon/includes/Phonon/Experimental/VideoFrame2 new file mode 100644 index 0000000..29d1af9 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/Experimental/VideoFrame2 @@ -0,0 +1 @@ +#include "../../../phonon/experimental/videoframe2.h" diff --git a/src/3rdparty/phonon/includes/Phonon/Experimental/Visualization b/src/3rdparty/phonon/includes/Phonon/Experimental/Visualization new file mode 100644 index 0000000..b5aab38 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/Experimental/Visualization @@ -0,0 +1 @@ +#include "../../../phonon/experimental/visualization.h" diff --git a/src/3rdparty/phonon/includes/Phonon/Global b/src/3rdparty/phonon/includes/Phonon/Global new file mode 100644 index 0000000..4215e3b --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/Global @@ -0,0 +1 @@ +#include "../../phonon/phononnamespace.h" diff --git a/src/3rdparty/phonon/includes/Phonon/MediaController b/src/3rdparty/phonon/includes/Phonon/MediaController new file mode 100644 index 0000000..666be3e --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/MediaController @@ -0,0 +1 @@ +#include "../../phonon/mediacontroller.h" diff --git a/src/3rdparty/phonon/includes/Phonon/MediaNode b/src/3rdparty/phonon/includes/Phonon/MediaNode new file mode 100644 index 0000000..8083158 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/MediaNode @@ -0,0 +1 @@ +#include "../../phonon/medianode.h" diff --git a/src/3rdparty/phonon/includes/Phonon/MediaObject b/src/3rdparty/phonon/includes/Phonon/MediaObject new file mode 100644 index 0000000..fa6d33b --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/MediaObject @@ -0,0 +1 @@ +#include "../../phonon/mediaobject.h" diff --git a/src/3rdparty/phonon/includes/Phonon/MediaObjectInterface b/src/3rdparty/phonon/includes/Phonon/MediaObjectInterface new file mode 100644 index 0000000..c78243b --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/MediaObjectInterface @@ -0,0 +1 @@ +#include "../../phonon/mediaobjectinterface.h" diff --git a/src/3rdparty/phonon/includes/Phonon/MediaSource b/src/3rdparty/phonon/includes/Phonon/MediaSource new file mode 100644 index 0000000..6ee2a72 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/MediaSource @@ -0,0 +1 @@ +#include "../../phonon/mediasource.h" diff --git a/src/3rdparty/phonon/includes/Phonon/ObjectDescription b/src/3rdparty/phonon/includes/Phonon/ObjectDescription new file mode 100644 index 0000000..3f75a1d --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/ObjectDescription @@ -0,0 +1 @@ +#include "../../phonon/objectdescription.h" diff --git a/src/3rdparty/phonon/includes/Phonon/ObjectDescriptionModel b/src/3rdparty/phonon/includes/Phonon/ObjectDescriptionModel new file mode 100644 index 0000000..eae8aec --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/ObjectDescriptionModel @@ -0,0 +1 @@ +#include "../../phonon/objectdescriptionmodel.h" diff --git a/src/3rdparty/phonon/includes/Phonon/Path b/src/3rdparty/phonon/includes/Phonon/Path new file mode 100644 index 0000000..1b3d2d5 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/Path @@ -0,0 +1 @@ +#include "../../phonon/path.h" diff --git a/src/3rdparty/phonon/includes/Phonon/PlatformPlugin b/src/3rdparty/phonon/includes/Phonon/PlatformPlugin new file mode 100644 index 0000000..258f339 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/PlatformPlugin @@ -0,0 +1 @@ +#include "../../phonon/platformplugin.h" diff --git a/src/3rdparty/phonon/includes/Phonon/SeekSlider b/src/3rdparty/phonon/includes/Phonon/SeekSlider new file mode 100644 index 0000000..3dde9a8 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/SeekSlider @@ -0,0 +1 @@ +#include "../../phonon/seekslider.h" diff --git a/src/3rdparty/phonon/includes/Phonon/StreamInterface b/src/3rdparty/phonon/includes/Phonon/StreamInterface new file mode 100644 index 0000000..efd8c20 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/StreamInterface @@ -0,0 +1 @@ +#include "../../phonon/streaminterface.h" diff --git a/src/3rdparty/phonon/includes/Phonon/VideoPlayer b/src/3rdparty/phonon/includes/Phonon/VideoPlayer new file mode 100644 index 0000000..68c0fd3 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/VideoPlayer @@ -0,0 +1 @@ +#include "../../phonon/videoplayer.h" diff --git a/src/3rdparty/phonon/includes/Phonon/VideoWidget b/src/3rdparty/phonon/includes/Phonon/VideoWidget new file mode 100644 index 0000000..9420f6b --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/VideoWidget @@ -0,0 +1 @@ +#include "../../phonon/videowidget.h" diff --git a/src/3rdparty/phonon/includes/Phonon/VideoWidgetInterface b/src/3rdparty/phonon/includes/Phonon/VideoWidgetInterface new file mode 100644 index 0000000..173dd33 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/VideoWidgetInterface @@ -0,0 +1 @@ +#include "../../phonon/videowidgetinterface.h" diff --git a/src/3rdparty/phonon/includes/Phonon/VolumeFaderEffect b/src/3rdparty/phonon/includes/Phonon/VolumeFaderEffect new file mode 100644 index 0000000..9e64400 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/VolumeFaderEffect @@ -0,0 +1 @@ +#include "../../phonon/volumefadereffect.h" diff --git a/src/3rdparty/phonon/includes/Phonon/VolumeFaderInterface b/src/3rdparty/phonon/includes/Phonon/VolumeFaderInterface new file mode 100644 index 0000000..47bfe26 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/VolumeFaderInterface @@ -0,0 +1 @@ +#include "../../phonon/volumefaderinterface.h" diff --git a/src/3rdparty/phonon/includes/Phonon/VolumeSlider b/src/3rdparty/phonon/includes/Phonon/VolumeSlider new file mode 100644 index 0000000..f4d50f7 --- /dev/null +++ b/src/3rdparty/phonon/includes/Phonon/VolumeSlider @@ -0,0 +1 @@ +#include "../../phonon/volumeslider.h" diff --git a/src/3rdparty/phonon/phonon.pc.cmake b/src/3rdparty/phonon/phonon.pc.cmake new file mode 100644 index 0000000..10c3712 --- /dev/null +++ b/src/3rdparty/phonon/phonon.pc.cmake @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=@EXEC_INSTALL_PREFIX@ +libdir=@LIB_INSTALL_DIR@ +includedir=@INCLUDE_INSTALL_DIR@ + +Name: Phonon +Description: Phonon library needed to build applications +Version: @PHONON_LIB_MAJOR_VERSION@.@PHONON_LIB_MINOR_VERSION@.@PHONON_LIB_PATCH_VERSION@ +Requires: QtCore QtGui QtDBus +Libs: -L${libdir} -lphonon +Cflags: -I${includedir} diff --git a/src/3rdparty/phonon/phonon/.krazy b/src/3rdparty/phonon/phonon/.krazy new file mode 100644 index 0000000..f0ab4d5 --- /dev/null +++ b/src/3rdparty/phonon/phonon/.krazy @@ -0,0 +1,2 @@ +EXCLUDE qclasses +SKIP /DESIGN/ diff --git a/src/3rdparty/phonon/phonon/BUGS b/src/3rdparty/phonon/phonon/BUGS new file mode 100644 index 0000000..fb74e44 --- /dev/null +++ b/src/3rdparty/phonon/phonon/BUGS @@ -0,0 +1,9 @@ +When the backend changes there's a drop in the playback. When it's in +PlayingState it could time how long it takes until the playback resumes and add +that time to the seek. + +When the backend changes the paths are not restored correctly and it crashes + +When a new AudioOutput is created with the same name and category it'll still cause +a notification if a device fallback was necessary. One such notification per +name/category or even ignoring the name sure is enough. diff --git a/src/3rdparty/phonon/phonon/CMakeLists.txt b/src/3rdparty/phonon/phonon/CMakeLists.txt new file mode 100644 index 0000000..11d7913 --- /dev/null +++ b/src/3rdparty/phonon/phonon/CMakeLists.txt @@ -0,0 +1,104 @@ +if (PHONON_BUILD_TESTS) + add_subdirectory(tests) +endif (PHONON_BUILD_TESTS) + +if (PHONON_BUILD_EXAMPLES) + add_subdirectory(examples) +endif (PHONON_BUILD_EXAMPLES) + +add_subdirectory(experimental) + +set(phonon_LIB_SRCS + objectdescription.cpp + objectdescriptionmodel.cpp + phononnamespace.cpp + mediasource.cpp + abstractmediastream.cpp + streaminterface.cpp + mediaobject.cpp + medianode.cpp + path.cpp + effectparameter.cpp + effect.cpp + volumefadereffect.cpp + abstractaudiooutput.cpp + abstractaudiooutput_p.cpp + audiooutput.cpp + audiooutputinterface.cpp + abstractvideooutput.cpp + abstractvideooutput_p.cpp + backendcapabilities.cpp + globalconfig.cpp + factory.cpp + platform.cpp + mediacontroller.cpp + videowidget.cpp + videoplayer.cpp + seekslider.cpp + volumeslider.cpp + effectwidget.cpp + iodevicestream.cpp + ) + +if (QT_QTDBUS_FOUND) + list(APPEND phonon_LIB_SRCS + audiooutputadaptor.cpp + ) +endif (QT_QTDBUS_FOUND) + + +add_definitions(-DPHONON_LIBRARY_PATH="${PLUGIN_INSTALL_DIR}/plugins") +automoc4_add_library(phonon SHARED ${phonon_LIB_SRCS}) +target_link_libraries(phonon ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY}) +if (QT_QTDBUS_FOUND) + target_link_libraries(phonon ${QT_QTDBUS_LIBRARY}) +endif (QT_QTDBUS_FOUND) +if (${CMAKE_SYSTEM_NAME} MATCHES "SunOS") + # We need to explicitly link libm to phonon in Solaris + target_link_libraries(phonon m) +endif (${CMAKE_SYSTEM_NAME} MATCHES "SunOS") + +set_target_properties(phonon PROPERTIES + VERSION ${PHONON_LIB_VERSION} + SOVERSION ${PHONON_LIB_SOVERSION} + DEFINE_SYMBOL MAKE_PHONON_LIB + ) +install(TARGETS phonon ${INSTALL_TARGETS_DEFAULT_ARGS}) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/phononnamespace.h.in ${CMAKE_CURRENT_BINARY_DIR}/phononnamespace.h) + +install(FILES + phonon_export.h + objectdescription.h + objectdescriptionmodel.h + ${CMAKE_CURRENT_BINARY_DIR}/phononnamespace.h + mediasource.h + abstractmediastream.h + streaminterface.h + mediaobject.h + audiooutput.h + medianode.h + path.h + effectparameter.h + effect.h + effectinterface.h + volumefadereffect.h + volumefaderinterface.h + abstractaudiooutput.h + abstractvideooutput.h + backendcapabilities.h + phonondefs.h + backendinterface.h + mediaobjectinterface.h + audiooutputinterface.h + addoninterface.h + mediacontroller.h + videowidget.h + videowidgetinterface.h + videoplayer.h + seekslider.h + volumeslider.h + effectwidget.h + platformplugin.h + DESTINATION ${INCLUDE_INSTALL_DIR}/phonon COMPONENT Devel) + +install(FILES org.kde.Phonon.AudioOutput.xml DESTINATION ${DBUS_INTERFACES_INSTALL_DIR}) diff --git a/src/3rdparty/phonon/phonon/IDEAS b/src/3rdparty/phonon/phonon/IDEAS new file mode 100644 index 0000000..96fe056 --- /dev/null +++ b/src/3rdparty/phonon/phonon/IDEAS @@ -0,0 +1,70 @@ +MediaInfo class +=============== + +It might be nice to have a class that gives information about MediaSource +objects without having to use MediaObject for it. Something like QFileInfo for +QFile. MediaObject could also return a list of MediaInfo objects for all the +MediaSource objects in the queue. + +Rationale: The app then can get info about length and substreams so that it can +set up everything correctly for the next "file" in the queue. + + +- on hotplug of a USB audio/video device: + 1. If the device is plugged in for the first time + Check whether the backend now provides new device selections. If yes, open + up central config with the page that allows selection of the default + devices. Tell the user what new options are available. + Allow the user to set preference of devices: If device USB-Headset is + available use that, else use builtin speakers. + + device info persistance +========================= +On device selections: should the user be presented with options that are +currently not available? It might actually be confusing to select a device that +is not usable at the moment. + +Some situations: +(device A is always available) +- user plugs device B +- selects device B as default +next day: device B unplugged +- phonon falls back to device A as B is not available and shows a passive popup + informing the user about the fallback +- user opens config dialog +- device B still shows up as default +- selects device A, closes dialog +- opens dialog again +- device B is gone... +- user plugs device B +- device B reappears + +The Backend has to provide the information about devices. Those can map directly +to hardware or a soundserver or some other virtual device. The Backend has to +have some unique identifier. For example the ALSA devices could be identified +using the ALSA device names (either hw:0, hw:1 or aliases from asoundrc). OSS +devices could be identified using /dev/dsp, /dev/dsp1 and so on. +=> the backend internal uid is a string +In the Frontend all that is needed is a name and a description of the device +(both translated to the current locale) and a unique identifier to talk to the +backend. This identifier could be the same as used internally by the Backend, +but doesn't have to be. + + "lowlevel" audio I/O +====================== +ByteStream is a buffered stream, and as therefore completely useless for cases +where an application wants to write audio data to the soundcard/-system with low +latency. +=> PcmInStream and PcmOutStream + + +============================================================================ += Ideas for "useless but nice" things (until somebody can convince me that = += they're useful) = +============================================================================ + + Video Output +============== +Add another VideoOutput that can be used directly with QGraphicsView by creating +a QGraphicsItem subclass. + diff --git a/src/3rdparty/phonon/phonon/Messages.sh b/src/3rdparty/phonon/phonon/Messages.sh new file mode 100644 index 0000000..c628a04 --- /dev/null +++ b/src/3rdparty/phonon/phonon/Messages.sh @@ -0,0 +1,6 @@ +#! /usr/bin/env bash +find ./ -maxdepth 1 -name "*.cpp" -print > files +find ./ -maxdepth 1 -name "*.h" -print >> files +#$EXTRACTRC `find $dirs -maxdepth 1 \( -name \*.rc -o -name \*.ui -o -name \*.ui3 -o -name \*.ui4 -o -name \*.kcfg \) ` >> rc.cpp || exit 11 +$XGETTEXT_QT --copyright-holder=This_file_is_part_of_KDE --msgid-bugs-address=http://bugs.kde.org --files-from=files -o $podir/libphonon.pot +rm files diff --git a/src/3rdparty/phonon/phonon/TODO b/src/3rdparty/phonon/phonon/TODO new file mode 100644 index 0000000..479bbd7 --- /dev/null +++ b/src/3rdparty/phonon/phonon/TODO @@ -0,0 +1,31 @@ +- add a metadata function that returns a QVariant instead of a QString - useful for QImage or other binary (QByteArray) content + +- QImage VideoWidget::screenshot(): ordinary screenshot doesn't work with XV says Christoph. And VideoDataOutput + would be major overkill. + +- consider whether to add a signal to notify when the information for + availableAudioStreams/availableVideoStreams/availableSubtitleStreams + is available + +- look at collaboration with other projects, e.g. pavucontrol, gsmartmix + +- consider Player dbus interface in MediaObject + +- add global setting to pause video when it's not visible + +- add a way to request a specific version (>=) from the Backend, so that known to be buggy Backends aren't used + +- Video Overlays (OSD) like: show a rectangle as a play symbol, show the position in + the file as something like [IIIIIIIII------], and so on + +- http://bugs.kde.org/show_bug.cgi?id=147494 + +- frame/sample precise positioning (cue in/out) + +- different timecode support like SMPTE + +- Codec interface - at least for audio + +- Speed factor for playback (useful mostly for audio - but video still needs to stay in sync) + +- tell the platform plugin which backend was loaded (if it doesn't do it itself) so that it can load KDE translations for the backend diff --git a/src/3rdparty/phonon/phonon/abstractaudiooutput.cpp b/src/3rdparty/phonon/phonon/abstractaudiooutput.cpp new file mode 100644 index 0000000..47c5a89 --- /dev/null +++ b/src/3rdparty/phonon/phonon/abstractaudiooutput.cpp @@ -0,0 +1,50 @@ +/* This file is part of the KDE project +Copyright (C) 2005-2006 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ +#include "abstractaudiooutput.h" +#include "abstractaudiooutput_p.h" +#include "factory_p.h" + +#define PHONON_CLASSNAME AbstractAudioOutput + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + + AbstractAudioOutput::AbstractAudioOutput(AbstractAudioOutputPrivate &dd, QObject *parent) : QObject(parent), + MediaNode(dd) + { + } + + AbstractAudioOutput::~AbstractAudioOutput() + { + } + +} //namespace Phonon + +QT_END_NAMESPACE + +#undef PHONON_CLASSNAME + +#include "moc_abstractaudiooutput.cpp" + +// vim: sw=4 ts=4 tw=80 diff --git a/src/3rdparty/phonon/phonon/abstractaudiooutput.h b/src/3rdparty/phonon/phonon/abstractaudiooutput.h new file mode 100644 index 0000000..a466298 --- /dev/null +++ b/src/3rdparty/phonon/phonon/abstractaudiooutput.h @@ -0,0 +1,57 @@ +/* This file is part of the KDE project +Copyright (C) 2005-2006 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ +#ifndef Phonon_ABSTRACTAUDIOOUTPUTBASE_H +#define Phonon_ABSTRACTAUDIOOUTPUTBASE_H + +#include "phonondefs.h" +#include "phonon_export.h" +#include "medianode.h" +#include <QtCore/QObject> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + class AbstractAudioOutputPrivate; + + /** \class AbstractAudioOutput abstractaudiooutput.h Phonon/AbstractAudioOutput + * Common base class for all audio outputs. + * + * \see AudioOutput + */ + class PHONON_EXPORT AbstractAudioOutput : public QObject, public MediaNode + { + Q_OBJECT + K_DECLARE_PRIVATE(AbstractAudioOutput) + protected: + AbstractAudioOutput(AbstractAudioOutputPrivate &dd, QObject *parent); + public: + ~AbstractAudioOutput(); + }; +} //namespace Phonon + +QT_END_NAMESPACE +QT_END_HEADER + +// vim: sw=4 ts=4 tw=80 +#endif // Phonon_ABSTRACTAUDIOOUTPUTBASE_H diff --git a/src/3rdparty/phonon/phonon/abstractaudiooutput_p.cpp b/src/3rdparty/phonon/phonon/abstractaudiooutput_p.cpp new file mode 100644 index 0000000..83ee8b0 --- /dev/null +++ b/src/3rdparty/phonon/phonon/abstractaudiooutput_p.cpp @@ -0,0 +1,44 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#include "abstractaudiooutput_p.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + +bool AbstractAudioOutputPrivate::aboutToDeleteBackendObject() +{ + return true; +} + +void AbstractAudioOutputPrivate::setupBackendObject() +{ + Q_ASSERT(m_backendObject); + + // set up attributes +} + +} // namespace Phonon + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/phonon/abstractaudiooutput_p.h b/src/3rdparty/phonon/phonon/abstractaudiooutput_p.h new file mode 100644 index 0000000..831eb8d --- /dev/null +++ b/src/3rdparty/phonon/phonon/abstractaudiooutput_p.h @@ -0,0 +1,50 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_ABSTRACTAUDIOOUTPUT_P_H +#define PHONON_ABSTRACTAUDIOOUTPUT_P_H + +#include "abstractaudiooutput.h" +#include "medianode_p.h" +#include "phonondefs_p.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +class AbstractAudioOutputPrivate : public MediaNodePrivate +{ + Q_DECLARE_PUBLIC(AbstractAudioOutput) + PHONON_PRIVATEABSTRACTCLASS + public: + virtual QObject *qObject() { return q_func(); } + protected: + AbstractAudioOutputPrivate(CastId castId = AbstractAudioOutputPrivateType) + : MediaNodePrivate(castId) + { + } +}; +} //namespace Phonon + +QT_END_NAMESPACE + +#endif // PHONON_ABSTRACTAUDIOOUTPUT_P_H diff --git a/src/3rdparty/phonon/phonon/abstractmediastream.cpp b/src/3rdparty/phonon/phonon/abstractmediastream.cpp new file mode 100644 index 0000000..a661702 --- /dev/null +++ b/src/3rdparty/phonon/phonon/abstractmediastream.cpp @@ -0,0 +1,198 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#include "abstractmediastream.h" +#include "abstractmediastream_p.h" +#include "mediaobjectinterface.h" +#include "mediaobject_p.h" +#include "streaminterface_p.h" + +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + +AbstractMediaStream::AbstractMediaStream(QObject *parent) + : QObject(parent), + d_ptr(new AbstractMediaStreamPrivate) +{ + d_ptr->q_ptr = this; +} + +AbstractMediaStream::AbstractMediaStream(AbstractMediaStreamPrivate &dd, QObject *parent) + : QObject(parent), + d_ptr(&dd) +{ + d_ptr->q_ptr = this; +} + +AbstractMediaStream::~AbstractMediaStream() +{ + delete d_ptr; +} + +qint64 AbstractMediaStream::streamSize() const +{ + return d_ptr->streamSize; +} + +void AbstractMediaStream::setStreamSize(qint64 newSize) +{ + d_ptr->setStreamSize(newSize); +} + +void AbstractMediaStreamPrivate::setStreamSize(qint64 newSize) +{ + streamSize = newSize; + if (streamInterface) { + streamInterface->setStreamSize(newSize); + } +} + +bool AbstractMediaStream::streamSeekable() const +{ + return d_ptr->streamSeekable; +} + +void AbstractMediaStream::setStreamSeekable(bool s) +{ + d_ptr->setStreamSeekable(s); +} + +void AbstractMediaStreamPrivate::setStreamSeekable(bool s) +{ + streamSeekable = s; + if (streamInterface) { + streamInterface->setStreamSeekable(s); + } +} + +void AbstractMediaStream::writeData(const QByteArray &data) +{ + d_ptr->writeData(data); +} + +void AbstractMediaStreamPrivate::writeData(const QByteArray &data) +{ + if (ignoreWrites) { + return; + } + Q_ASSERT(streamInterface); + streamInterface->writeData(data); +} + +void AbstractMediaStream::endOfData() +{ + d_ptr->endOfData(); +} + +void AbstractMediaStreamPrivate::endOfData() +{ + if (streamInterface) { + streamInterface->endOfData(); + } +} + +void AbstractMediaStream::error(Phonon::ErrorType type, const QString &text) +{ + Q_D(AbstractMediaStream); + d->errorType = type; + d->errorText = text; + if (d->mediaObjectPrivate) { + // TODO: MediaObject might be in a different thread + d->mediaObjectPrivate->streamError(type, text); + } +} + +void AbstractMediaStream::enoughData() +{ +} + +void AbstractMediaStream::seekStream(qint64) +{ + Q_ASSERT(!d_ptr->streamSeekable); +} + +AbstractMediaStreamPrivate::~AbstractMediaStreamPrivate() +{ + if (mediaObjectPrivate) { + // TODO: MediaObject might be in a different thread + mediaObjectPrivate->removeDestructionHandler(this); + } + if (streamInterface) { + // TODO: StreamInterface might be in a different thread + streamInterface->d->disconnectMediaStream(); + } +} + +void AbstractMediaStreamPrivate::setStreamInterface(StreamInterface *iface) +{ + Q_Q(AbstractMediaStream); + streamInterface = iface; + if (!iface) { + // our subclass might be just about to call writeData, so tell it we have enoughData and + // ignore the next writeData calls + q->enoughData(); + ignoreWrites = true; + return; + } + if (ignoreWrites) { + ignoreWrites = false; + // we had a StreamInterface before. The new StreamInterface expects us to start reading from + // position 0 + q->reset(); + } else { + iface->setStreamSize(streamSize); + iface->setStreamSeekable(streamSeekable); + } +} + +void AbstractMediaStreamPrivate::setMediaObjectPrivate(MediaObjectPrivate *mop) +{ + // TODO: MediaObject might be in a different thread + mediaObjectPrivate = mop; + mediaObjectPrivate->addDestructionHandler(this); + if (!errorText.isEmpty()) { + mediaObjectPrivate->streamError(errorType, errorText); + } +} + +void AbstractMediaStreamPrivate::phononObjectDestroyed(MediaNodePrivate *bp) +{ + // TODO: MediaObject might be in a different thread + Q_ASSERT(bp == mediaObjectPrivate); + Q_UNUSED(bp); + mediaObjectPrivate = 0; +} + +} // namespace Phonon + + +QT_END_NAMESPACE + +#include "moc_abstractmediastream.cpp" + +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + +// vim: sw=4 sts=4 et tw=100 diff --git a/src/3rdparty/phonon/phonon/abstractmediastream.h b/src/3rdparty/phonon/phonon/abstractmediastream.h new file mode 100644 index 0000000..0daa92a --- /dev/null +++ b/src/3rdparty/phonon/phonon/abstractmediastream.h @@ -0,0 +1,227 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_ABSTRACTMEDIASTREAM_H +#define PHONON_ABSTRACTMEDIASTREAM_H + +#include "phonon_export.h" +#include "phononnamespace.h" +#include <QtCore/QObject> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +class QByteArray; + +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + +namespace Phonon +{ +class MediaObject; +class AbstractMediaStreamPrivate; + +/** \class AbstractMediaStream abstractmediastream.h Phonon/AbstractMediaStream + * \brief Base class for custom media data streams. + * + * Implement this class to provide a custom data stream to the backend. The class supports both, the + * push and the pull model. + * + * Push: + * \code + * PushStream::PushStream(QObject *parent) + * : AbstractMediaStream(parent), m_timer(new QTimer(this)) + * { + * setStreamSize(getMediaStreamSize()); + * + * connect(m_timer, SIGNAL(timeout()), SLOT(moreData())); + * m_timer->setInterval(0); + * } + * + * void PushStream::moreData() + * { + * const QByteArray data = getMediaData(); + * if (data.isEmpty()) { + * endOfData(); + * } else { + * writeData(data); + * } + * } + * + * void PushStream::needData() + * { + * m_timer->start(); + * moreData(); + * } + * + * void PushStream::enoughData() + * { + * m_timer->stop(); + * } + * \endcode + * + * Pull: + * \code + * PullStream::PullStream(QObject *parent) + * : AbstractMediaStream(parent) + * { + * setStreamSize(getMediaStreamSize()); + * } + * + * void PullStream::needData() + * { + * const QByteArray data = getMediaData(); + * if (data.isEmpty()) { + * endOfData(); + * } else { + * writeData(data); + * } + * } + * \endcode + * + * \ingroup Playback + * \author Matthias Kretz <kretz@kde.org> + */ +class PHONON_EXPORT AbstractMediaStream : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(AbstractMediaStream) + friend class MediaObject; + friend class MediaObjectPrivate; + friend class StreamInterface; + public: + virtual ~AbstractMediaStream(); + + protected: + /** + * Constructs an AbstractMediaStream object with a \p parent. + */ + explicit AbstractMediaStream(QObject *parent = 0); + + /** + * Returns the stream size that was set with \ref setStreamSize. + * + * A negative value means that the length of the stream cannot be known. + * + * Defaults to \c 0. + */ + qint64 streamSize() const; + + /** + * Sets the size of the stream in number of bytes. + * + * A negative value means that the length of the stream cannot be known. + * + * Defaults to 0. + * + * This function has to be called. A backend will not call \ref needData() until the + * stream size is set. + */ + void setStreamSize(qint64); + + /** + * Returns whether your data stream is set as seekable. + * + * Defaults to \c false. + */ + bool streamSeekable() const; + + /** + * Sets whether your data stream is seekable. + * + * Defaults to \c false. + * + * If you set this to \c true you have to implement the \ref seekStream function. + */ + void setStreamSeekable(bool); + + /** + * Sends the media \p data to the backend for decoding. + * + * \warning Don't call this function before the first needData() is emitted. + */ + void writeData(const QByteArray &data); + + /** + * Tells the backend that the media data stream is at its end. + * + * \warning Don't call this function before the first needData() is emitted. + */ + void endOfData(); + + /** + * If an I/O error occurs you should call this function to make MediaObject go into + * ErrorState. + * + * \see MediaObject::errorType() + * \see MediaObject::errorString() + */ + void error(Phonon::ErrorType errorType, const QString &errorString); + + /** + * Reimplement this function to reset the stream. Subsequent calls to writeData should start + * from the first position of the data unless a seek is requested. + * + * The function is necessary for the case where a non-seekable MediaStream is + * played more than once. For a seekable stream the implementation can simply call + * \code + * seekStream(0); + * \endcode. + */ + virtual void reset() = 0; + + /** + * Reimplement this function to be notified when the backend needs data. + * + * When this function is called you should try to call writeData or endOfData before + * returning. + */ + virtual void needData() = 0; + + /** + * Reimplement this function to be notified when the backend has enough data and your stream + * object may take a break. This method is important for pushing data to the backend in + * order to not fill the backend buffer unnecessarily. + */ + virtual void enoughData(); + + /** + * Reimplement this function if your stream is seekable. + * + * When this function is called the next call to writeData has to be at the requested \p + * offset. + * + * \warning Do not call the parent implementation. + */ + virtual void seekStream(qint64 offset); + + AbstractMediaStream(AbstractMediaStreamPrivate &dd, QObject *parent); + AbstractMediaStreamPrivate *d_ptr; +}; + +} // namespace Phonon + +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // PHONON_ABSTRACTMEDIASTREAM_H diff --git a/src/3rdparty/phonon/phonon/abstractmediastream_p.h b/src/3rdparty/phonon/phonon/abstractmediastream_p.h new file mode 100644 index 0000000..a9d6489 --- /dev/null +++ b/src/3rdparty/phonon/phonon/abstractmediastream_p.h @@ -0,0 +1,83 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef ABSTRACTMEDIASTREAM_P_H +#define ABSTRACTMEDIASTREAM_P_H + +#include "phonon_export.h" +#include "abstractmediastream.h" +#include "mediaobject_p.h" +#include "streaminterface.h" + +#include "medianodedestructionhandler_p.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + +class MediaObjectPrivate; + +namespace Phonon +{ +class PHONON_EXPORT AbstractMediaStreamPrivate : private MediaNodeDestructionHandler +{ + friend class MediaObject; + Q_DECLARE_PUBLIC(AbstractMediaStream) + public: + void setStreamInterface(StreamInterface *); + void setMediaObjectPrivate(MediaObjectPrivate *); + + protected: + AbstractMediaStreamPrivate() + : streamSize(0), + streamSeekable(false), + ignoreWrites(false), + streamInterface(0), + mediaObjectPrivate(0), + errorType(NoError) + { + } + ~AbstractMediaStreamPrivate(); + + virtual void setStreamSize(qint64 newSize); + virtual void setStreamSeekable(bool s); + virtual void writeData(const QByteArray &data); + virtual void endOfData(); + void phononObjectDestroyed(MediaNodePrivate *); + + AbstractMediaStream *q_ptr; + qint64 streamSize; + bool streamSeekable; + bool ignoreWrites; + StreamInterface *streamInterface; + MediaObjectPrivate *mediaObjectPrivate; + Phonon::ErrorType errorType; + QString errorText; +}; +} // namespace Phonon + +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + +QT_END_NAMESPACE + +#endif // ABSTRACTMEDIASTREAM_P_H +// vim: sw=4 sts=4 et tw=100 diff --git a/src/3rdparty/phonon/phonon/abstractvideooutput.cpp b/src/3rdparty/phonon/phonon/abstractvideooutput.cpp new file mode 100644 index 0000000..30dde14 --- /dev/null +++ b/src/3rdparty/phonon/phonon/abstractvideooutput.cpp @@ -0,0 +1,41 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ +#include "abstractvideooutput.h" +#include "abstractvideooutput_p.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_VIDEO + +namespace Phonon +{ + +AbstractVideoOutput::AbstractVideoOutput(AbstractVideoOutputPrivate &d) + : MediaNode(d) +{ +} + +} // namespace Phonon + +#endif //QT_NO_PHONON_VIDEO + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/phonon/abstractvideooutput.h b/src/3rdparty/phonon/phonon/abstractvideooutput.h new file mode 100644 index 0000000..04cfdf0 --- /dev/null +++ b/src/3rdparty/phonon/phonon/abstractvideooutput.h @@ -0,0 +1,74 @@ +/* This file is part of the KDE project + Copyright (C) 2005-2006 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ +#ifndef Phonon_ABSTRACTVIDEOOUTPUTBASE_H +#define Phonon_ABSTRACTVIDEOOUTPUTBASE_H + +#include "phonondefs.h" +#include "phonon_export.h" +#include "medianode.h" +#include <QtCore/QObject> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_VIDEO + +class QString; + +namespace Phonon +{ + +namespace Experimental +{ + class Visualization; + class VisualizationPrivate; +} // namespace Experimental + + class AbstractVideoOutputPrivate; + + /** \class AbstractVideoOutput abstractvideooutput.h Phonon/AbstractVideoOutput + * \brief Common base class for all video outputs. + * + * \see VideoWidget + */ + class PHONON_EXPORT AbstractVideoOutput : public MediaNode + { + friend class Experimental::Visualization; + friend class Experimental::VisualizationPrivate; + K_DECLARE_PRIVATE(AbstractVideoOutput) + protected: + /** + * \internal + * Constructor that is called from derived classes. + * + * \param d the private object + */ + AbstractVideoOutput(AbstractVideoOutputPrivate &d); + }; +} //namespace Phonon + +#endif //QT_NO_PHONON_VIDEO + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // Phonon_ABSTRACTVIDEOOUTPUTBASE_H diff --git a/src/3rdparty/phonon/phonon/abstractvideooutput_p.cpp b/src/3rdparty/phonon/phonon/abstractvideooutput_p.cpp new file mode 100644 index 0000000..83d0650 --- /dev/null +++ b/src/3rdparty/phonon/phonon/abstractvideooutput_p.cpp @@ -0,0 +1,41 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#include "abstractvideooutput_p.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_VIDEO + +namespace Phonon +{ + +bool AbstractVideoOutputPrivate::aboutToDeleteBackendObject() +{ + return true; +} + +} //namespace Phonon + +#endif //QT_NO_PHONON_VIDEO + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/phonon/abstractvideooutput_p.h b/src/3rdparty/phonon/phonon/abstractvideooutput_p.h new file mode 100644 index 0000000..d0e8157 --- /dev/null +++ b/src/3rdparty/phonon/phonon/abstractvideooutput_p.h @@ -0,0 +1,48 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef ABSTRACTVIDEOOUTPUT_P_H +#define ABSTRACTVIDEOOUTPUT_P_H + +#include "abstractvideooutput.h" +#include "medianode_p.h" +#include "phonondefs_p.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_VIDEO + +namespace Phonon +{ +class AbstractVideoOutputPrivate : public MediaNodePrivate +{ + Q_DECLARE_PUBLIC(AbstractVideoOutput) + PHONON_PRIVATEABSTRACTCLASS +}; +} //namespace Phonon + +#endif //QT_NO_PHONON_VIDEO + +QT_END_NAMESPACE + +#endif // ABSTRACTVIDEOOUTPUT_P_H +// vim: sw=4 ts=4 tw=80 diff --git a/src/3rdparty/phonon/phonon/addoninterface.h b/src/3rdparty/phonon/phonon/addoninterface.h new file mode 100644 index 0000000..f400523 --- /dev/null +++ b/src/3rdparty/phonon/phonon/addoninterface.h @@ -0,0 +1,103 @@ +/* This file is part of the KDE project + Copyright (C) 2007-2008 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_ADDONINTERFACE_H +#define PHONON_ADDONINTERFACE_H + +#include "phononnamespace.h" + +#include <QtCore/QList> +#include <QtCore/QVariant> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_MEDIACONTROLLER + +namespace Phonon +{ +/** \class AddonInterface addoninterface.h Phonon/AddonInterface + * \short Interface for Menu, Chapter, Angle and Title/Track control. + * + * \author Matthias Kretz <kretz@kde.org> + */ +class AddonInterface +{ + public: + virtual ~AddonInterface() {} + + enum Interface { + NavigationInterface = 1, + ChapterInterface = 2, + AngleInterface = 3, + TitleInterface = 4, + SubtitleInterface = 5, + AudioChannelInterface = 6 + }; + + enum NavigationCommand { + Menu1Button + }; + enum ChapterCommand { + availableChapters, + chapter, + setChapter + }; + enum AngleCommand { + availableAngles, + angle, + setAngle + }; + enum TitleCommand { + availableTitles, + title, + setTitle, + autoplayTitles, + setAutoplayTitles + }; + enum SubtitleCommand { + availableSubtitles, + currentSubtitle, + setCurrentSubtitle + }; + enum AudioChannelCommand { + availableAudioChannels, + currentAudioChannel, + setCurrentAudioChannel + }; + + virtual bool hasInterface(Interface iface) const = 0; + + virtual QVariant interfaceCall(Interface iface, int command, + const QList<QVariant> &arguments = QList<QVariant>()) = 0; +}; + +} // namespace Phonon + +Q_DECLARE_INTERFACE(Phonon::AddonInterface, "AddonInterface0.2.phonon.kde.org") + +#endif //QT_NO_PHONON_MEDIACONTROLLER + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // PHONON_ADDONINTERFACE_H diff --git a/src/3rdparty/phonon/phonon/audiooutput.cpp b/src/3rdparty/phonon/phonon/audiooutput.cpp new file mode 100644 index 0000000..752580a --- /dev/null +++ b/src/3rdparty/phonon/phonon/audiooutput.cpp @@ -0,0 +1,418 @@ +/* This file is part of the KDE project + Copyright (C) 2005-2006 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ +#include "audiooutput.h" +#include "audiooutput_p.h" +#include "factory_p.h" +#include "objectdescription.h" +#include "audiooutputadaptor_p.h" +#include "globalconfig_p.h" +#include "audiooutputinterface.h" +#include "phononnamespace_p.h" +#include "platform_p.h" + +#include <qmath.h> + +#define PHONON_CLASSNAME AudioOutput +#define IFACES2 AudioOutputInterface42 +#define IFACES1 IFACES2 +#define IFACES0 AudioOutputInterface40, IFACES1 +#define PHONON_INTERFACENAME IFACES0 + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + +static inline bool callSetOutputDevice(MediaNodePrivate *const d, int index) +{ + Iface<IFACES2> iface(d); + if (iface) { + return iface->setOutputDevice(AudioOutputDevice::fromIndex(index)); + } + return Iface<IFACES0>::cast(d)->setOutputDevice(index); +} + +static inline bool callSetOutputDevice(MediaNodePrivate *const d, const AudioOutputDevice &dev) +{ + Iface<IFACES2> iface(d); + if (iface) { + return iface->setOutputDevice(dev); + } + return Iface<IFACES0>::cast(d)->setOutputDevice(dev.index()); +} + +AudioOutput::AudioOutput(Phonon::Category category, QObject *parent) + : AbstractAudioOutput(*new AudioOutputPrivate, parent) +{ + K_D(AudioOutput); + d->init(category); +} + +AudioOutput::AudioOutput(QObject *parent) + : AbstractAudioOutput(*new AudioOutputPrivate, parent) +{ + K_D(AudioOutput); + d->init(NoCategory); +} + +void AudioOutputPrivate::init(Phonon::Category c) +{ + Q_Q(AudioOutput); +#ifndef QT_NO_DBUS + adaptor = new AudioOutputAdaptor(q); + static unsigned int number = 0; + const QString &path = QLatin1String("/AudioOutputs/") + QString::number(number++); + QDBusConnection con = QDBusConnection::sessionBus(); + con.registerObject(path, q); + emit adaptor->newOutputAvailable(con.baseService(), path); + q->connect(q, SIGNAL(volumeChanged(qreal)), adaptor, SIGNAL(volumeChanged(qreal))); + q->connect(q, SIGNAL(mutedChanged(bool)), adaptor, SIGNAL(mutedChanged(bool))); +#endif + + category = c; + + // select hardware device according to the category + device = AudioOutputDevice::fromIndex(GlobalConfig().audioOutputDeviceFor(category, GlobalConfig::AdvancedDevicesFromSettings | GlobalConfig::HideUnavailableDevices)); + + createBackendObject(); + + q->connect(Factory::sender(), SIGNAL(availableAudioOutputDevicesChanged()), SLOT(_k_deviceListChanged())); +} + + + +void AudioOutputPrivate::createBackendObject() +{ + if (m_backendObject) + return; + Q_Q(AudioOutput); + m_backendObject = Factory::createAudioOutput(q); + if (m_backendObject) { + setupBackendObject(); + } +} + +QString AudioOutput::name() const +{ + K_D(const AudioOutput); + return d->name; +} + +void AudioOutput::setName(const QString &newName) +{ + K_D(AudioOutput); + if (d->name == newName) { + return; + } + d->name = newName; + setVolume(Platform::loadVolume(newName)); +#ifndef QT_NO_DBUS + emit d->adaptor->nameChanged(newName); +#endif +} + +static const qreal LOUDNESS_TO_VOLTAGE_EXPONENT = qreal(0.67); +static const qreal VOLTAGE_TO_LOUDNESS_EXPONENT = qreal(1.0/LOUDNESS_TO_VOLTAGE_EXPONENT); + +void AudioOutput::setVolume(qreal volume) +{ + K_D(AudioOutput); + d->volume = volume; + if (k_ptr->backendObject() && !d->muted) { + // using Stevens' power law loudness is proportional to (sound pressure)^0.67 + // sound pressure is proportional to voltage: + // p² \prop P \prop V² + // => if a factor for loudness of x is requested + INTERFACE_CALL(setVolume(pow(volume, VOLTAGE_TO_LOUDNESS_EXPONENT))); + } else { + emit volumeChanged(volume); + } + Platform::saveVolume(d->name, volume); +} + +qreal AudioOutput::volume() const +{ + K_D(const AudioOutput); + if (d->muted || !d->m_backendObject) { + return d->volume; + } + return pow(INTERFACE_CALL(volume()), LOUDNESS_TO_VOLTAGE_EXPONENT); +} + +#ifndef PHONON_LOG10OVER20 +#define PHONON_LOG10OVER20 +static const qreal log10over20 = qreal(0.1151292546497022842); // ln(10) / 20 +#endif // PHONON_LOG10OVER20 + +qreal AudioOutput::volumeDecibel() const +{ + K_D(const AudioOutput); + if (d->muted || !d->m_backendObject) { + return log(d->volume) / log10over20; + } + return 0.67 * log(INTERFACE_CALL(volume())) / log10over20; +} + +void AudioOutput::setVolumeDecibel(qreal newVolumeDecibel) +{ + setVolume(exp(newVolumeDecibel * log10over20)); +} + +bool AudioOutput::isMuted() const +{ + K_D(const AudioOutput); + return d->muted; +} + +void AudioOutput::setMuted(bool mute) +{ + K_D(AudioOutput); + if (d->muted != mute) { + if (mute) { + d->muted = mute; + if (k_ptr->backendObject()) { + INTERFACE_CALL(setVolume(0.0)); + } + } else { + if (k_ptr->backendObject()) { + INTERFACE_CALL(setVolume(pow(d->volume, VOLTAGE_TO_LOUDNESS_EXPONENT))); + } + d->muted = mute; + } + emit mutedChanged(mute); + } +} + +Category AudioOutput::category() const +{ + K_D(const AudioOutput); + return d->category; +} + +AudioOutputDevice AudioOutput::outputDevice() const +{ + K_D(const AudioOutput); + return d->device; +} + +bool AudioOutput::setOutputDevice(const AudioOutputDevice &newAudioOutputDevice) +{ + K_D(AudioOutput); + if (!newAudioOutputDevice.isValid()) { + d->outputDeviceOverridden = false; + const int newIndex = GlobalConfig().audioOutputDeviceFor(d->category); + if (newIndex == d->device.index()) { + return true; + } + d->device = AudioOutputDevice::fromIndex(newIndex); + } else { + d->outputDeviceOverridden = true; + if (d->device == newAudioOutputDevice) { + return true; + } + d->device = newAudioOutputDevice; + } + if (k_ptr->backendObject()) { + return callSetOutputDevice(k_ptr, d->device.index()); + } + return true; +} + +bool AudioOutputPrivate::aboutToDeleteBackendObject() +{ + if (m_backendObject) { + volume = pINTERFACE_CALL(volume()); + } + return AbstractAudioOutputPrivate::aboutToDeleteBackendObject(); +} + +void AudioOutputPrivate::setupBackendObject() +{ + Q_Q(AudioOutput); + Q_ASSERT(m_backendObject); + AbstractAudioOutputPrivate::setupBackendObject(); + + QObject::connect(m_backendObject, SIGNAL(volumeChanged(qreal)), q, SLOT(_k_volumeChanged(qreal))); + QObject::connect(m_backendObject, SIGNAL(audioDeviceFailed()), q, SLOT(_k_audioDeviceFailed())); + + // set up attributes + pINTERFACE_CALL(setVolume(pow(volume, VOLTAGE_TO_LOUDNESS_EXPONENT))); + + // if the output device is not available and the device was not explicitly set + if (!callSetOutputDevice(this, device) && !outputDeviceOverridden) { + // fall back in the preference list of output devices + QList<int> deviceList = GlobalConfig().audioOutputDeviceListFor(category, GlobalConfig::AdvancedDevicesFromSettings | GlobalConfig::HideUnavailableDevices); + if (deviceList.isEmpty()) { + return; + } + foreach (int devIndex, deviceList) { + const AudioOutputDevice &dev = AudioOutputDevice::fromIndex(devIndex); + if (callSetOutputDevice(this, dev)) { + handleAutomaticDeviceChange(dev, AudioOutputPrivate::FallbackChange); + return; // found one that works + } + } + // if we get here there is no working output device. Tell the backend. + const AudioOutputDevice none; + callSetOutputDevice(this, none); + handleAutomaticDeviceChange(none, FallbackChange); + } +} + +void AudioOutputPrivate::_k_volumeChanged(qreal newVolume) +{ + if (!muted) { + Q_Q(AudioOutput); + emit q->volumeChanged(pow(newVolume, qreal(0.67))); + } +} + +void AudioOutputPrivate::_k_revertFallback() +{ + if (deviceBeforeFallback == -1) { + return; + } + device = AudioOutputDevice::fromIndex(deviceBeforeFallback); + callSetOutputDevice(this, device); + Q_Q(AudioOutput); + emit q->outputDeviceChanged(device); +#ifndef QT_NO_DBUS + emit adaptor->outputDeviceIndexChanged(device.index()); +#endif +} + +void AudioOutputPrivate::_k_audioDeviceFailed() +{ + pDebug() << Q_FUNC_INFO; + // outputDeviceIndex identifies a failing device + // fall back in the preference list of output devices + QList<int> deviceList = GlobalConfig().audioOutputDeviceListFor(category, GlobalConfig::AdvancedDevicesFromSettings | GlobalConfig::HideUnavailableDevices); + foreach (int devIndex, deviceList) { + // if it's the same device as the one that failed, ignore it + if (device.index() != devIndex) { + const AudioOutputDevice &info = AudioOutputDevice::fromIndex(devIndex); + if (callSetOutputDevice(this, info)) { + handleAutomaticDeviceChange(info, FallbackChange); + return; // found one that works + } + } + } + // if we get here there is no working output device. Tell the backend. + const AudioOutputDevice none; + callSetOutputDevice(this, none); + handleAutomaticDeviceChange(none, FallbackChange); +} + +void AudioOutputPrivate::_k_deviceListChanged() +{ + pDebug() << Q_FUNC_INFO; + // let's see if there's a usable device higher in the preference list + QList<int> deviceList = GlobalConfig().audioOutputDeviceListFor(category, GlobalConfig::AdvancedDevicesFromSettings); + DeviceChangeType changeType = HigherPreferenceChange; + foreach (int devIndex, deviceList) { + const AudioOutputDevice &info = AudioOutputDevice::fromIndex(devIndex); + if (!info.property("available").toBool()) { + if (device.index() == devIndex) { + // we've reached the currently used device and it's not available anymore, so we + // fallback to the next available device + changeType = FallbackChange; + } + pDebug() << devIndex << "is not available"; + continue; + } + pDebug() << devIndex << "is available"; + if (device.index() == devIndex) { + // we've reached the currently used device, nothing to change + break; + } + if (callSetOutputDevice(this, info)) { + handleAutomaticDeviceChange(info, changeType); + break; // found one with higher preference that works + } + } +} + +static struct +{ + int first; + int second; +} g_lastFallback = { 0, 0 }; + +void AudioOutputPrivate::handleAutomaticDeviceChange(const AudioOutputDevice &device2, DeviceChangeType type) +{ + Q_Q(AudioOutput); + deviceBeforeFallback = device.index(); + device = device2; + emit q->outputDeviceChanged(device2); +#ifndef QT_NO_DBUS + emit adaptor->outputDeviceIndexChanged(device.index()); +#endif + const AudioOutputDevice &device1 = AudioOutputDevice::fromIndex(deviceBeforeFallback); + switch (type) { + case FallbackChange: + if (g_lastFallback.first != device1.index() || g_lastFallback.second != device2.index()) { +#ifndef QT_NO_PHONON_PLATFORMPLUGIN + const QString &text = //device2.isValid() ? + AudioOutput::tr("<html>The audio playback device <b>%1</b> does not work.<br/>" + "Falling back to <b>%2</b>.</html>").arg(device1.name()).arg(device2.name()) /*: + AudioOutput::tr("<html>The audio playback device <b>%1</b> does not work.<br/>" + "No other device available.</html>").arg(device1.name())*/; + Platform::notification("AudioDeviceFallback", text); +#endif //QT_NO_PHONON_PLATFORMPLUGIN + g_lastFallback.first = device1.index(); + g_lastFallback.second = device2.index(); + } + break; + case HigherPreferenceChange: + { +#ifndef QT_NO_PHONON_PLATFORMPLUGIN + const QString text = AudioOutput::tr("<html>Switching to the audio playback device <b>%1</b><br/>" + "which just became available and has higher preference.</html>").arg(device2.name()); + Platform::notification("AudioDeviceFallback", text, + QStringList(AudioOutput::tr("Revert back to device '%1'").arg(device1.name())), + q, SLOT(_k_revertFallback())); +#endif //QT_NO_PHONON_PLATFORMPLUGIN + g_lastFallback.first = 0; + g_lastFallback.second = 0; + } + break; + } +} + +AudioOutputPrivate::~AudioOutputPrivate() +{ +#ifndef QT_NO_DBUS + emit adaptor->outputDestroyed(); +#endif +} + +} //namespace Phonon + +QT_END_NAMESPACE + +#include "moc_audiooutput.cpp" + +#undef PHONON_CLASSNAME +#undef PHONON_INTERFACENAME +#undef IFACES2 +#undef IFACES1 +#undef IFACES0 diff --git a/src/3rdparty/phonon/phonon/audiooutput.h b/src/3rdparty/phonon/phonon/audiooutput.h new file mode 100644 index 0000000..54bb389 --- /dev/null +++ b/src/3rdparty/phonon/phonon/audiooutput.h @@ -0,0 +1,179 @@ +/* This file is part of the KDE project + Copyright (C) 2005-2006 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ +#ifndef Phonon_AUDIOOUTPUT_H +#define Phonon_AUDIOOUTPUT_H + +#include "phonon_export.h" +#include "abstractaudiooutput.h" +#include "phonondefs.h" +#include "phononnamespace.h" +#include "objectdescription.h" + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +class QString; + +class AudioOutputAdaptor; +namespace Phonon +{ + class AudioOutputPrivate; + + /** \class AudioOutput audiooutput.h Phonon/AudioOutput + * \short Class for audio output to the soundcard. + * + * Use this class to define the audio output. + * + * \ingroup Frontend + * \author Matthias Kretz <kretz@kde.org> + * \see Phonon::Ui::VolumeSlider + */ + class PHONON_EXPORT AudioOutput : public AbstractAudioOutput + { + friend class FactoryPrivate; + friend class ::AudioOutputAdaptor; + Q_OBJECT + K_DECLARE_PRIVATE(AudioOutput) + /** + * This is the name that appears in Mixer applications that control + * the volume of this output. + * + * \see category + */ + Q_PROPERTY(QString name READ name WRITE setName) + /** + * This is the current loudness of the output (it is using Stevens' law + * to calculate the change in voltage internally). + * + * \see volumeDecibel + */ + Q_PROPERTY(qreal volume READ volume WRITE setVolume NOTIFY volumeChanged) + /** + * This is the current volume of the output in decibel. + * + * 0 dB means no change in volume, -6dB means an attenuation of the + * voltage to 50% and an attenuation of the power to 25%, -inf dB means + * silence. + * + * \see volume + */ + Q_PROPERTY(qreal volumeDecibel READ volumeDecibel WRITE setVolumeDecibel) + /** + * This property holds the (hardware) destination for the output. + * + * The default device is determined by the category and the global + * configuration for that category of outputs. Normally you don't need + * to override this setting - letting the user change the global + * configuration is the right choice. You can still override the + * device though, if you have good reasons to do so. + * + * \see outputDeviceChanged + */ + Q_PROPERTY(AudioOutputDevice outputDevice READ outputDevice WRITE setOutputDevice) + + /** + * This property tells whether the output is muted. + * + * Muting the output has the same effect as calling setVolume(0.0). + */ + Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged) + public: + /** + * Creates a new AudioOutput that defines output to a physical + * device. + * + * \param category The category can be used by mixer applications to group volume + * controls of applications into categories. That makes it easier for + * the user to identify the programs. + * The category is also used for the default output device that is + * configured centrally. As an example: often users want to have the + * audio signal of a VoIP application go to their USB headset while + * all other sounds should go to the internal soundcard. + * + * \param parent QObject parent + * + * \see Phonon::categoryToString + * \see outputDevice + */ + explicit AudioOutput(Phonon::Category category, QObject *parent = 0); + explicit AudioOutput(QObject *parent = 0); + + QString name() const; + qreal volume() const; + qreal volumeDecibel() const; + + /** + * Returns the category of this output. + * + * \see AudioOutput(Phonon::Category, QObject *) + */ + Phonon::Category category() const; + AudioOutputDevice outputDevice() const; + bool isMuted() const; + + public Q_SLOTS: + void setName(const QString &newName); + void setVolume(qreal newVolume); + void setVolumeDecibel(qreal newVolumeDecibel); + bool setOutputDevice(const Phonon::AudioOutputDevice &newAudioOutputDevice); + void setMuted(bool mute); + + Q_SIGNALS: + /** + * This signal is emitted whenever the volume has changed. As the + * volume can change without a call to setVolume (calls over dbus) + * this is important + * to keep a widget showing the current volume up to date. + */ + void volumeChanged(qreal newVolume); + + /** + * This signal is emitted when the muted property has changed. As + * this property can change by IPC (DBus) calls a UI element showing + * the muted property should listen to this signal. + */ + void mutedChanged(bool); + + /** + * This signal is emitted when the (hardware) device for the output + * has changed. + * + * The change can happen either through setOutputDevice or if the + * global configuration for the used category has changed. + * + * \see outputDevice + */ + void outputDeviceChanged(const Phonon::AudioOutputDevice &newAudioOutputDevice); + + private: + Q_PRIVATE_SLOT(k_func(), void _k_volumeChanged(qreal)) + Q_PRIVATE_SLOT(k_func(), void _k_revertFallback()) + Q_PRIVATE_SLOT(k_func(), void _k_audioDeviceFailed()) + Q_PRIVATE_SLOT(k_func(), void _k_deviceListChanged()) + }; +} //namespace Phonon + +QT_END_NAMESPACE +QT_END_HEADER + +// vim: sw=4 ts=4 tw=80 +#endif // Phonon_AUDIOOUTPUT_H diff --git a/src/3rdparty/phonon/phonon/audiooutput_p.h b/src/3rdparty/phonon/phonon/audiooutput_p.h new file mode 100644 index 0000000..459b491 --- /dev/null +++ b/src/3rdparty/phonon/phonon/audiooutput_p.h @@ -0,0 +1,95 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef AUDIOOUTPUT_P_H +#define AUDIOOUTPUT_P_H + +#include "audiooutput.h" +#include "abstractaudiooutput_p.h" +#include "platform_p.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +class AudioOutputAdaptor; + +class AudioOutputPrivate : public AbstractAudioOutputPrivate +{ + Q_DECLARE_PUBLIC(AudioOutput) + PHONON_PRIVATECLASS + public: + inline static AudioOutputPrivate *cast(MediaNodePrivate *x) + { + if (x && x->castId == MediaNodePrivate::AudioOutputType) { + return static_cast<AudioOutputPrivate *>(x); + } + return 0; + } + void init(Phonon::Category c); + + + protected: + AudioOutputPrivate(CastId castId = MediaNodePrivate::AudioOutputType) + : AbstractAudioOutputPrivate(castId), + name(Platform::applicationName()), + volume(Platform::loadVolume(name)), +#ifndef QT_NO_DBUS + adaptor(0), +#endif + deviceBeforeFallback(-1), + outputDeviceOverridden(false), + muted(false) + { + } + + ~AudioOutputPrivate(); + + enum DeviceChangeType { + FallbackChange, + HigherPreferenceChange + }; + void handleAutomaticDeviceChange(const AudioOutputDevice &newDev, DeviceChangeType type); + + void _k_volumeChanged(qreal); + void _k_revertFallback(); + void _k_audioDeviceFailed(); + void _k_deviceListChanged(); + + private: + QString name; + Phonon::AudioOutputDevice device; + qreal volume; +#ifndef QT_NO_DBUS + Phonon::AudioOutputAdaptor *adaptor; +#endif + Category category; + int deviceBeforeFallback; + bool outputDeviceOverridden; + bool muted; +}; +} //namespace Phonon + +QT_END_NAMESPACE + +#endif // AUDIOOUTPUT_P_H +// vim: sw=4 ts=4 tw=80 diff --git a/src/3rdparty/phonon/phonon/audiooutputadaptor.cpp b/src/3rdparty/phonon/phonon/audiooutputadaptor.cpp new file mode 100644 index 0000000..2c01773 --- /dev/null +++ b/src/3rdparty/phonon/phonon/audiooutputadaptor.cpp @@ -0,0 +1,101 @@ +/* + * This file was generated by dbusidl2cpp version 0.4 + * when processing input file org.kde.Phonon.AudioOutput.xml + * + * dbusidl2cpp is Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * This is an auto-generated file. This file has been hand-edited. + */ + +#include "audiooutputadaptor_p.h" +#include "audiooutput.h" +#include <QtCore/QArgument> +#include <QtCore/QByteRef> +#include <QtCore/QList> +#include <QtCore/QMap> +#include <QtCore/QString> +#include <QtCore/QStringList> +#include <QtCore/QVariant> +#include "phononnamespace_p.h" +#include "objectdescription.h" + +#ifndef QT_NO_DBUS + +/* + * Implementation of adaptor class AudioOutputAdaptor + */ + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + +AudioOutputAdaptor::AudioOutputAdaptor(QObject *parent) + : QDBusAbstractAdaptor(parent) +{ + // constructor + setAutoRelaySignals(true); +} + +AudioOutputAdaptor::~AudioOutputAdaptor() +{ + // destructor +} + +double AudioOutputAdaptor::volume() const +{ + // get the value of property volume + return qvariant_cast<qreal>(parent()->property("volume")); +} + +void AudioOutputAdaptor::setVolume(double value) +{ + // set the value of property volume + parent()->setProperty("volume", QVariant::fromValue(static_cast<qreal>(value))); +} + +bool AudioOutputAdaptor::muted() const +{ + return parent()->property("muted").toBool(); +} + +void AudioOutputAdaptor::setMuted(bool value) +{ + parent()->setProperty("muted", value); +} + +QString AudioOutputAdaptor::category() +{ + // handle method call org.kde.Phonon.AudioOutput.category + return Phonon::categoryToString(static_cast<Phonon::AudioOutput *>(parent())->category()); +} + +QString AudioOutputAdaptor::name() +{ + // handle method call org.kde.Phonon.AudioOutput.name + QString name; + //QMetaObject::invokeMethod(parent(), "name", Q_RETURN_ARG(QString, name)); + + // Alternative: + name = static_cast<Phonon::AudioOutput *>(parent())->name(); + return name; +} + +int AudioOutputAdaptor::outputDeviceIndex() const +{ + return static_cast<Phonon::AudioOutput *>(parent())->outputDevice().index(); +} + +void AudioOutputAdaptor::setOutputDeviceIndex(int newAudioOutputDeviceIndex) +{ + static_cast<Phonon::AudioOutput *>(parent()) + ->setOutputDevice(Phonon::AudioOutputDevice::fromIndex(newAudioOutputDeviceIndex)); +} + +} // namespace Phonon + +QT_END_NAMESPACE + +#include "moc_audiooutputadaptor_p.cpp" + +#endif diff --git a/src/3rdparty/phonon/phonon/audiooutputadaptor_p.h b/src/3rdparty/phonon/phonon/audiooutputadaptor_p.h new file mode 100644 index 0000000..7178e9b --- /dev/null +++ b/src/3rdparty/phonon/phonon/audiooutputadaptor_p.h @@ -0,0 +1,109 @@ +/* + * This file was generated by dbusidl2cpp version 0.4 + * when processing input file org.kde.Phonon.AudioOutput.xml + * + * dbusidl2cpp is Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * This is an auto-generated file. This file has been hand-edited. + */ + +#ifndef AUDIOOUTPUTADAPTOR_P_H +#define AUDIOOUTPUTADAPTOR_P_H + +#include <QtCore/QObject> + +#ifndef QT_NO_DBUS +#include <QtDBus/QtDBus> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +class QByteArray; +template<class T> class QList; +template<class Key, class Value> class QMap; +class QString; +class QStringList; +class QVariant; + +namespace Phonon +{ + class AudioOutputPrivate; + class AudioOutput; + +/* + * Adaptor class for interface org.kde.Phonon.AudioOutput + */ +class AudioOutputAdaptor: public QDBusAbstractAdaptor +{ + friend class Phonon::AudioOutputPrivate; + friend class Phonon::AudioOutput; + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.Phonon.AudioOutput") + Q_CLASSINFO("D-Bus Introspection", "" +" <interface name=\"org.kde.Phonon.AudioOutput\" >\n" +" <property access=\"readwrite\" type=\"d\" name=\"volume\" />\n" +" <property access=\"readwrite\" type=\"b\" name=\"muted\" />\n" +" <property access=\"readwrite\" type=\"i\" name=\"outputDeviceIndex\" />\n" +" <signal name=\"volumeChanged\" >\n" +" <arg direction=\"out\" type=\"d\" />\n" +" </signal>\n" +" <signal name=\"mutedChanged\" >\n" +" <arg direction=\"out\" type=\"b\" />\n" +" </signal>\n" +" <signal name=\"outputDeviceIndexChanged\" >\n" +" <arg direction=\"out\" type=\"i\" />\n" +" </signal>\n" +" <signal name=\"nameChanged\" >\n" +" <arg direction=\"out\" type=\"s\" name=\"newName\" />\n" +" </signal>\n" +" <signal name=\"newOutputAvailable\" >\n" +" <arg direction=\"out\" type=\"s\" name=\"service\" />\n" +" <arg direction=\"out\" type=\"s\" name=\"path\" />\n" +" </signal>\n" +" <signal name=\"outputDestroyed\" >\n" +" </signal>\n" +" <method name=\"category\" >\n" +" <arg direction=\"out\" type=\"s\" />\n" +" </method>\n" +" <method name=\"name\" >\n" +" <arg direction=\"out\" type=\"s\" />\n" +" </method>\n" +" </interface>\n" + "") +public: + AudioOutputAdaptor(QObject *parent); + virtual ~AudioOutputAdaptor(); + +public: // PROPERTIES + Q_PROPERTY(bool muted READ muted WRITE setMuted) + bool muted() const; + void setMuted(bool value); + + Q_PROPERTY(int outputDeviceIndex READ outputDeviceIndex WRITE setOutputDeviceIndex) + int outputDeviceIndex() const; + void setOutputDeviceIndex(int value); + + Q_PROPERTY(double volume READ volume WRITE setVolume) + double volume() const; + void setVolume(double value); + +public Q_SLOTS: // METHODS + QString category(); + QString name(); +Q_SIGNALS: // SIGNALS + void mutedChanged(bool in0); + void nameChanged(const QString &newName); + void newOutputAvailable(const QString &service, const QString &path); + void outputDestroyed(); + void outputDeviceIndexChanged(int in0); + void volumeChanged(qreal in0); +}; + +} // namespace Phonon + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // QT_NO_DBUS + +#endif // AUDIOOUTPUTADAPTOR_P_H diff --git a/src/3rdparty/phonon/phonon/audiooutputinterface.cpp b/src/3rdparty/phonon/phonon/audiooutputinterface.cpp new file mode 100644 index 0000000..be2780d --- /dev/null +++ b/src/3rdparty/phonon/phonon/audiooutputinterface.cpp @@ -0,0 +1,40 @@ +/* This file is part of the KDE project + Copyright (C) 2008 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ +#include "audiooutputinterface.h" +#include <QtCore/QList> +#include <QtCore/QStringList> +#include <QtCore/QPair> +#include "platform_p.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + +QList<QPair<QByteArray, QString> > AudioOutputInterface42::deviceAccessListFor(const Phonon::AudioOutputDevice &deviceDesc) const +{ + return Platform::deviceAccessListFor(deviceDesc); +} + +} // namespace Phonon + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/phonon/audiooutputinterface.h b/src/3rdparty/phonon/phonon/audiooutputinterface.h new file mode 100644 index 0000000..1511e02 --- /dev/null +++ b/src/3rdparty/phonon/phonon/audiooutputinterface.h @@ -0,0 +1,151 @@ +/* This file is part of the KDE project + Copyright (C) 2007-2008 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_AUDIOOUTPUTINTERFACE_H +#define PHONON_AUDIOOUTPUTINTERFACE_H + +#include "phononnamespace.h" +#include "objectdescription.h" +#include "phonondefs.h" +#include <QtCore/QtGlobal> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +/** \class AudioOutputInterface audiooutputinterface.h Phonon/AudioOutputInterface + * \short Interface for AudioOutput objects + * + * The implementation can make use of the signals + * \code + void volumeChanged(qreal newVolume); + void audioDeviceFailed(); + * \endcode + * to notify the frontend whenever the volume has changed or when an audioDeviceFailed (e.g. USB + * unplug or sound server failure). + * + * \author Matthias Kretz <kretz@kde.org> + */ +class AudioOutputInterface40 +{ + public: + virtual ~AudioOutputInterface40() {} + + /** + * Returns the current software volume. + * + * A value of 0.0 means muted, 1.0 means unchanged, 2.0 means double voltage (i.e. all + * samples are multiplied by 2). + */ + virtual qreal volume() const = 0; + /** + * Sets the new current software volume. + * + * A value of 0.0 means muted, 1.0 means unchanged, 2.0 means double voltage (i.e. all + * samples are multiplied by 2). + * + * Everytime the volume in the backend changes it should emit volumeChanged(qreal), also + * inside this function. + */ + virtual void setVolume(qreal) = 0; + + /** + * Returns the index of the device that is used. The index is the number returned from + * BackendInterface::objectDescriptionIndexes(AudioOutputDeviceType). + */ + virtual int outputDevice() const = 0; + /** + * \deprecated + * + * Requests to change the current output device to the one identified by the passed index. + * + * The index is the number returned from + * BackendInterface::objectDescriptionIndexes(AudioOutputDeviceType). + * + * \returns \c true if the requested device works and is used after this call. + * \returns \c false if something failed and the device is not used after this call. + */ + virtual bool setOutputDevice(int) = 0; +}; + +class AudioOutputInterface42 : public AudioOutputInterface40 +{ + public: + /** + * Requests to change the current output device. + * + * \returns \c true if the requested device works and is used after this call. + * \returns \c false if something failed and the device is not used after this call. + */ + virtual bool setOutputDevice(const Phonon::AudioOutputDevice &) = 0; + + using AudioOutputInterface40::setOutputDevice; + + /** + * Helper function for backends to get a list of (driver, handle) pairs for + * AudioOutputDevice objects that are listed by the platform plugin. + * + * Example: + * \code + typedef QPair<QByteArray, QString> PhononDeviceAccess; + const QList<PhononDeviceAccess> &deviceAccessList = deviceAccessListFor(deviceDesc); + foreach (const PhononDeviceAccess &access, deviceAccessList) { + const QByteArray &driver = access.first; + const QString &handle = access.second; + if (openDevice(driver, handle)) { + // we found the first pair in the list that works. done. + return; + } + // continue trying the other (driver, handle) pairs + } + // none of the (driver, handle) pairs worked, that means the whole AudioOutputDevice is + // inaccessible and the frontend needs to know (either by emitting audioDeviceFailed or + // returning false when called from setOutputDevice) + * \endcode + * + * At the time of this writing the following driver strings are known to be in use: + * \li \c alsa: The handle is the string to pass to snd_pcm_open (e.g. "dmix:CARD=0,DEV=1") + * \li \c oss: The handle is the device file (e.g. "/dev/dsp") + * \li \c pulseaudio: The handle contains the server string and the sink/source name + * separated by a newline character. + * (e.g. unix:/tmp/pulse-mkretz/native\nalsa_output.pci_8086_293e_sound_card_0_alsa_playback_0) + */ + PHONON_EXPORT QList<QPair<QByteArray, QString> > deviceAccessListFor(const Phonon::AudioOutputDevice &) const; +}; + +} // namespace Phonon + +#ifdef PHONON_BACKEND_VERSION_4_2 +namespace Phonon { typedef AudioOutputInterface42 AudioOutputInterface; } +Q_DECLARE_INTERFACE(Phonon::AudioOutputInterface40, "AudioOutputInterface2.phonon.kde.org") +Q_DECLARE_INTERFACE(Phonon::AudioOutputInterface, "3AudioOutputInterface.phonon.kde.org") +#else +namespace Phonon { typedef AudioOutputInterface40 AudioOutputInterface; } +Q_DECLARE_INTERFACE(Phonon::AudioOutputInterface, "AudioOutputInterface2.phonon.kde.org") +Q_DECLARE_INTERFACE(Phonon::AudioOutputInterface42, "3AudioOutputInterface.phonon.kde.org") +#endif + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // PHONON_AUDIOOUTPUTINTERFACE_H diff --git a/src/3rdparty/phonon/phonon/backend.dox b/src/3rdparty/phonon/phonon/backend.dox new file mode 100644 index 0000000..8a9c5b2 --- /dev/null +++ b/src/3rdparty/phonon/phonon/backend.dox @@ -0,0 +1,107 @@ +/** +\page phonon_Backend The Backend Class +\ingroup Backend + +\section phonon_Backend_requiredfunctions Required Functions +\li bool \ref phonon_Backend_supportsOSD "supportsOSD()" +\li bool \ref phonon_Backend_supportsFourcc "supportsFourcc( quint32 )" +\li bool \ref phonon_Backend_supportsSubtitles "supportsSubtitles()" +\li bool \ref phonon_Backend_supportsVideo "supportsVideo()" +\li QStringList \ref phonon_Backend_availableMimeTypes "availableMimeTypes()" + +\section Member Function Documentation + +\subsection phonon_Backend_supportsFourcc bool supportsFourcc( quint32 fourcc ) +Tells whether the FOURCC (four character code) is supported for +the \ref phonon_VideoDataOutput "VideoDataOutput" interface. If you return \c true, you have to be +able to return VideoFrame objects accordingly from +\ref phonon_VideoDataOutput "VideoDataOutput". +\param fourcc A four character code defining a video frame format. +\returns \c true if your \ref phonon_VideoDataOutput "VideoDataOutput" can +output video frames in the requested format. +\returns \c false if the video frames can not be converted into the requested +format. + +\subsection phonon_Backend_availableMimeTypes QStringList availableMimeTypes() +Lists the MIME types the backend can read and decode. + +\subsection phonon_Backend_xIndexes QSet<int> <device/codec/effect/...>Indexes() + Returns a set of indexes that identify the devices/codecs/effects/... the + backend supports. This list needs to be compiled from looking at + available hardware and virtual devices/plugins/... . The implementation + should use cached information, but you need to invalidate the cache + whenever the hardware configuration changes or new virtual devices come + available/new plugins are installed/... . + + \return The indexes of the available devices/codecs/effects/... + + \see \ref phonon_Backend_xName + \see \ref phonon_Backend_xDescription + +\subsection phonon_Backend_xName QString <device/codec/effect/...>Name( int index ) + Returns the name of the given device/codec/effect/... + + \param index The index of one device/codec/effect/... this is one index + out of \ref phonon_Backend_xIndexes + + \returns A translated user visible string to name the device. + + \see \ref phonon_Backend_xIndexes + \see \ref phonon_Backend_xDescription +\subsection phonon_Backend_xDescription QString <device/codec/effect/...>Description( int index ) + Returns the description of the given device/codec/effect/... + + \param index The index of one device/codec/effect/... this is one index + out of \ref phonon_Backend_xIndexes + + \returns A translated user visible string to describe the device. + + \see \ref phonon_Backend_xIndexes + \see \ref phonon_Backend_xName + +\subsection phonon_Backend_audioCaptureDeviceVideoIndex qint32 audioCaptureDeviceVideoIndex( int index ) + \param index The index of the device. This is one of the indexes the backend + returned via \ref phonon_Backend_xIndexes + \returns An index of a video capture device that is associated with the given + audio capture device. For example a webcam might have both a video and an audio + capture device, and in order give the user a hint that the audio and video + capture devices belong together this index is used. + \returns If there is no associated video capture device return -1. + +\subsection phonon_Backend_videoCaptureDeviceAudioIndex qint32 videoCaptureDeviceAudioIndex( int index ) + \param index The index of the device. This is one of the indexes the backend + returned via \ref phonon_Backend_xIndexes + \returns An index of a audio capture device that is associated with the given + video capture device. For example a webcam might have both a audio and an video + capture device, and in order give the user a hint that the video and audio + capture devices belong together this index is used. + \returns If there is no associated audio capture device return -1. + +\page phonon_AudioDataOutput The AudioDataOutput Class +\ingroup Backend + +\page phonon_AudioOutput The AudioOutput Class +\ingroup Backend + +\page phonon_VideoDataOutput The VideoDataOutput Class +\ingroup Backend + +\page phonon_VideoWidget The VideoWidget Class +\ingroup Backend + +\page phonon_Effect The Effect Class +\ingroup Backend + +\page phonon_BrightnessControl The BrightnessControl Class +\ingroup Backend + +\page phonon_VideoEffect The VideoEffect Class +\ingroup Backend + +\page phonon_Visualization The Visualization Class +\ingroup Backend + +\page phonon_VolumeFaderEffect The VolumeFaderEffect Class +\ingroup Backend + +*/ diff --git a/src/3rdparty/phonon/phonon/backendcapabilities.cpp b/src/3rdparty/phonon/phonon/backendcapabilities.cpp new file mode 100644 index 0000000..5dee6a0 --- /dev/null +++ b/src/3rdparty/phonon/phonon/backendcapabilities.cpp @@ -0,0 +1,121 @@ +/* This file is part of the KDE project + Copyright (C) 2005-2006 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#include "backendcapabilities.h" +#include "backendcapabilities_p.h" + +#include "phonondefs_p.h" +#include "backendinterface.h" +#include "factory_p.h" +#include "globalconfig_p.h" +#include "globalstatic_p.h" +#include "objectdescription.h" + +#include <QtCore/QList> +#include <QtCore/QSet> +#include <QtCore/QStringList> + +QT_BEGIN_NAMESPACE + +PHONON_GLOBAL_STATIC(Phonon::BackendCapabilitiesPrivate, globalBCPrivate) + +namespace Phonon +{ + +BackendCapabilities::Notifier *BackendCapabilities::notifier() +{ + return globalBCPrivate; +} + +QStringList BackendCapabilities::availableMimeTypes() +{ + if (BackendInterface *backendIface = qobject_cast<BackendInterface *>(Factory::backend())) + return backendIface->availableMimeTypes(); + else + return QStringList(); +} + +bool BackendCapabilities::isMimeTypeAvailable(const QString &mimeType) +{ + QObject *m_backendObject = Factory::backend(false); + if (!m_backendObject) { + if (!Factory::isMimeTypeAvailable(mimeType)) { + return false; + } + // without loading the backend we found out that the MIME type might be supported, now we + // want to know for certain. For that we need to load the backend. + m_backendObject = Factory::backend(true); + } + if (!m_backendObject) { + // no backend == no MIME type supported at all + return false; + } + return availableMimeTypes().contains(mimeType); +} + +QList<AudioOutputDevice> BackendCapabilities::availableAudioOutputDevices() +{ + QList<AudioOutputDevice> ret; + const QList<int> deviceIndexes = GlobalConfig().audioOutputDeviceListFor(Phonon::NoCategory); + foreach (int i, deviceIndexes) { + ret.append(AudioOutputDevice::fromIndex(i)); + } + return ret; +} + + +#ifndef QT_NO_PHONON_AUDIOCAPTURE +QList<AudioCaptureDevice> BackendCapabilities::availableAudioCaptureDevices() +{ + QList<AudioCaptureDevice> ret; + const QList<int> deviceIndexes = GlobalConfig().audioCaptureDeviceListFor(Phonon::NoCategory); + foreach (int i, deviceIndexes) { + ret.append(AudioCaptureDevice::fromIndex(i)); + } + return ret; +} +#endif //QT_NO_PHONON_AUDIOCAPTURE + +#ifndef QT_NO_PHONON_EFFECT +QList<EffectDescription> BackendCapabilities::availableAudioEffects() +{ + BackendInterface *backendIface = qobject_cast<BackendInterface *>(Factory::backend()); + QList<EffectDescription> ret; + if (backendIface) { + QList<int> deviceIndexes = backendIface->objectDescriptionIndexes(Phonon::EffectType); + foreach (int i, deviceIndexes) { + ret.append(EffectDescription::fromIndex(i)); + } + } + return ret; +} +#endif //QT_NO_PHONON_EFFECT + +} // namespace Phonon + +QT_END_NAMESPACE + +#include "moc_backendcapabilities.cpp" + +// vim: sw=4 ts=4 + + diff --git a/src/3rdparty/phonon/phonon/backendcapabilities.h b/src/3rdparty/phonon/phonon/backendcapabilities.h new file mode 100644 index 0000000..65b2830 --- /dev/null +++ b/src/3rdparty/phonon/phonon/backendcapabilities.h @@ -0,0 +1,213 @@ +/* This file is part of the KDE project + Copyright (C) 2005-2006 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef Phonon_BACKENDCAPABILITIES_H +#define Phonon_BACKENDCAPABILITIES_H + +#include "phonon_export.h" +#include "objectdescription.h" + +#include <QtCore/QObject> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +#ifdef __QT_SYNCQT__ +// Tell syncqt that the BackendCapabilities namespace should be treated like a class +#pragma qt_class(Phonon::BackendCapabilities) +#pragma qt_sync_stop_processing +#endif + +template<class T> class QList; +class QStringList; + +namespace Phonon +{ + +/** + * Collection of functions describing the capabilities of the Backend. + * + * \ingroup BackendInformation + * \author Matthias Kretz <kretz@kde.org> + */ +namespace BackendCapabilities +{ + /** \class Notifier backendcapabilities.h Phonon/BackendCapabilities + * Notifications about backend capabilities. + * + * \ingroup BackendInformation + */ + class Notifier : public QObject + { + Q_OBJECT + Q_SIGNALS: + /** + * This signal is emitted if the capabilities have changed. This can + * happen if the user has requested a backend change. + */ + void capabilitiesChanged(); + + /** + * This signal is emitted when audio output devices were plugged or + * unplugged. + * + * Check BackendCapabilities::availableAudioOutputDevices to get the + * current list of available devices. + */ + void availableAudioOutputDevicesChanged(); + + /** + * This signal is emitted when audio capture devices were plugged or + * unplugged. + * + * Check BackendCapabilities::availableAudioCaptureDevices to get the + * current list of available devices. + */ +#ifndef QT_NO_PHONON_AUDIOCAPTURE + void availableAudioCaptureDevicesChanged(); +#endif //QT_NO_PHONON_AUDIOCAPTURE + }; + + /** + * Use this function to get a QObject pointer to connect to the capabilitiesChanged signal. + * + * \return a pointer to a QObject. + * + * The capabilitiesChanged signal is emitted if the capabilities have changed. This can + * happen if the user has requested a backend change. + * + * To connect to this signal do the following: + * \code + * QObject::connect(BackendCapabilities::notifier(), SIGNAL(capabilitiesChanged()), ... + * \endcode + * + * \see Notifier::capabilitiesChanged() + */ + PHONON_EXPORT Notifier *notifier(); + + /** + * Returns a list of mime types that the Backend can decode. + * + * \see isMimeTypeAvailable() + */ + PHONON_EXPORT QStringList availableMimeTypes(); + + /** + * Often all you want to know is whether one given MIME type can be + * decoded by the backend. Use this method in favor of availableMimeTypes() + * as it can give you a negative answer without having a backend loaded. + * + * \see availableMimeTypes(); + */ + PHONON_EXPORT bool isMimeTypeAvailable(const QString &mimeType); + + /** + * Returns the audio output devices the backend supports. + * + * \return A list of AudioOutputDevice objects that give a name and + * description for every supported audio output device. + */ + PHONON_EXPORT QList<AudioOutputDevice> availableAudioOutputDevices(); + + /** + * Returns the audio capture devices the backend supports. + * + * \return A list of AudioCaptureDevice objects that give a name and + * description for every supported audio capture device. + */ +#ifndef QT_NO_PHONON_AUDIOCAPTURE + PHONON_EXPORT QList<AudioCaptureDevice> availableAudioCaptureDevices(); +#endif //QT_NO_PHONON_AUDIOCAPTURE + + /** + * Returns the video output devices the backend supports. + * + * \return A list of VideoOutputDevice objects that give a name and + * description for every supported video output device. + */ +// PHONON_EXPORT QList<VideoOutputDevice> availableVideoOutputDevices(); + + /** + * Returns the video capture devices the backend supports. + * + * \return A list of VideoCaptureDevice objects that give a name and + * description for every supported video capture device. + */ +// PHONON_EXPORT QList<VideoCaptureDevice> availableVideoCaptureDevices(); + + /** + * Returns the visualization effects the backend supports. + * + * \return A list of VisualizationEffect objects that give a name and + * description for every supported visualization effect. + */ +// PHONON_EXPORT QList<VisualizationDescription> availableVisualizations(); + + /** + * Returns descriptions for the audio effects the backend supports. + * + * \return A list of AudioEffectDescription objects that give a name and + * description for every supported audio effect. + */ +#ifndef QT_NO_PHONON_EFFECT + PHONON_EXPORT QList<EffectDescription> availableAudioEffects(); +#endif //QT_NO_PHONON_EFFECT + +//X /** +//X * Returns descriptions for the video effects the backend supports. +//X * +//X * \return A list of VideoEffectDescription objects that give a name and +//X * description for every supported video effect. +//X */ +//X PHONON_EXPORT QList<EffectDescription> availableVideoEffects(); + + /** + * Returns descriptions for the audio codecs the backend supports. + * + * \return A list of AudioCodec objects that give a name and + * description for every supported audio codec. + */ +// PHONON_EXPORT QList<AudioCodecDescription> availableAudioCodecs(); + + /** + * Returns descriptions for the video codecs the backend supports. + * + * \return A list of VideoCodec objects that give a name and + * description for every supported video codec. + */ +// PHONON_EXPORT QList<VideoCodecDescription> availableVideoCodecs(); + + /** + * Returns descriptions for the container formats the backend supports. + * + * \return A list of ContainerFormat objects that give a name and + * description for every supported container format. + */ +// PHONON_EXPORT QList<ContainerFormatDescription> availableContainerFormats(); +} // namespace BackendCapabilities +} // namespace Phonon + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // Phonon_BACKENDCAPABILITIES_H +// vim: sw=4 ts=4 tw=80 diff --git a/src/3rdparty/phonon/phonon/backendcapabilities_p.h b/src/3rdparty/phonon/phonon/backendcapabilities_p.h new file mode 100644 index 0000000..c17f24f --- /dev/null +++ b/src/3rdparty/phonon/phonon/backendcapabilities_p.h @@ -0,0 +1,50 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_BACKENDCAPABILITIES_P_H +#define PHONON_BACKENDCAPABILITIES_P_H + +#include "backendcapabilities.h" +#include <QtCore/QObject> +#include "factory_p.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +class BackendCapabilitiesPrivate : public BackendCapabilities::Notifier +{ + public: + BackendCapabilitiesPrivate() + { + connect(Factory::sender(), SIGNAL(backendChanged()), SIGNAL(capabilitiesChanged())); + connect(Factory::sender(), SIGNAL(availableAudioOutputDevicesChanged()), SIGNAL(availableAudioOutputDevicesChanged())); + connect(Factory::sender(), SIGNAL(availableAudioCaptureDevicesChanged()), SIGNAL(availableAudioCaptureDevicesChanged())); + } +}; + +} // namespace Phonon + +QT_END_NAMESPACE + +#endif // PHONON_BACKENDCAPABILITIES_P_H +// vim: sw=4 sts=4 et tw=100 diff --git a/src/3rdparty/phonon/phonon/backendinterface.h b/src/3rdparty/phonon/phonon/backendinterface.h new file mode 100644 index 0000000..e1f11da --- /dev/null +++ b/src/3rdparty/phonon/phonon/backendinterface.h @@ -0,0 +1,287 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_BACKENDINTERFACE_H +#define PHONON_BACKENDINTERFACE_H + +#include "phonon_export.h" +#include "objectdescription.h" + +#include <QtCore/QtGlobal> +#include <QtCore/QSet> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +class QVariant; + +namespace Phonon +{ + +/** \class BackendInterface backendinterface.h Phonon/BackendInterface + * \short Main Backend class interface + * + * This interface defines the main factory of the backend. The createObject function creates all the + * objects needed by the frontend. + * + * The objectDescriptionIndexes and objectDescriptionProperties functions return information about + * available devices, effects and codecs. + * + * An implementation could look like this: + * \code + * QObject *Backend::createObject(BackendInterface::Class c, QObject *parent, const QList<QVariant> &args) + * { + * switch (c) { + * case MediaObjectClass: + * return new MediaObject(parent); + * case VolumeFaderEffectClass: + * return new VolumeFaderEffect(parent); + * case AudioOutputClass: + * return new AudioOutput(parent); + * case AudioDataOutputClass: + * return new AudioDataOutput(parent); + * case VisualizationClass: + * return new Visualization(parent); + * case VideoDataOutputClass: + * return new VideoDataOutput(parent); + * case EffectClass: + * return new Effect(args[0].toInt(), parent); + * case VideoWidgetClass: + * return new VideoWidget(qobject_cast<QWidget *>(parent)); + * } + * return 0; + * } + * + * QSet<int> Backend::objectDescriptionIndexes(ObjectDescriptionType type) const + * { + * QSet<int> set; + * switch(type) + * { + * case Phonon::AudioOutputDeviceType: + * // use AudioDeviceEnumerator to list ALSA and OSS devices + * set << 10000 << 10001; + * break; + * case Phonon::AudioCaptureDeviceType: + * set << 20000 << 20001; + * break; + * case Phonon::VideoOutputDeviceType: + * break; + * case Phonon::VideoCaptureDeviceType: + * set << 30000 << 30001; + * break; + * case Phonon::VisualizationType: + * case Phonon::AudioCodecType: + * case Phonon::VideoCodecType: + * case Phonon::ContainerFormatType: + * break; + * case Phonon::EffectType: + * set << 0x7F000001; + * break; + * } + * return set; + * } + * + * QHash<QByteArray, QVariant> Backend::objectDescriptionProperties(ObjectDescriptionType type, int index) const + * { + * QHash<QByteArray, QVariant> ret; + * switch (type) { + * case Phonon::AudioOutputDeviceType: + * switch (index) { + * case 10000: + * ret.insert("name", QLatin1String("internal Soundcard")); + * break; + * case 10001: + * ret.insert("name", QLatin1String("USB Headset")); + * ret.insert("icon", KIcon("usb-headset")); + * ret.insert("available", false); + * break; + * } + * break; + * case Phonon::AudioCaptureDeviceType: + * switch (index) { + * case 20000: + * ret.insert("name", QLatin1String("Soundcard")); + * ret.insert("description", QLatin1String("first description")); + * break; + * case 20001: + * ret.insert("name", QLatin1String("DV")); + * ret.insert("description", QLatin1String("second description")); + * break; + * } + * break; + * case Phonon::VideoOutputDeviceType: + * break; + * case Phonon::VideoCaptureDeviceType: + * switch (index) { + * case 30000: + * ret.insert("name", QLatin1String("USB Webcam")); + * ret.insert("description", QLatin1String("first description")); + * break; + * case 30001: + * ret.insert("name", QLatin1String("DV")); + * ret.insert("description", QLatin1String("second description")); + * break; + * } + * break; + * case Phonon::VisualizationType: + * break; + * case Phonon::AudioCodecType: + * break; + * case Phonon::VideoCodecType: + * break; + * case Phonon::ContainerFormatType: + * break; + * case Phonon::EffectType: + * switch (index) { + * case 0x7F000001: + * ret.insert("name", QLatin1String("Delay")); + * ret.insert("description", QLatin1String("Simple delay effect with time, feedback and level controls.")); + * break; + * } + * break; + * } + * return ret; + * } + * \endcode + * + * \author Matthias Kretz <kretz@kde.org> + */ +class BackendInterface +{ + public: + /** + * \internal + * + * Silence gcc's warning. + */ + virtual ~BackendInterface() {} + + /** + * Classes that the createObject function has to handle. + */ + enum Class { + /** + * Request to return a %MediaObject object. + */ + MediaObjectClass, + /** + * Request to return a %VolumeFaderEffect object. + */ + VolumeFaderEffectClass, + /** + * Request to return a %AudioOutput object. + */ + AudioOutputClass, + /** + * Request to return a %AudioDataOutput object. + */ + AudioDataOutputClass, + /** + * Request to return a %Visualization object. + */ + VisualizationClass, + /** + * Request to return a %VideoDataOutput object. + */ + VideoDataOutputClass, + /** + * Request to return a %Effect object. + * + * Takes an additional int that specifies the effect Id. + */ + EffectClass, + /** + * Request to return a %VideoWidget object. + */ + VideoWidgetClass + }; + + /** + * Returns a new instance of the requested class. + * + * \param c The requested class. + * \param parent The parent object. + * \param args Additional arguments (documented in \ref Class). + */ + virtual QObject *createObject(Class c, QObject *parent, const QList<QVariant> &args = QList<QVariant>()) = 0; + + /** + * Returns the unique identifiers for the devices/effects/codecs of the given \p type. + * + * \param type see \ref ObjectDescriptionType + */ + virtual QList<int> objectDescriptionIndexes(ObjectDescriptionType type) const = 0; + + /** + * Given a unique identifier that was returned from objectDescriptionIndexes this function + * returns a hash mapping property names to values. + * + * The property "name" must always be present. All other properties are optional. + * + * List of possible properties: + * \li \c \b name: The name of the device/effect/codec/... + * \li \c \b description: A text explaining what this device/effect/codec/... is/can do + * \li \c \b icon: An icon name (using the freedesktop naming scheme) or a QIcon for this + * device/effect/codec/... + * \li \c \b available: A bool telling whether the device is present or unplugged. + * + * \param type see \ref ObjectDescriptionType + * \param index The unique identifier that is returned from objectDescriptionIndexes + */ + virtual QHash<QByteArray, QVariant> objectDescriptionProperties(ObjectDescriptionType type, int index) const = 0; + + /** + * When this function is called the nodes given in the parameter list should not lose any + * signal data when connections are changed. + */ + virtual bool startConnectionChange(QSet<QObject *>) = 0; + + /** + * Defines a signal connection between the two given nodes. + */ + virtual bool connectNodes(QObject *, QObject *) = 0; + + /** + * Cuts a signal connection between the two given nodes. + */ + virtual bool disconnectNodes(QObject *, QObject *) = 0; + + /** + * When this function is called the nodes given in the parameter list may lose + * signal data when a port is not connected. + */ + virtual bool endConnectionChange(QSet<QObject *>) = 0; + + /** + * gets all available mime types + */ + virtual QStringList availableMimeTypes() const = 0; + +}; +} // namespace Phonon + +Q_DECLARE_INTERFACE(Phonon::BackendInterface, "BackendInterface3.phonon.kde.org") + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // PHONON_BACKENDINTERFACE_H diff --git a/src/3rdparty/phonon/phonon/effect.cpp b/src/3rdparty/phonon/phonon/effect.cpp new file mode 100644 index 0000000..c125232 --- /dev/null +++ b/src/3rdparty/phonon/phonon/effect.cpp @@ -0,0 +1,136 @@ +/* This file is part of the KDE project + Copyright (C) 2005-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ +#include "effect.h" +#include "effect_p.h" +#include "effectparameter.h" +#include "effectinterface.h" +#include "factory_p.h" + +#define PHONON_INTERFACENAME EffectInterface + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_EFFECT + +namespace Phonon +{ +Effect::~Effect() +{ +} + +Effect::Effect(const EffectDescription &description, QObject *parent) + : QObject(parent), MediaNode(*new EffectPrivate) +{ + K_D(Effect); + d->description = description; + d->createBackendObject(); +} + +Effect::Effect(EffectPrivate &dd, QObject *parent) + : QObject(parent), MediaNode(dd) +{ +} + +void EffectPrivate::createBackendObject() +{ + if (m_backendObject) + return; + Q_Q(Effect); + m_backendObject = Factory::createEffect(description.index(), q); + if (m_backendObject) { + setupBackendObject(); + } +} + +//X Effect::Type Effect::type() const +//X { +//X K_D(const Effect); +//X return d->type; +//X } +//X +EffectDescription Effect::description() const +{ + K_D(const Effect); + return d->description; +} + +QList<EffectParameter> Effect::parameters() const +{ + K_D(const Effect); + // there should be an iface object, but better be safe for those backend + // switching corner-cases: when the backend switches the new backend might + // not support this effect -> no iface object + if (d->m_backendObject) { + return INTERFACE_CALL(parameters()); + } + return QList<EffectParameter>(); +} + +QVariant Effect::parameterValue(const EffectParameter ¶m) const +{ + K_D(const Effect); + if (!d->m_backendObject) { + return d->parameterValues[param]; + } + return INTERFACE_CALL(parameterValue(param)); +} + +void Effect::setParameterValue(const EffectParameter ¶m, const QVariant &newValue) +{ + K_D(Effect); + d->parameterValues[param] = newValue; + if (d->backendObject()) { + INTERFACE_CALL(setParameterValue(param, newValue)); + } +} + +bool EffectPrivate::aboutToDeleteBackendObject() +{ + if (m_backendObject) { + const QList<EffectParameter> parameters = pINTERFACE_CALL(parameters()); + foreach (const EffectParameter &p, parameters) { + parameterValues[p] = pINTERFACE_CALL(parameterValue(p)); + } + } + return true; +} + +void EffectPrivate::setupBackendObject() +{ + Q_ASSERT(m_backendObject); + + // set up attributes + const QList<EffectParameter> parameters = pINTERFACE_CALL(parameters()); + foreach (const EffectParameter &p, parameters) { + pINTERFACE_CALL(setParameterValue(p, parameterValues[p])); + } +} + +} //namespace Phonon + +#endif //QT_NO_PHONON_EFFECT + +QT_END_NAMESPACE + +#include "moc_effect.cpp" + +// vim: sw=4 ts=4 tw=80 diff --git a/src/3rdparty/phonon/phonon/effect.h b/src/3rdparty/phonon/phonon/effect.h new file mode 100644 index 0000000..b3a7975 --- /dev/null +++ b/src/3rdparty/phonon/phonon/effect.h @@ -0,0 +1,119 @@ +/* This file is part of the KDE project + Copyright (C) 2005-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + + +#ifndef PHONON_EFFECT_H +#define PHONON_EFFECT_H + +#include "phonondefs.h" +#include <QtCore/QObject> +#include "objectdescription.h" +#include "medianode.h" + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_EFFECT + +class QString; +template<class T> class QList; + +namespace Phonon +{ + class EffectParameter; + class EffectPrivate; + + /** \class Effect effect.h Phonon/Effect + * \short Effects that can be inserted into a Path. + * An effect is a special object which can perform + * transformations on the specified path. Examples may include simple + * modifiers such as fading or pitch shifting, or more complex mathematical + * transformations. + * + * In order to use an effect, insert it into the path as follows: + * \code + * Path path = Phonon::createPath(...); + * Effect *effect = new Effect(this); + * path.insertEffect(effect); + * \endcode + * + * The effect will immediately begin applying it's transformations on + * the path. To stop it, remove the Effect from the path. + * + * \ingroup PhononEffects + * \author Matthias Kretz <kretz@kde.org> + */ + class PHONON_EXPORT Effect : public QObject, public MediaNode + { + Q_OBJECT + K_DECLARE_PRIVATE(Effect) + + public: + ~Effect(); + +//X enum Type { +//X AudioEffect, +//X VideoEffect +//X }; + + /** + * QObject constructor. + * + * \param description An EffectDescription object to determine the + * type of effect. See BackendCapabilities::availableAudioEffects(). + * \param parent QObject parent + */ + explicit Effect(const EffectDescription &description, QObject *parent = 0); + +//X Type type() const; + + /** + * Returns the description of this effect. This is the same type as was + * passed to the constructor. + */ + EffectDescription description() const; + + /** + * Returns a list of parameters that this effect provides to control + * its behaviour. + * + * \see EffectParameter + * \see EffectWidget + */ + QList<EffectParameter> parameters() const; + + QVariant parameterValue(const EffectParameter&) const; + void setParameterValue(const EffectParameter&, const QVariant &value); + + protected: + Effect(EffectPrivate &dd, QObject *parent); + }; +} //namespace Phonon + +#endif // QT_NO_EFFECT + +QT_END_NAMESPACE +QT_END_HEADER + +// vim: sw=4 ts=4 tw=80 +#endif // PHONON_EFFECT_H + diff --git a/src/3rdparty/phonon/phonon/effect_p.h b/src/3rdparty/phonon/phonon/effect_p.h new file mode 100644 index 0000000..586812a --- /dev/null +++ b/src/3rdparty/phonon/phonon/effect_p.h @@ -0,0 +1,61 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef EFFECT_P_H +#define EFFECT_P_H + +#include "effect.h" +#include "effectparameter.h" +#include "medianode_p.h" +#include <QtCore/QHash> +#include <QtCore/QVariant> +#include "phonondefs_p.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_EFFECT + +namespace Phonon +{ +class EffectPrivate : public MediaNodePrivate +{ + Q_DECLARE_PUBLIC(Effect) + PHONON_PRIVATECLASS + public: + virtual QObject *qObject() { return q_func(); } + protected: + EffectPrivate() + { + } + +//X Effect::Type type; + EffectDescription description; + QHash<EffectParameter, QVariant> parameterValues; +}; +} //namespace Phonon + +QT_END_NAMESPACE + +#endif //QT_NO_PHONON_EFFECT + +#endif // EFFECT_P_H +// vim: sw=4 ts=4 tw=80 diff --git a/src/3rdparty/phonon/phonon/effectinterface.h b/src/3rdparty/phonon/phonon/effectinterface.h new file mode 100644 index 0000000..f535105 --- /dev/null +++ b/src/3rdparty/phonon/phonon/effectinterface.h @@ -0,0 +1,68 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2008 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_EFFECTINTERFACE_H +#define PHONON_EFFECTINTERFACE_H + +#include "phononnamespace.h" +#include <QtCore/QVariant> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_EFFECT + +namespace Phonon +{ + class EffectParameter; + /** \class EffectInterface effectinterface.h Phonon/EffectInterface + * \short Interface for Effect objects + * + * \author Matthias Kretz <kretz@kde.org> + */ + class EffectInterface + { + public: + virtual ~EffectInterface() {} + /** + * Returns the EffectParameter objects to describe the parameters of this effect. + */ + virtual QList<EffectParameter> parameters() const = 0; + /** + * Returns the value for the selected parameter. + */ + virtual QVariant parameterValue(const EffectParameter &) const = 0; + /** + * Sets the value for the selected parameter. + */ + virtual void setParameterValue(const EffectParameter &, const QVariant &newValue) = 0; + }; +} //namespace Phonon + +Q_DECLARE_INTERFACE(Phonon::EffectInterface, "EffectInterface0.phonon.kde.org") + +#endif //QT_NO_PHONON_EFFECT + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // PHONON_EFFECTINTERFACE_H diff --git a/src/3rdparty/phonon/phonon/effectparameter.cpp b/src/3rdparty/phonon/phonon/effectparameter.cpp new file mode 100644 index 0000000..6030b6e --- /dev/null +++ b/src/3rdparty/phonon/phonon/effectparameter.cpp @@ -0,0 +1,142 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#include "effectparameter.h" +#include "effectparameter_p.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_EFFECT + +namespace Phonon +{ + +uint qHash(const Phonon::EffectParameter ¶m) +{ + return param.id(); +} + +EffectParameter::EffectParameter() + : d(new EffectParameterPrivate) +{ +} + +EffectParameter::EffectParameter(int parameterId, const QString &name, Hints hints, + const QVariant &defaultValue, const QVariant &min, const QVariant &max, + const QVariantList &values, const QString &description) + : d(new EffectParameterPrivate) +{ + d->parameterId = parameterId; + d->min = min; + d->max = max; + d->defaultValue = defaultValue; + d->name = name; + d->possibleValues = values; + d->description = description; + d->hints = hints; +} + +EffectParameter::~EffectParameter() +{ +} + +EffectParameter::EffectParameter(const EffectParameter &rhs) + : d(rhs.d) +{ +} + +EffectParameter &EffectParameter::operator=(const EffectParameter &rhs) +{ + d = rhs.d; + return *this; +} + +bool EffectParameter::operator<(const EffectParameter &rhs) const +{ + return d->parameterId < rhs.d->parameterId; +} + +bool EffectParameter::operator==(const EffectParameter &rhs) const +{ + return d->parameterId == rhs.d->parameterId; +} + +bool EffectParameter::operator>(const EffectParameter &rhs) const +{ + return d->parameterId > rhs.d->parameterId; +} + +const QString &EffectParameter::name() const +{ + return d->name; +} + +const QString &EffectParameter::description() const +{ + return d->description; +} + +bool EffectParameter::isLogarithmicControl() const +{ + return d->hints & LogarithmicHint; +} + +QVariant::Type EffectParameter::type() const +{ + if (d->possibleValues.isEmpty()) { + return d->defaultValue.type(); + } + return QVariant::String; +} + +QVariantList EffectParameter::possibleValues() const +{ + return d->possibleValues; +} + +QVariant EffectParameter::minimumValue() const +{ + return d->min; +} + +QVariant EffectParameter::maximumValue() const +{ + return d->max; +} + +QVariant EffectParameter::defaultValue() const +{ + return d->defaultValue; +} + +int EffectParameter::id() const +{ + return d->parameterId; +} + +} + +#endif //QT_NO_PHONON_EFFECT + +QT_END_NAMESPACE + +// vim: sw=4 ts=4 diff --git a/src/3rdparty/phonon/phonon/effectparameter.h b/src/3rdparty/phonon/phonon/effectparameter.h new file mode 100644 index 0000000..55c7049 --- /dev/null +++ b/src/3rdparty/phonon/phonon/effectparameter.h @@ -0,0 +1,237 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_EFFECTPARAMETER_H +#define PHONON_EFFECTPARAMETER_H + +#include "phonon_export.h" + +#include <QtCore/QExplicitlySharedDataPointer> +#include <QtCore/QVariant> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_EFFECT + +namespace Phonon +{ + +class Effect; +class EffectParameterPrivate; + +/** \class EffectParameter effectparameter.h Phonon/EffectParameter + * \brief This class describes one parameter of an effect. + * + * \ingroup PhononEffects + * \author Matthias Kretz <kretz@kde.org> + * \see Effect + */ +class PHONON_EXPORT EffectParameter +{ + friend class BrightnessControl; + public: + /** + * \internal + * + * Creates an invalid effect parameter. + */ + EffectParameter(); + + /** + * The name of the parameter. Can be used as the label. + * + * \return A label for the parameter. + */ + const QString &name() const; + + /** + * The parameter may come with a description (LADSPA doesn't have a + * field for this, so don't expect many effects to provide a + * description). + * + * The description can be used for a tooltip or WhatsThis help. + * + * \return A text describing the parameter. + */ + const QString &description() const; + + /** + * Returns the parameter type. + * + * Common types are QVariant::Int, QVariant::Double, QVariant::Bool and QVariant::String. When + * QVariant::String is returned you get the possible values from possibleValues. + */ + QVariant::Type type() const; + + /** + * Returns whether the parameter should be + * displayed using a logarithmic scale. This is particularly useful for + * frequencies and gains. + */ + bool isLogarithmicControl() const; + + /** + * The minimum value to be used for the control to edit the parameter. + * + * If the returned QVariant is invalid the value is not bounded from + * below. + */ + QVariant minimumValue() const; + + /** + * The maximum value to be used for the control to edit the parameter. + * + * If the returned QVariant is invalid the value is not bounded from + * above. + */ + QVariant maximumValue() const; + + /** + * The default value. + */ + QVariant defaultValue() const; + + /** + * The possible values to be used for the control to edit the parameter. + * + * if the value of this parameter is to be picked from predefined values + * this returns the list (otherwise it returns an empty QVariantList). + */ + QVariantList possibleValues() const; + + /** + * \internal + * compares the ids of the parameters + */ + bool operator<(const EffectParameter &rhs) const; + + /** + * \internal + * compares the ids of the parameters + */ + bool operator>(const EffectParameter &rhs) const; + + /** + * \internal + * compares the ids of the parameters + */ + bool operator==(const EffectParameter &rhs) const; + + /* dtor, cctor and operator= for forward decl of EffectParameterPrivate */ + ~EffectParameter(); + EffectParameter(const EffectParameter &rhs); + EffectParameter &operator=(const EffectParameter &rhs); + + /** + * Only for backend developers: + * + * Flags to set the return values of isToggleControl(), + * isLogarithmicControl(), isIntegerControl(), isBoundedBelow() and + * isBoundedAbove(). The values of the flags correspond to the values + * used for LADSPA effects. + */ + enum Hint { + /** + * If this hint is set it means that + * the the control has only two states: zero and non-zero. + * + * \see isToggleControl() + */ + ToggledHint = 0x04, + + /* LADSPA's SAMPLE_RATE hint needs to be translated by the backend + * to normal bounds, as the backend knows the sample rate - and the + * frontend doesn't */ + + /** + * \see isLogarithmicControl() + */ + LogarithmicHint = 0x10, + /** + * \see isIntegerControl + */ + IntegerHint = 0x20 + }; + Q_DECLARE_FLAGS(Hints, Hint) + + /** + * Only to be used by backend implementations: + * + * Creates a new effect parameter. + * + * \param parameterId This is a number to uniquely identify the + * parameter. The id is used for value() and setValue(). + * + * \param name The name/label for this parameter. + * + * \param hints Sets the hints for the type of parameter. + * + * \param defaultValue The value that should be used as a default. + * + * \param min The minimum value allowed for this parameter. You only + * need to set this if the BoundedBelowHint is set. + * + * \param max The maximum value allowed for this parameter. You only + * need to set this if the BoundedAboveHint is set. + * + * \param description A descriptive text for the parameter + * (explaining what it controls) to be used as a tooltip or + * WhatsThis help. + */ + EffectParameter(int parameterId, const QString &name, Hints hints, + const QVariant &defaultValue, const QVariant &min = QVariant(), + const QVariant &max = QVariant(), const QVariantList &values = QVariantList(), + const QString &description = QString()); + + /** + * \internal + * + * Returns the parameter's id. + */ + int id() const; + + protected: + /** + * The data is implicitly shared. + */ + QExplicitlySharedDataPointer<EffectParameterPrivate> d; +}; + +uint PHONON_EXPORT qHash(const Phonon::EffectParameter ¶m); + +} // namespace Phonon + +#if defined(Q_CC_MSVC) && _MSC_VER <= 1300 +//this ensures that code outside Phonon can use the hash function +//it also a workaround for some compilers +inline uint qHash(const Phonon::EffectParameter ¶m) { return Phonon::qHash(param); } //krazy:exclude=inline +#endif +Q_DECLARE_OPERATORS_FOR_FLAGS(Phonon::EffectParameter::Hints) + +#endif //QT_NO_PHONON_EFFECT + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // PHONON_EFFECTPARAMETER_H +// vim: sw=4 ts=4 tw=80 diff --git a/src/3rdparty/phonon/phonon/effectparameter_p.h b/src/3rdparty/phonon/phonon/effectparameter_p.h new file mode 100644 index 0000000..0fc387a --- /dev/null +++ b/src/3rdparty/phonon/phonon/effectparameter_p.h @@ -0,0 +1,56 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef EFFECTPARAMETER_P_H +#define EFFECTPARAMETER_P_H + +#include "effectparameter.h" +#include <QtCore/QSharedData> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_EFFECT + +namespace Phonon +{ + +class EffectParameterPrivate : public QSharedData +{ + public: + int parameterId; + QVariant min; + QVariant max; + QVariant defaultValue; + QString name; + QString description; + QVariantList possibleValues; + EffectParameter::Hints hints; +}; + +} // namespace Phonon + +#endif //QT_NO_PHONON_EFFECT + +QT_END_NAMESPACE + +#endif // EFFECTPARAMETER_P_H +// vim: sw=4 ts=4 tw=80 diff --git a/src/3rdparty/phonon/phonon/effectwidget.cpp b/src/3rdparty/phonon/phonon/effectwidget.cpp new file mode 100644 index 0000000..da5a51a --- /dev/null +++ b/src/3rdparty/phonon/phonon/effectwidget.cpp @@ -0,0 +1,254 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#include "effectwidget.h" +#include "effectwidget_p.h" + +#include <QtCore/QtAlgorithms> +#include <QtCore/QList> + +#include "effect.h" +#include "effectparameter.h" +#include "phonondefs_p.h" +#include <QtGui/QBoxLayout> +#include <QtGui/QLabel> +#include <QtGui/QSpinBox> +#include <QtGui/QCheckBox> +#include <QtGui/QComboBox> +#include <QtGui/QSlider> +#include <limits> + +#ifdef min +#undef min +#endif +#ifdef max +#undef max +#endif +static const qreal DEFAULT_MIN = std::numeric_limits<qreal>::min(); +static const qreal DEFAULT_MAX = std::numeric_limits<qreal>::max(); +static const int DEFAULT_MIN_INT = std::numeric_limits<int>::min(); +static const int DEFAULT_MAX_INT = std::numeric_limits<int>::max(); +static const int SLIDER_RANGE = 8; +static const int TICKINTERVAL = 4; + + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_EFFECTWIDGET + +namespace Phonon +{ + +EffectWidget::EffectWidget(Effect *effect, QWidget *parent) + : QWidget(parent), + k_ptr(new EffectWidgetPrivate(effect)) +{ + K_D(EffectWidget); + d->q_ptr = this; + d->autogenerateUi(); +} + +EffectWidget::~EffectWidget() +{ + delete k_ptr; +} + +/* +EffectWidget::EffectWidget(EffectWidgetPrivate &dd, QWidget *parent) + : QWidget(parent) + , k_ptr(&dd) +{ + K_D(EffectWidget); + d->q_ptr = this; + d->autogenerateUi(); +} +*/ + +EffectWidgetPrivate::EffectWidgetPrivate(Effect *e) + : effect(e) +{ + //TODO: look up whether there is a specialized widget for this effect. This + //could be a DSO or a Designer ui file found via KTrader. + // + //if no specialized widget is available: +} + +void EffectWidgetPrivate::autogenerateUi() +{ + Q_Q(EffectWidget); + QVBoxLayout *mainLayout = new QVBoxLayout(q); + mainLayout->setMargin(0); + foreach (const EffectParameter ¶, effect->parameters()) { + QVariant value = effect->parameterValue(para); + QHBoxLayout *pLayout = new QHBoxLayout; + mainLayout->addLayout(pLayout); + + QLabel *label = new QLabel(q); + pLayout->addWidget(label); + label->setText(para.name()); +#ifndef QT_NO_TOOLTIP + label->setToolTip(para.description()); +#endif + + QWidget *control = 0; + switch (para.type()) { + case QVariant::String: + { + QComboBox *cb = new QComboBox(q); + control = cb; + if (value.type() == QVariant::Int) { + //value just defines the item index + foreach (const QVariant &item, para.possibleValues()) { + cb->addItem(item.toString()); + } + cb->setCurrentIndex(value.toInt()); + QObject::connect(cb, SIGNAL(currentIndexChanged(int)), q, SLOT(_k_setIntParameter(int))); + } else { + foreach (const QVariant &item, para.possibleValues()) { + cb->addItem(item.toString()); + if (item == value) { + cb->setCurrentIndex(cb->count() - 1); + } + } + QObject::connect(cb, SIGNAL(currentIndexChanged(QString)), q, SLOT(_k_setStringParameter(QString))); + } + } + break; + case QVariant::Bool: + { + QCheckBox *cb = new QCheckBox(q); + control = cb; + cb->setChecked(value.toBool()); + QObject::connect(cb, SIGNAL(toggled(bool)), q, SLOT(_k_setToggleParameter(bool))); + } + break; + case QVariant::Int: + { + QSpinBox *sb = new QSpinBox(q); + control = sb; + bool minValueOk = false; + bool maxValueOk = false; + const int minValue = para.minimumValue().toInt(&minValueOk); + const int maxValue = para.minimumValue().toInt(&maxValueOk); + + sb->setRange(minValueOk ? minValue : DEFAULT_MIN_INT, maxValueOk ? maxValue : DEFAULT_MAX_INT); + sb->setValue(value.toInt()); + QObject::connect(sb, SIGNAL(valueChanged(int)), q, SLOT(_k_setIntParameter(int))); + } + break; + case QVariant::Double: + { + const double minValue = (para.minimumValue().type() == QVariant::Double ? + para.minimumValue().toDouble() : DEFAULT_MIN); + const double maxValue = (para.maximumValue().type() == QVariant::Double ? + para.maximumValue().toDouble() : DEFAULT_MAX); + + if (minValue == -1. && maxValue == 1.) { + //Special case values between -1 and 1.0 to use a slider for improved usability + QSlider *slider = new QSlider(Qt::Horizontal, q); + slider->setRange(-SLIDER_RANGE, +SLIDER_RANGE); + slider->setValue(int(SLIDER_RANGE * value.toDouble())); + slider->setTickPosition(QSlider::TicksBelow); + slider->setTickInterval(TICKINTERVAL); + QObject::connect(slider, SIGNAL(valueChanged(int)), q, SLOT(_k_setSliderParameter(int))); + } else { + double step = 0.1; + if (qAbs(maxValue - minValue) > 50) + step = 1.0; + QDoubleSpinBox *sb = new QDoubleSpinBox(q); + control = sb; + sb->setRange(minValue, maxValue); + sb->setValue(value.toDouble()); + sb->setSingleStep(step); + QObject::connect(sb, SIGNAL(valueChanged(double)), q, + SLOT(_k_setDoubleParameter(double))); + } + } + break; + default: + break; + } + +#ifndef QT_NO_TOOLTIP + control->setToolTip(para.description()); +#endif + if (control) { +#ifndef QT_NO_SHORTCUT + label->setBuddy(control); +#endif + pLayout->addWidget(control); + parameterForObject.insert(control, para); + } + } +} + +void EffectWidgetPrivate::_k_setToggleParameter(bool checked) +{ + Q_Q(EffectWidget); + if (parameterForObject.contains(q->sender())) { + effect->setParameterValue(parameterForObject[q->sender()], checked); + } +} + +void EffectWidgetPrivate::_k_setIntParameter(int value) +{ + Q_Q(EffectWidget); + if (parameterForObject.contains(q->sender())) { + effect->setParameterValue(parameterForObject[q->sender()], value); + } +} + +void EffectWidgetPrivate::_k_setDoubleParameter(double value) +{ + Q_Q(EffectWidget); + if (parameterForObject.contains(q->sender())) { + effect->setParameterValue(parameterForObject[q->sender()], value); + } +} + +void EffectWidgetPrivate::_k_setStringParameter(const QString &value) +{ + Q_Q(EffectWidget); + if (parameterForObject.contains(q->sender())) { + effect->setParameterValue(parameterForObject[q->sender()], value); + } +} + +void EffectWidgetPrivate::_k_setSliderParameter(int value) +{ + Q_Q(EffectWidget); + if (parameterForObject.contains(q->sender())) { + effect->setParameterValue(parameterForObject[q->sender()], double(value) / double(SLIDER_RANGE)); + } +} + + +} // namespace Phonon + + +#endif // QT_NO_PHONON_EFFECTWIDGET + +QT_END_NAMESPACE + +#include "moc_effectwidget.cpp" + +// vim: sw=4 ts=4 diff --git a/src/3rdparty/phonon/phonon/effectwidget.h b/src/3rdparty/phonon/phonon/effectwidget.h new file mode 100644 index 0000000..340b2e3 --- /dev/null +++ b/src/3rdparty/phonon/phonon/effectwidget.h @@ -0,0 +1,76 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_UI_EFFECTWIDGET_H +#define PHONON_UI_EFFECTWIDGET_H + +#include "phonon_export.h" +#include "phonondefs.h" +#include <QtGui/QWidget> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_EFFECTWIDGET + +namespace Phonon +{ +class Effect; + + class EffectWidgetPrivate; + + /** \class EffectWidget effectwidget.h Phonon/EffectWidget + * \brief Widget to control the parameters of an \ref Effect. + * + * \ingroup PhononWidgets + * \ingroup PhononEffects + * \author Matthias Kretz <kretz@kde.org> + */ + class PHONON_EXPORT EffectWidget : public QWidget + { + Q_OBJECT + K_DECLARE_PRIVATE(EffectWidget) + public: + explicit EffectWidget(Effect *effect, QWidget *parent = 0); + ~EffectWidget(); + + protected: + //EffectWidget(EffectWidgetPrivate &dd, QWidget *parent); + EffectWidgetPrivate *const k_ptr; + + private: + Q_PRIVATE_SLOT(k_func(), void _k_setToggleParameter(bool checked)) + Q_PRIVATE_SLOT(k_func(), void _k_setIntParameter(int value)) + Q_PRIVATE_SLOT(k_func(), void _k_setDoubleParameter(double value)) + Q_PRIVATE_SLOT(k_func(), void _k_setStringParameter(const QString &)) + Q_PRIVATE_SLOT(k_func(), void _k_setSliderParameter(int)) + }; +} // namespace Phonon + +#endif //QT_NO_PHONON_EFFECTWIDGET + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // PHONON_UI_EFFECTWIDGET_H + +// vim: sw=4 ts=4 tw=100 diff --git a/src/3rdparty/phonon/phonon/effectwidget_p.h b/src/3rdparty/phonon/phonon/effectwidget_p.h new file mode 100644 index 0000000..6ce44bf --- /dev/null +++ b/src/3rdparty/phonon/phonon/effectwidget_p.h @@ -0,0 +1,64 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_UI_EFFECTWIDGET_P_H +#define PHONON_UI_EFFECTWIDGET_P_H + +#include "effectwidget.h" +#include "effectparameter.h" +#include <QtCore/QHash> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_EFFECTWIDGET + +namespace Phonon +{ + class EffectWidgetPrivate + { + Q_DECLARE_PUBLIC(EffectWidget) + protected: + EffectWidgetPrivate(Effect *effect); + + EffectWidget *q_ptr; + + private: + Effect *effect; + QHash<QObject *, EffectParameter> parameterForObject; + + void _k_setToggleParameter(bool checked); + void _k_setIntParameter(int value); + void _k_setDoubleParameter(double value); + void _k_setStringParameter(const QString &); + void _k_setSliderParameter(int); + + void autogenerateUi(); + }; +} // namespace Phonon + +#endif //QT_NO_PHONON_EFFECTWIDGET + +QT_END_NAMESPACE + +#endif // PHONON_UI_EFFECTWIDGET_P_H + +// vim: sw=4 ts=4 tw=80 diff --git a/src/3rdparty/phonon/phonon/extractmethodcalls.rb b/src/3rdparty/phonon/phonon/extractmethodcalls.rb new file mode 100755 index 0000000..9100489 --- /dev/null +++ b/src/3rdparty/phonon/phonon/extractmethodcalls.rb @@ -0,0 +1,527 @@ +#!/usr/bin/ruby + +class MethodDef + def initialize(returnType, signature, optional) + @returnType = returnType + @signature = signature + @optional = optional + end + + attr_reader :returnType, :signature, :optional + attr_writer :optional +end + +class SignalDef + def initialize(signature) + @signature = signature + end + + attr_reader :signature +end + +class Parser + def initialize(filename) + @file = File.new filename, "r" + @signatures = Hash.new + parse + end + + attr_reader :signatures + + private + def addSignal(signature) + unless @signatures.include? signature + @signatures[signature] = SignalDef.new(signature) + end + end + + def addMethod(returnType, signature, optional) + if @signatures.include? signature + if returnType != '' + if @signatures[signature].returnType == '' + optional = false if @signatures[signature].optional == false + @signatures[signature] = MethodDef.new(returnType, signature, optional) + elsif @signatures[signature].returnType != returnType + fail "same signature '#{signature}' but differing return types: #{returnType} and #{@signatures[signature].returnType}" + end + elsif not optional and @signatures[signature].optional + @signatures[signature].optional = false + end + else + @signatures[signature] = MethodDef.new(returnType, signature, optional) + end + end + + PARSER_RETURN_TYPE = 0 + PARSER_RETURN_VAR = 1 + PARSER_METHOD_NAME = 2 + PARSER_ARGUMENT_TYPE = 3 + PARSER_ARGUMENT_VAR = 4 + + PARSER2_CLASSNAME = 0 + PARSER2_METHODPREFIX = 1 + + PARSER3_QMETAOBJECT = 0 + PARSER3_SCOPEDELIMIT = 1 + PARSER3_INVOKEMETHOD = 2 + PARSER3_OPENPARENTH = 3 + PARSER3_BACKENDOBJ = 4 + PARSER3_METHODNAME = 5 + PARSER3_CONNECTION = 6 + PARSER3_RET_OR_ARG = 7 + PARSER3_RET_OPEN = 8 + PARSER3_RET_TYPE = 9 + PARSER3_RET_NAME = 10 + PARSER3_RET_CLOSE = 11 + PARSER3_ARG = 12 + PARSER3_ARG_OPEN = 13 + PARSER3_ARG_TYPE = 14 + PARSER3_ARG_NAME = 15 + PARSER3_ARG_CLOSE = 16 + PARSER3_CLOSE = 17 + PARSER3_DONE = 18 + + PARSER4_OPENING_PAREN = 0 + PARSER4_SENDER = 1 + PARSER4_PRIVATE_SENDER = 2 + PARSER4_COMMA_1 = 3 + PARSER4_SIGNAL_MACRO = 4 + PARSER4_SIGNAL_OPENING_PAREN = 5 + PARSER4_SIGNAL_SIGNATURE = 6 + PARSER4_SIGNAL_SIGNATURE_OPENING_PAREN = 7 + PARSER4_SIGNAL_SIGNATURE_CONST = 8 + PARSER4_SIGNAL_SIGNATURE_TYPE1 = 9 + PARSER4_SIGNAL_SIGNATURE_TYPE2_1 = 10 + PARSER4_SIGNAL_SIGNATURE_TYPE2_2 = 11 + PARSER4_SIGNAL_CLOSING_PAREN = 12 + + def parse + inbackendcall = false + innamedescriptioncall = false + ininvokemethodcall = false + invokemethodcallOnBackendObject = false + inconnect = false + optionalmethod = false + lasttoken = ';' + thistoken = ';' + returnType = String.new + signature = String.new + parserstate = PARSER_RETURN_TYPE + depth = 0 + tokenize do |token| + #STDERR.puts token + lasttoken = thistoken + thistoken = token + token = token[1..-1] if token[0,9] == "pBACKEND_" + if token[0,8] == "BACKEND_" + fail if innamedescriptioncall + fail if inbackendcall + fail if ininvokemethodcall + fail if inconnect + inbackendcall = true + if token[8,3] != "GET" # skip return arg + parserstate = PARSER_METHOD_NAME + returnType = '' + else + parserstate = PARSER_RETURN_TYPE + end + elsif token == 'NAMEDESCRIPTIONFROMINDEX' + fail if innamedescriptioncall + fail if inbackendcall + fail if ininvokemethodcall + fail if inconnect + innamedescriptioncall = true + parserstate = PARSER2_CLASSNAME + elsif token == 'QMetaObject' + fail if innamedescriptioncall + fail if inbackendcall + fail if ininvokemethodcall + fail if inconnect + ininvokemethodcall = true + parserstate = PARSER3_SCOPEDELIMIT + optionalmethod = (lasttoken[-1,1] == '=' or lasttoken == '(' or lasttoken == '!') ? true : false + elsif token == 'connect' + fail if innamedescriptioncall + fail if inbackendcall + fail if ininvokemethodcall + fail if inconnect + inconnect = true + parserstate = PARSER4_OPENING_PAREN + elsif inconnect + #puts "state = #{parserstate}, token = #{token}" + lastparserstate = parserstate + case parserstate + when PARSER4_OPENING_PAREN + parserstate = PARSER4_SENDER if token == '(' + when PARSER4_SENDER + # d->m_backendObject or only m_backendObject + parserstate = PARSER4_COMMA_1 if token == 'm_backendObject' + parserstate = PARSER4_PRIVATE_SENDER if token == 'd' + when PARSER4_PRIVATE_SENDER + parserstate = PARSER4_SENDER if token == '->' + when PARSER4_COMMA_1 + parserstate = PARSER4_SIGNAL_MACRO if token == ',' + when PARSER4_SIGNAL_MACRO + parserstate = PARSER4_SIGNAL_OPENING_PAREN if token == 'SIGNAL' + when PARSER4_SIGNAL_OPENING_PAREN + parserstate = PARSER4_SIGNAL_SIGNATURE if token == '(' + when PARSER4_SIGNAL_SIGNATURE + signature = token + parserstate = PARSER4_SIGNAL_SIGNATURE_OPENING_PAREN + when PARSER4_SIGNAL_SIGNATURE_OPENING_PAREN + case token + when '(' + signature += '(' + parserstate = PARSER4_SIGNAL_SIGNATURE_CONST + when '()' + signature += '()' + parserstate = PARSER4_SIGNAL_CLOSING_PAREN + end + when PARSER4_SIGNAL_SIGNATURE_CONST + case token + when 'const' + signature += 'const ' + parserstate = PARSER4_SIGNAL_SIGNATURE_TYPE1 + when ')' + signature += ')' + parserstate = PARSER4_SIGNAL_CLOSING_PAREN + else + signature += token + parserstate = PARSER4_SIGNAL_SIGNATURE_TYPE2_1 + end + when PARSER4_SIGNAL_SIGNATURE_TYPE1 + case token + when 'const' + when ')' + else + signature += token + parserstate = PARSER4_SIGNAL_SIGNATURE_TYPE2_1 + end + when PARSER4_SIGNAL_SIGNATURE_TYPE2_1 + case token + when ',' + signature += ', ' + parserstate = PARSER4_SIGNAL_SIGNATURE_TYPE1 + when ')' + signature += ')' + parserstate = PARSER4_SIGNAL_CLOSING_PAREN + else + signature += token + parserstate = PARSER4_SIGNAL_SIGNATURE_TYPE2_2 + end + when PARSER4_SIGNAL_SIGNATURE_TYPE2_2 + case token + when ',' + signature += ', ' + parserstate = PARSER4_SIGNAL_SIGNATURE_TYPE1 + when ')' + signature += ')' + parserstate = PARSER4_SIGNAL_CLOSING_PAREN + else + signature += token + parserstate = PARSER4_SIGNAL_SIGNATURE_TYPE2_1 + end + when PARSER4_SIGNAL_CLOSING_PAREN + addSignal(signature) if token == ')' + end + if parserstate == lastparserstate + inconnect = false + signature = String.new + end + elsif ininvokemethodcall + case parserstate + when PARSER3_BACKENDOBJ + if token == ',' + if invokemethodcallOnBackendObject + parserstate += 1 + else + ininvokemethodcall = false + end + elsif token =~ /backendObject/ + invokemethodcallOnBackendObject = true + end + when PARSER3_SCOPEDELIMIT + if token == '::' + parserstate += 1 + else + ininvokemethodcall = false + end + when PARSER3_INVOKEMETHOD + if token == 'invokeMethod' + parserstate += 1 + else + ininvokemethodcall = false + end + when PARSER3_OPENPARENTH + fail if token != '(' + parserstate += 1 + invokemethodcallOnBackendObject = false + when PARSER3_METHODNAME + case token + when ',' + signature += '(' + parserstate = PARSER3_CONNECTION + else + fail if signature.length > 0 + signature = token[1..-2] + end + when PARSER3_CONNECTION + case token + when ',' + parserstate = PARSER3_RET_OR_ARG + when ')' + parserstate = PARSER3_CLOSE +# the connection is optional + when 'Q_RETURN_ARG' + parserstate = PARSER3_RET_OPEN + when 'Q_ARG' + returnType = '' + parserstate = PARSER3_ARG_OPEN + end + when PARSER3_RET_OR_ARG + if token == 'Q_RETURN_ARG' + parserstate = PARSER3_RET_OPEN + elsif token == 'Q_ARG' + returnType = '' + parserstate = PARSER3_ARG_OPEN + else + fail "unexpected token '#{token}" + end + when PARSER3_RET_TYPE + if token == ',' + parserstate += 1 + else + if token == '*' or token == '&' or token == '<' or token == '>' or token == '::' or returnType.empty? or returnType[-1,1] == '<' or returnType[-2,2] == '::' + returnType += token + else + returnType += ' ' + token + end + end + when PARSER3_RET_NAME + parserstate = PARSER3_RET_CLOSE if token == ')' + when PARSER3_RET_CLOSE + case token + when ')' + parserstate = PARSER3_CLOSE + when ',' + parserstate = PARSER3_ARG + end + when PARSER3_ARG + if token == 'Q_ARG' + parserstate = PARSER3_ARG_OPEN + else + fail "unexpected token '#{token}" + end + when PARSER3_ARG_TYPE + if token == ',' + parserstate += 1 + else + signature += ' ' if signature[-1,1] =~ /\w/ and token[0,1] =~ /\w/ + signature += token + end + when PARSER3_ARG_NAME + case token + when '(' + depth += 1 + when ')' + if depth == 0 + parserstate = PARSER3_ARG_CLOSE + else + depth -= 1 + end + end + when PARSER3_ARG_CLOSE + case token + when ',' + signature += ',' + parserstate = PARSER3_ARG + when ')' + parserstate = PARSER3_CLOSE + else + fail + end + when PARSER3_ARG_OPEN, PARSER3_RET_OPEN + fail if token != '(' + parserstate += 1 + when PARSER3_CLOSE + signature += ')' + addMethod returnType, signature, optionalmethod + ininvokemethodcall = false + returnType = String.new + signature = String.new + end + elsif innamedescriptioncall + case parserstate + when PARSER2_CLASSNAME + parserstate = PARSER2_METHODPREFIX if token == ',' + when PARSER2_METHODPREFIX + addMethod 'QSet<int>', token + 'Indexes()', false + addMethod 'int', token + 'Name()', false + addMethod 'int', token + 'Description()', false + innamedescriptioncall = false + end + elsif inbackendcall + next if token == '(' # skip ( + if token == ';' + if signature.length > 0 + signature += ')' + addMethod returnType, signature, false + signature = String.new + returnType = String.new + end + inbackendcall = false + else + if token == ',' + if parserstate == PARSER_ARGUMENT_VAR + signature += ',' + parserstate = PARSER_ARGUMENT_TYPE + else + parserstate += 1 + end + next + end + case parserstate + when PARSER_RETURN_TYPE + returnType += token + when PARSER_RETURN_VAR + when PARSER_METHOD_NAME + next if token == ')' + fail if token[0,1] != '"' or token[-1,1] != '"' + fail if signature.length > 0 + signature = token[1..-2] + '(' + when PARSER_ARGUMENT_TYPE + signature += token + end + end + end + end + end + + def tokenize + incomment = false + instring = false + laststring = '' + linenum = 0 + @file.each_line do |line| + linenum += 1 + line.strip! + next if line[0..1] == "//" + next if line[0,1] == "#" # ignore preprocessor statements + line.split(/(\b|\s+)/).each do |token| + #STDERR.puts "string: #{instring} comment: #{incomment} token: '#{token}'" + if instring + indexOfEscapedQuote = token.index '\\"' + if indexOfEscapedQuote != nil + laststring += token + next + end + indexOfQuote = token.index '"' + if indexOfQuote and indexOfQuote > 0 + fail if token[indexOfQuote-1,1] == '\\' + laststring += token[0..indexOfQuote-1] + token = token[indexOfQuote..-1] + end + if token[0,2] == '""' + laststring += token[2..-1] + next + elsif token[0,1] == '"' + if laststring[-1,1] == '\\' + laststring[-1,1] = '"' + laststring += token[1..-1] + next + end + instring = false + yield laststring + '"' + token = token[1..-1] + else + laststring += token + next + end + end + token.strip! + next if token.empty? + if incomment + incomment = false if token[0..1] == "*/" + next + else + if token[0..1] == "/*" + incomment = true + next + end + break if token == "//" + end + doublequote = token.index '""' + if doublequote != nil + if doublequote > 0 + yield token[0,doublequote] + end + yield '""' + if token.length > doublequote+2 + token = token[doublequote+2..-1] + else + next + end + end + quote = token.index '"' + if quote != nil + laststring = token[quote..-1] + instring = true + if quote > 0 + token = token[0,quote] + else + next + end + end + semicolon = token.index ';' + if not semicolon + tokenize2(token) { |i| yield i } + elsif semicolon > 0 + tokenize2(token[0..semicolon-1]) { |i| yield i } + yield ';' + if token.length > semicolon + 1 + tokenize2(token[semicolon+1..-1]) { |i| yield i } + end + elsif (semicolon == 0 and token.length > 1) + yield ';' + tokenize2(token[1..-1]) { |i| yield i } + else + yield token # a single ; + end + end + end + end + + def tokenize2(token) + if token.length > 1 + #STDERR.puts "long token: #{token}" + while token[0,1] == '(' or token[0,1] == ')' or token[0,1] == "'" or token[0,1] == '&' + yield token[0,1] + token = token[1..-1] + #STDERR.puts "less long token: #{token}" + end + return if token.empty? + if token.length == 1 + yield token + return + elsif token[-1,1] == ',' or token[-1,1] == "'" + yield token[0..-2] + yield token[-1,1] + return + end + end + yield token + end +end + +p = Parser.new ARGV[0] +p.signatures.each do |signature,method| + if method.class == SignalDef + puts "addSignal(\"#{signature}\");" + else + if method.optional + puts "addMethod(\"#{method.returnType}\", \"#{signature}\", true);" + else + puts "addMethod(\"#{method.returnType}\", \"#{signature}\");" + end + end +end diff --git a/src/3rdparty/phonon/phonon/factory.cpp b/src/3rdparty/phonon/phonon/factory.cpp new file mode 100644 index 0000000..43c45ee --- /dev/null +++ b/src/3rdparty/phonon/phonon/factory.cpp @@ -0,0 +1,457 @@ +/* This file is part of the KDE project + Copyright (C) 2004-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#include "factory_p.h" + +#include "backendinterface.h" +#include "medianode_p.h" +#include "mediaobject.h" +#include "audiooutput.h" +#include "globalstatic_p.h" +#include "objectdescription.h" +#include "platformplugin.h" +#include "phononnamespace_p.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QDir> +#include <QtCore/QList> +#include <QtCore/QPluginLoader> +#include <QtCore/QPointer> +#ifndef QT_NO_DBUS +#include <QtDBus/QtDBus> +#endif + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + +class PlatformPlugin; +class FactoryPrivate : public Phonon::Factory::Sender +{ + friend QObject *Factory::backend(bool); + Q_OBJECT + public: + FactoryPrivate(); + ~FactoryPrivate(); + bool createBackend(); +#ifndef QT_NO_PHONON_PLATFORMPLUGIN + PlatformPlugin *platformPlugin(); + + PlatformPlugin *m_platformPlugin; + bool m_noPlatformPlugin; +#endif //QT_NO_PHONON_PLATFORMPLUGIN + QPointer<QObject> m_backendObject; + + QList<QObject *> objects; + QList<MediaNodePrivate *> mediaNodePrivateList; + + private Q_SLOTS: + /** + * This is called via DBUS when the user changes the Phonon Backend. + */ +#ifndef QT_NO_DBUS + void phononBackendChanged(); +#endif //QT_NO_DBUS + + /** + * unregisters the backend object + */ + void objectDestroyed(QObject *); + + void objectDescriptionChanged(ObjectDescriptionType); +}; + +PHONON_GLOBAL_STATIC(Phonon::FactoryPrivate, globalFactory) + +static inline void ensureLibraryPathSet() +{ +#ifdef PHONON_LIBRARY_PATH + static bool done = false; + if (!done) { + done = true; + QCoreApplication::addLibraryPath(QLatin1String(PHONON_LIBRARY_PATH)); + } +#endif // PHONON_LIBRARY_PATH +} + +void Factory::setBackend(QObject *b) +{ + Q_ASSERT(globalFactory->m_backendObject == 0); + globalFactory->m_backendObject = b; +} + +/*void Factory::createBackend(const QString &library, const QString &version) +{ + Q_ASSERT(globalFactory->m_backendObject == 0); + PlatformPlugin *f = globalFactory->platformPlugin(); + if (f) { + globalFactory->m_backendObject = f->createBackend(library, version); + } +}*/ + +bool FactoryPrivate::createBackend() +{ + Q_ASSERT(m_backendObject == 0); +#ifndef QT_NO_PHONON_PLATFORMPLUGIN + PlatformPlugin *f = globalFactory->platformPlugin(); + if (f) { + m_backendObject = f->createBackend(); + } +#endif //QT_NO_PHONON_PLATFORMPLUGIN + if (!m_backendObject) { + ensureLibraryPathSet(); + + // could not load a backend through the platform plugin. Falling back to the default + // (finding the first loadable backend). + const QLatin1String suffix("/phonon_backend/"); + foreach (QString libPath, QCoreApplication::libraryPaths()) { + libPath += suffix; + const QDir dir(libPath); + if (!dir.exists()) { + pDebug() << Q_FUNC_INFO << dir.absolutePath() << "does not exist"; + continue; + } + foreach (const QString &pluginName, dir.entryList(QDir::Files)) { + QPluginLoader pluginLoader(libPath + pluginName); + if (!pluginLoader.load()) { + pDebug() << Q_FUNC_INFO << " load failed:" + << pluginLoader.errorString(); + continue; + } + pDebug() << pluginLoader.instance(); + m_backendObject = pluginLoader.instance(); + if (m_backendObject) { + break; + } + + // no backend found, don't leave an unused plugin in memory + pluginLoader.unload(); + } + + if (m_backendObject) { + break; + } + } + if (!m_backendObject) { + pWarning() << Q_FUNC_INFO << "phonon backend plugin could not be loaded"; + return false; + } + } + + connect(m_backendObject, SIGNAL(objectDescriptionChanged(ObjectDescriptionType)), + SLOT(objectDescriptionChanged(ObjectDescriptionType))); + + return true; +} + +FactoryPrivate::FactoryPrivate() +#ifndef QT_NO_PHONON_PLATFORMPLUGIN + : m_platformPlugin(0), + m_noPlatformPlugin(false) +#endif //QT_NO_PHONON_PLATFORMPLUGIN + , m_backendObject(0) +{ + // Add the post routine to make sure that all other global statics (especially the ones from Qt) + // are still available. If the FactoryPrivate dtor is called too late many bad things can happen + // as the whole backend might still be alive. + qAddPostRoutine(globalFactory.destroy); +#ifndef QT_NO_DBUS + QDBusConnection::sessionBus().connect(QString(), QString(), QLatin1String("org.kde.Phonon.Factory"), + QLatin1String("phononBackendChanged"), this, SLOT(phononBackendChanged())); +#endif +} + +FactoryPrivate::~FactoryPrivate() +{ + foreach (QObject *o, objects) { + MediaObject *m = qobject_cast<MediaObject *>(o); + if (m) { + m->stop(); + } + } + foreach (MediaNodePrivate *bp, mediaNodePrivateList) { + bp->deleteBackendObject(); + } + if (objects.size() > 0) { + pError() << "The backend objects are not deleted as was requested."; + qDeleteAll(objects); + } + delete m_backendObject; +#ifndef QT_NO_PHONON_PLATFORMPLUGIN + delete m_platformPlugin; +#endif //QT_NO_PHONON_PLATFORMPLUGIN +} + +void FactoryPrivate::objectDescriptionChanged(ObjectDescriptionType type) +{ +#ifdef PHONON_METHODTEST + Q_UNUSED(type); +#else + pDebug() << Q_FUNC_INFO << type; + switch (type) { + case AudioOutputDeviceType: + emit availableAudioOutputDevicesChanged(); + break; + case AudioCaptureDeviceType: + emit availableAudioCaptureDevicesChanged(); + break; + default: + break; + } + //emit capabilitiesChanged(); +#endif // PHONON_METHODTEST +} + +Factory::Sender *Factory::sender() +{ + return globalFactory; +} + +bool Factory::isMimeTypeAvailable(const QString &mimeType) +{ +#ifndef QT_NO_PHONON_PLATFORMPLUGIN + PlatformPlugin *f = globalFactory->platformPlugin(); + if (f) { + return f->isMimeTypeAvailable(mimeType); + } +#else + Q_UNUSED(mimeType); +#endif //QT_NO_PHONON_PLATFORMPLUGIN + return true; // the MIME type might be supported, let BackendCapabilities find out +} + +void Factory::registerFrontendObject(MediaNodePrivate *bp) +{ + globalFactory->mediaNodePrivateList.prepend(bp); // inserted last => deleted first +} + +void Factory::deregisterFrontendObject(MediaNodePrivate *bp) +{ + // The Factory can already be cleaned up while there are other frontend objects still alive. + // When those are deleted they'll call deregisterFrontendObject through ~BasePrivate + if (!globalFactory.isDestroyed()) { + globalFactory->mediaNodePrivateList.removeAll(bp); + } +} + +#ifndef QT_NO_DBUS +void FactoryPrivate::phononBackendChanged() +{ + if (m_backendObject) { + foreach (MediaNodePrivate *bp, mediaNodePrivateList) { + bp->deleteBackendObject(); + } + if (objects.size() > 0) { + pDebug() << "WARNING: we were asked to change the backend but the application did\n" + "not free all references to objects created by the factory. Therefore we can not\n" + "change the backend without crashing. Now we have to wait for a restart to make\n" + "backendswitching possible."; + // in case there were objects deleted give 'em a chance to recreate + // them now + foreach (MediaNodePrivate *bp, mediaNodePrivateList) { + bp->createBackendObject(); + } + return; + } + delete m_backendObject; + m_backendObject = 0; + } + createBackend(); + foreach (MediaNodePrivate *bp, mediaNodePrivateList) { + bp->createBackendObject(); + } + emit backendChanged(); +} +#endif //QT_NO_DBUS + +//X void Factory::freeSoundcardDevices() +//X { +//X if (globalFactory->backend) { +//X globalFactory->backend->freeSoundcardDevices(); +//X } +//X } + +void FactoryPrivate::objectDestroyed(QObject * obj) +{ + //pDebug() << Q_FUNC_INFO << obj; + objects.removeAll(obj); +} + +#define FACTORY_IMPL(classname) \ +QObject *Factory::create ## classname(QObject *parent) \ +{ \ + if (backend()) { \ + return registerQObject(qobject_cast<BackendInterface *>(backend())->createObject(BackendInterface::classname##Class, parent)); \ + } \ + return 0; \ +} +#define FACTORY_IMPL_1ARG(classname) \ +QObject *Factory::create ## classname(int arg1, QObject *parent) \ +{ \ + if (backend()) { \ + return registerQObject(qobject_cast<BackendInterface *>(backend())->createObject(BackendInterface::classname##Class, parent, QList<QVariant>() << arg1)); \ + } \ + return 0; \ +} + +FACTORY_IMPL(MediaObject) +#ifndef QT_NO_PHONON_EFFECT +FACTORY_IMPL_1ARG(Effect) +#endif //QT_NO_PHONON_EFFECT +#ifndef QT_NO_PHONON_VOLUMEFADEREFFECT +FACTORY_IMPL(VolumeFaderEffect) +#endif //QT_NO_PHONON_VOLUMEFADEREFFECT +FACTORY_IMPL(AudioOutput) +#ifndef QT_NO_PHONON_VIDEO +FACTORY_IMPL(VideoWidget) +#endif //QT_NO_PHONON_VIDEO + +#undef FACTORY_IMPL + +#ifndef QT_NO_PHONON_PLATFORMPLUGIN +PlatformPlugin *FactoryPrivate::platformPlugin() +{ + if (m_platformPlugin) { + return m_platformPlugin; + } + if (m_noPlatformPlugin) { + return 0; + } +#ifndef QT_NO_DBUS + if (!QCoreApplication::instance() || QCoreApplication::applicationName().isEmpty()) { + pWarning() << "Phonon needs QCoreApplication::applicationName to be set to export audio output names through the DBUS interface"; + } +#endif + Q_ASSERT(QCoreApplication::instance()); + const QByteArray platform_plugin_env = qgetenv("PHONON_PLATFORMPLUGIN"); + if (!platform_plugin_env.isEmpty()) { + QPluginLoader pluginLoader(QString::fromLocal8Bit(platform_plugin_env.constData())); + if (pluginLoader.load()) { + m_platformPlugin = qobject_cast<PlatformPlugin *>(pluginLoader.instance()); + if (m_platformPlugin) { + return m_platformPlugin; + } + } + } + const QString suffix(QLatin1String("/phonon_platform/")); + ensureLibraryPathSet(); + QDir dir; + dir.setNameFilters( + !qgetenv("KDE_FULL_SESSION").isEmpty() ? QStringList(QLatin1String("kde.*")) : + (!qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty() ? QStringList(QLatin1String("gnome.*")) : + QStringList()) + ); + dir.setFilter(QDir::Files); + forever { + foreach (QString libPath, QCoreApplication::libraryPaths()) { + libPath += suffix; + dir.setPath(libPath); + if (!dir.exists()) { + continue; + } + foreach (const QString &pluginName, dir.entryList()) { + QPluginLoader pluginLoader(libPath + pluginName); + if (!pluginLoader.load()) { + pDebug() << Q_FUNC_INFO << " platform plugin load failed:" + << pluginLoader.errorString(); + continue; + } + pDebug() << pluginLoader.instance(); + QObject *qobj = pluginLoader.instance(); + m_platformPlugin = qobject_cast<PlatformPlugin *>(qobj); + pDebug() << m_platformPlugin; + if (m_platformPlugin) { + connect(qobj, SIGNAL(objectDescriptionChanged(ObjectDescriptionType)), + SLOT(objectDescriptionChanged(ObjectDescriptionType))); + return m_platformPlugin; + } else { + delete qobj; + pDebug() << Q_FUNC_INFO << dir.absolutePath() << "exists but the platform plugin was not loadable:" << pluginLoader.errorString(); + pluginLoader.unload(); + } + } + } + if (dir.nameFilters().isEmpty()) { + break; + } + dir.setNameFilters(QStringList()); + } + pDebug() << Q_FUNC_INFO << "platform plugin could not be loaded"; + m_noPlatformPlugin = true; + return 0; +} + +PlatformPlugin *Factory::platformPlugin() +{ + return globalFactory->platformPlugin(); +} +#endif // QT_NO_PHONON_PLATFORMPLUGIN + +QObject *Factory::backend(bool createWhenNull) +{ + if (globalFactory.isDestroyed()) { + return 0; + } + if (createWhenNull && globalFactory->m_backendObject == 0) { + globalFactory->createBackend(); + // XXX: might create "reentrancy" problems: + // a method calls this method and is called again because the + // backendChanged signal is emitted + emit globalFactory->backendChanged(); + } + return globalFactory->m_backendObject; +} + +#define GET_STRING_PROPERTY(name) \ +QString Factory::name() \ +{ \ + if (globalFactory->m_backendObject) { \ + return globalFactory->m_backendObject->property(#name).toString(); \ + } \ + return QString(); \ +} \ + +GET_STRING_PROPERTY(identifier) +GET_STRING_PROPERTY(backendName) +GET_STRING_PROPERTY(backendComment) +GET_STRING_PROPERTY(backendVersion) +GET_STRING_PROPERTY(backendIcon) +GET_STRING_PROPERTY(backendWebsite) + +QObject *Factory::registerQObject(QObject *o) +{ + if (o) { + QObject::connect(o, SIGNAL(destroyed(QObject *)), globalFactory, SLOT(objectDestroyed(QObject *)), Qt::DirectConnection); + globalFactory->objects.append(o); + } + return o; +} + +} //namespace Phonon + +QT_END_NAMESPACE + +#include "factory.moc" +#include "moc_factory_p.cpp" + +// vim: sw=4 ts=4 diff --git a/src/3rdparty/phonon/phonon/factory_p.h b/src/3rdparty/phonon/phonon/factory_p.h new file mode 100644 index 0000000..de059f8 --- /dev/null +++ b/src/3rdparty/phonon/phonon/factory_p.h @@ -0,0 +1,196 @@ +/* This file is part of the KDE project + Copyright (C) 2004-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_FACTORY_P_H +#define PHONON_FACTORY_P_H + +#include "phonon_export.h" + +#include <QtCore/QObject> +#include <QtCore/QStringList> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +class QUrl; +class QIcon; + +namespace Phonon +{ + class PlatformPlugin; + class MediaNodePrivate; + class AbstractMediaStream; + +/** + * \internal + * \brief Factory to access the preferred Backend. + * + * This class is used internally to get the backend's implementation. + * It keeps track of the objects that were created. When a + * request for a backend change comes, it asks all frontend objects to delete + * their backend objects and then checks whether they were all deleted. Only + * then the old backend is unloaded and the new backend is loaded. + * + * \author Matthias Kretz <kretz@kde.org> + */ +namespace Factory +{ + /** + * Emits signals for Phonon::Factory. + */ + class Sender : public QObject + { + Q_OBJECT + Q_SIGNALS: + /** + * Emitted after the backend has successfully been changed. + */ + void backendChanged(); + + /** + * \copydoc BackendCapabilities::Notifier::availableAudioOutputDevicesChanged + */ + void availableAudioOutputDevicesChanged(); + + /** + * \copydoc BackendCapabilities::Notifier::availableAudioCaptureDevicesChanged + */ + void availableAudioCaptureDevicesChanged(); + }; + + /** + * Returns a pointer to the object emitting the signals. + * + * \see Sender::backendChanged() + */ + PHONON_EXPORT Sender *sender(); + + /** + * Create a new backend object for a MediaObject. + * + * \return a pointer to the MediaObject the backend provides. + */ + QObject *createMediaObject(QObject *parent = 0); + /** + * Create a new backend object for a Effect. + * + * \return a pointer to the Effect the backend provides. + */ +#ifndef QT_NO_PHONON_EFFECT + QObject *createEffect(int effectId, QObject *parent = 0); +#endif //QT_NO_PHONON_EFFECT + /** + * Create a new backend object for a VolumeFaderEffect. + * + * \return a pointer to the VolumeFaderEffect the backend provides. + */ +#ifndef QT_NO_PHONON_VOLUMEFADEREFFECT + QObject *createVolumeFaderEffect(QObject *parent = 0); +#endif //QT_NO_PHONON_VOLUMEFADEREFFECT + /** + * Create a new backend object for a AudioOutput. + * + * \return a pointer to the AudioOutput the backend provides. + */ + QObject *createAudioOutput(QObject *parent = 0); + /** + * Create a new backend object for a VideoWidget. + * + * \return a pointer to the VideoWidget the backend provides. + */ +#ifndef QT_NO_PHONON_VIDEO + QObject *createVideoWidget(QObject *parent = 0); +#endif //QT_NO_PHONON_VIDEO + + /** + * \return a pointer to the backend interface. + */ + PHONON_EXPORT QObject *backend(bool createWhenNull = true); + + /** + * Unique identifier for the Backend. Can be used in configuration files + * for example. + */ + QString identifier(); + + /** + * Get the name of the Backend. It's the name from the .desktop file. + */ + PHONON_EXPORT QString backendName(); + + /** + * Get the comment of the Backend. It's the comment from the .desktop file. + */ + QString backendComment(); + + /** + * Get the version of the Backend. It's the version from the .desktop file. + * + * The version is especially interesting if there are several versions + * available for binary incompatible versions of the backend's media + * framework. + */ + QString backendVersion(); + + /** + * Get the icon (name) of the Backend. It's the icon from the .desktop file. + */ + QString backendIcon(); + + /** + * Get the website of the Backend. It's the website from the .desktop file. + */ + QString backendWebsite(); + + /** + * registers the backend object + */ + PHONON_EXPORT QObject *registerQObject(QObject *o); + + bool isMimeTypeAvailable(const QString &mimeType); + + PHONON_EXPORT void registerFrontendObject(MediaNodePrivate *); + PHONON_EXPORT void deregisterFrontendObject(MediaNodePrivate *); + + PHONON_EXPORT void setBackend(QObject *); + //PHONON_EXPORT void createBackend(const QString &library, const QString &version = QString()); + + PHONON_EXPORT PlatformPlugin *platformPlugin(); + +//X It is probably better if we can get away with internal handling of +//X freeing the soundcard device when it's not needed anymore and +//X providing an IPC method to stop all MediaObjects -> free all +//X devices +//X /** +//X * \internal +//X * This is called when the application needs to free the soundcard +//X * device(s). +//X */ +//X void freeSoundcardDevices(); +} // namespace Factory +} // namespace Phonon + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // PHONON_FACTORY_P_H +// vim: sw=4 ts=4 diff --git a/src/3rdparty/phonon/phonon/frontendinterface_p.h b/src/3rdparty/phonon/phonon/frontendinterface_p.h new file mode 100644 index 0000000..b6c76ce --- /dev/null +++ b/src/3rdparty/phonon/phonon/frontendinterface_p.h @@ -0,0 +1,68 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_FRONTENDINTERFACEPRIVATE_H +#define PHONON_FRONTENDINTERFACEPRIVATE_H + +#include "addoninterface.h" +#include "mediaobject_p.h" +#include "phononnamespace_p.h" +#include <QtCore/QPointer> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_MEDIACONTROLLER + +namespace Phonon +{ +class FrontendInterfacePrivate +{ + public: + FrontendInterfacePrivate(MediaObject *mp) : media(mp) { + Q_ASSERT(media); + MediaObjectPrivate *d = media->k_func(); + d->interfaceList << this; + } + virtual ~FrontendInterfacePrivate() { + if (media) { + MediaObjectPrivate *d = media->k_func(); + d->interfaceList << this; + } + } + virtual void backendObjectChanged(QObject *iface) = 0; + void _backendObjectChanged() { + pDebug() << Q_FUNC_INFO; + QObject *x = media->k_ptr->backendObject(); + if (x) { + backendObjectChanged(x); + } + } + AddonInterface *iface() { return qobject_cast<AddonInterface *>(media->k_ptr->backendObject()); } + QPointer<MediaObject> media; +}; +} // namespace Phonon + +#endif //QT_NO_PHONON_MEDIACONTROLLER + +QT_END_NAMESPACE + +#endif // PHONON_FRONTENDINTERFACEPRIVATE_H diff --git a/src/3rdparty/phonon/phonon/globalconfig.cpp b/src/3rdparty/phonon/phonon/globalconfig.cpp new file mode 100644 index 0000000..d13e491 --- /dev/null +++ b/src/3rdparty/phonon/phonon/globalconfig.cpp @@ -0,0 +1,243 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2008 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#include "globalconfig_p.h" + +#include "factory_p.h" +#include "objectdescription.h" +#include "phonondefs_p.h" +#include "platformplugin.h" +#include "backendinterface.h" +#include "qsettingsgroup_p.h" +#include "phononnamespace_p.h" + +#include <QtCore/QList> +#include <QtCore/QVariant> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + +GlobalConfig::GlobalConfig() : m_config(QLatin1String("kde.org"), QLatin1String("libphonon")) +{ +} + +GlobalConfig::~GlobalConfig() +{ +} + +enum WhatToFilter { + FilterAdvancedDevices = 1, + FilterHardwareDevices = 2, + FilterUnavailableDevices = 4 +}; + +static void filter(ObjectDescriptionType type, BackendInterface *backendIface, QList<int> *list, int whatToFilter) +{ + QMutableListIterator<int> it(*list); + while (it.hasNext()) { + const QHash<QByteArray, QVariant> properties = backendIface->objectDescriptionProperties(type, it.next()); + QVariant var; + if (whatToFilter & FilterAdvancedDevices) { + var = properties.value("isAdvanced"); + if (var.isValid() && var.toBool()) { + it.remove(); + continue; + } + } + if (whatToFilter & FilterHardwareDevices) { + var = properties.value("isHardwareDevice"); + if (var.isValid() && var.toBool()) { + it.remove(); + continue; + } + } + if (whatToFilter & FilterUnavailableDevices) { + var = properties.value("available"); + if (var.isValid() && !var.toBool()) { + it.remove(); + continue; + } + } + } +} + +static QList<int> listSortedByConfig(const QSettingsGroup &backendConfig, Phonon::Category category, QList<int> &defaultList) +{ + if (defaultList.size() <= 1) { + // nothing to sort + return defaultList; + } else { + // make entries unique + QSet<int> seen; + QMutableListIterator<int> it(defaultList); + while (it.hasNext()) { + if (seen.contains(it.next())) { + it.remove(); + } else { + seen.insert(it.value()); + } + } + } + + QString categoryKey = QLatin1String("Category_") + QString::number(static_cast<int>(category)); + if (!backendConfig.hasKey(categoryKey)) { + // no list in config for the given category + categoryKey = QLatin1String("Category_") + QString::number(static_cast<int>(Phonon::NoCategory)); + if (!backendConfig.hasKey(categoryKey)) { + // no list in config for NoCategory + return defaultList; + } + } + + //Now the list from m_config + QList<int> deviceList = backendConfig.value(categoryKey, QList<int>()); + + //if there are devices in m_config that the backend doesn't report, remove them from the list + QMutableListIterator<int> i(deviceList); + while (i.hasNext()) { + if (0 == defaultList.removeAll(i.next())) { + i.remove(); + } + } + + //if the backend reports more devices that are not in m_config append them to the list + deviceList += defaultList; + + return deviceList; +} + +QList<int> GlobalConfig::audioOutputDeviceListFor(Phonon::Category category, int override) const +{ + //The devices need to be stored independently for every backend + const QSettingsGroup backendConfig(&m_config, QLatin1String("AudioOutputDevice")); // + Factory::identifier()); + const QSettingsGroup generalGroup(&m_config, QLatin1String("General")); + const bool hideAdvancedDevices = ((override & AdvancedDevicesFromSettings) + ? generalGroup.value(QLatin1String("HideAdvancedDevices"), true) + : static_cast<bool>(override & HideAdvancedDevices)); + + QList<int> defaultList; +#ifndef QT_NO_PHONON_PLATFORMPLUGIN + if (PlatformPlugin *platformPlugin = Factory::platformPlugin()) { + // the platform plugin lists the audio devices for the platform + // this list already is in default order (as defined by the platform plugin) + defaultList = platformPlugin->objectDescriptionIndexes(Phonon::AudioOutputDeviceType); + if (hideAdvancedDevices) { + QMutableListIterator<int> it(defaultList); + while (it.hasNext()) { + AudioOutputDevice objDesc = AudioOutputDevice::fromIndex(it.next()); + const QVariant var = objDesc.property("isAdvanced"); + if (var.isValid() && var.toBool()) { + it.remove(); + } + } + } + } +#endif //QT_NO_PHONON_PLATFORMPLUGIN + + // lookup the available devices directly from the backend (mostly for virtual devices) + if (BackendInterface *backendIface = qobject_cast<BackendInterface *>(Factory::backend())) { + // this list already is in default order (as defined by the backend) + QList<int> list = backendIface->objectDescriptionIndexes(Phonon::AudioOutputDeviceType); + if (hideAdvancedDevices || !defaultList.isEmpty() || (override & HideUnavailableDevices)) { + filter(AudioOutputDeviceType, backendIface, &list, + (hideAdvancedDevices ? FilterAdvancedDevices : 0) + // the platform plugin already provided the hardware devices + | (defaultList.isEmpty() ? 0 : FilterHardwareDevices) + | ((override & HideUnavailableDevices) ? FilterUnavailableDevices : 0) + ); + } + defaultList += list; + } + + return listSortedByConfig(backendConfig, category, defaultList); +} + +int GlobalConfig::audioOutputDeviceFor(Phonon::Category category, int override) const +{ + QList<int> ret = audioOutputDeviceListFor(category, override); + if (ret.isEmpty()) + return -1; + return ret.first(); +} + +#ifndef QT_NO_PHONON_AUDIOCAPTURE +QList<int> GlobalConfig::audioCaptureDeviceListFor(Phonon::Category category, int override) const +{ + //The devices need to be stored independently for every backend + const QSettingsGroup backendConfig(&m_config, QLatin1String("AudioCaptureDevice")); // + Factory::identifier()); + const QSettingsGroup generalGroup(&m_config, QLatin1String("General")); + const bool hideAdvancedDevices = ((override & AdvancedDevicesFromSettings) + ? generalGroup.value(QLatin1String("HideAdvancedDevices"), true) + : static_cast<bool>(override & HideAdvancedDevices)); + + QList<int> defaultList; +#ifndef QT_NO_PHONON_PLATFORMPLUGIN + if (PlatformPlugin *platformPlugin = Factory::platformPlugin()) { + // the platform plugin lists the audio devices for the platform + // this list already is in default order (as defined by the platform plugin) + defaultList = platformPlugin->objectDescriptionIndexes(Phonon::AudioCaptureDeviceType); + if (hideAdvancedDevices) { + QMutableListIterator<int> it(defaultList); + while (it.hasNext()) { + AudioCaptureDevice objDesc = AudioCaptureDevice::fromIndex(it.next()); + const QVariant var = objDesc.property("isAdvanced"); + if (var.isValid() && var.toBool()) { + it.remove(); + } + } + } + } +#endif //QT_NO_PHONON_PLATFORMPLUGIN + + // lookup the available devices directly from the backend (mostly for virtual devices) + if (BackendInterface *backendIface = qobject_cast<BackendInterface *>(Factory::backend())) { + // this list already is in default order (as defined by the backend) + QList<int> list = backendIface->objectDescriptionIndexes(Phonon::AudioCaptureDeviceType); + if (hideAdvancedDevices || !defaultList.isEmpty() || (override & HideUnavailableDevices)) { + filter(AudioCaptureDeviceType, backendIface, &list, + (hideAdvancedDevices ? FilterAdvancedDevices : 0) + // the platform plugin already provided the hardware devices + | (defaultList.isEmpty() ? 0 : FilterHardwareDevices) + | ((override & HideUnavailableDevices) ? FilterUnavailableDevices : 0) + ); + } + defaultList += list; + } + + return listSortedByConfig(backendConfig, category, defaultList); +} + +int GlobalConfig::audioCaptureDeviceFor(Phonon::Category category, int override) const +{ + QList<int> ret = audioCaptureDeviceListFor(category, override); + if (ret.isEmpty()) + return -1; + return ret.first(); +} +#endif //QT_NO_PHONON_AUDIOCAPTURE + + +} // namespace Phonon + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/phonon/globalconfig_p.h b/src/3rdparty/phonon/phonon/globalconfig_p.h new file mode 100644 index 0000000..023858f --- /dev/null +++ b/src/3rdparty/phonon/phonon/globalconfig_p.h @@ -0,0 +1,65 @@ +/* This file is part of the KDE project +Copyright (C) 2006-2008 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_GLOBALCONFIG_P_H +#define PHONON_GLOBALCONFIG_P_H + +#include <QtCore/QSettings> + +#include "phonon_export.h" +#include "phononnamespace.h" + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + class PHONON_EXPORT GlobalConfig + { + public: + GlobalConfig(); + virtual ~GlobalConfig(); + + enum DevicesToHideFlag { + ShowUnavailableDevices = 0, + ShowAdvancedDevices = 0, + HideAdvancedDevices = 1, + AdvancedDevicesFromSettings = 2, + HideUnavailableDevices = 4 + }; + QList<int> audioOutputDeviceListFor(Phonon::Category category, int override = AdvancedDevicesFromSettings) const; + int audioOutputDeviceFor(Phonon::Category category, int override = AdvancedDevicesFromSettings) const; + +#ifndef QT_NO_PHONON_AUDIOCAPTURE + QList<int> audioCaptureDeviceListFor(Phonon::Category category, int override = AdvancedDevicesFromSettings) const; + int audioCaptureDeviceFor(Phonon::Category category, int override = AdvancedDevicesFromSettings) const; +#endif //QT_NO_PHONON_AUDIOCAPTURE + + protected: + QSettings m_config; + }; +} // namespace Phonon + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // PHONON_GLOBALCONFIG_P_H diff --git a/src/3rdparty/phonon/phonon/globalstatic_p.h b/src/3rdparty/phonon/phonon/globalstatic_p.h new file mode 100644 index 0000000..04f8395 --- /dev/null +++ b/src/3rdparty/phonon/phonon/globalstatic_p.h @@ -0,0 +1,293 @@ +/* This file is part of the KDE libraries + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_GLOBALSTATIC_P_H +#define PHONON_GLOBALSTATIC_P_H + +#include <QtCore/QAtomicPointer> + +// +// WARNING!! +// This code uses undocumented Qt API +// Do not copy it to your application! Use only the functions that are here! +// Otherwise, it could break when a new version of Qt ships. +// + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + +/** + * @internal + */ +typedef void (*CleanUpFunction)(); + +/** + * @internal + * + * Helper class for PHONON_GLOBAL_STATIC to clean up the object on library unload or application + * shutdown. + */ +class CleanUpGlobalStatic +{ + public: + CleanUpFunction func; + + inline ~CleanUpGlobalStatic() { func(); } +}; + +} // namespace Phonon + +#ifdef Q_CC_MSVC +/** + * @internal + * + * MSVC seems to give anonymous structs the same name which fails at link time. So instead we name + * the struct and hope that by adding the line number to the name it's unique enough to never clash. + */ +# define PHONON_GLOBAL_STATIC_STRUCT_NAME(NAME) _k_##NAME##__LINE__ +#else +/** + * @internal + * + * Make the struct of the PHONON_GLOBAL_STATIC anonymous. + */ +# define PHONON_GLOBAL_STATIC_STRUCT_NAME(NAME) +#endif + +/** + * This macro makes it easy to use non-POD types as global statics. + * The object is created on first use and creation is threadsafe. + * + * The object is destructed on library unload or application exit. + * Be careful with calling other objects in the destructor of the class + * as you have to be sure that they (or objects they depend on) are not already destructed. + * + * @param TYPE The type of the global static object. Do not add a *. + * @param NAME The name of the function to get a pointer to the global static object. + * + * If you have code that might be called after the global object has been destroyed you can check + * for that using the isDestroyed() function. + * + * If needed (If the destructor of the global object calls other functions that depend on other + * global statics (e.g. KConfig::sync) your destructor has to be called before those global statics + * are destroyed. A Qt post routine does that.) you can also install a post routine (@ref qAddPostRoutine) to clean up the object + * using the destroy() method. If you registered a post routine and the object is destroyed because + * of a lib unload you have to call qRemovePostRoutine! + * + * Example: + * @code + * class A { + * public: + * ~A(); + * ... + * }; + * + * PHONON_GLOBAL_STATIC(A, globalA) + * // The above creates a new globally static variable named 'globalA' which you + * // can use as a pointer to an instance of A. + * + * void doSomething() + * { + * // The first time you access globalA a new instance of A will be created automatically. + * A *a = globalA; + * ... + * } + * + * void doSomethingElse() + * { + * if (globalA.isDestroyed()) { + * return; + * } + * A *a = globalA; + * ... + * } + * + * void installPostRoutine() + * { + * // A post routine can be used to delete the object when QCoreApplication destructs, + * // not adding such a post routine will delete the object normally at program unload + * qAddPostRoutine(globalA.destroy); + * } + * + * A::~A() + * { + * // When you install a post routine you have to remove the post routine from the destructor of + * // the class used as global static! + * qRemovePostRoutine(globalA.destroy); + * } + * @endcode + * + * A common case for the need of deletion on lib unload/app shutdown are Singleton classes. Here's + * an example how to do it: + * @code + * class MySingletonPrivate; + * class EXPORT_MACRO MySingleton + * { + * friend class MySingletonPrivate; + * public: + * static MySingleton *self(); + * QString someFunction(); + * + * private: + * MySingleton(); + * ~MySingleton(); + * }; + * @endcode + * in the .cpp file: + * @code + * // This class will be instantiated and referenced as a singleton in this example + * class MySingletonPrivate + * { + * public: + * QString foo; + * MySingleton instance; + * }; + * + * PHONON_GLOBAL_STATIC(MySingletonPrivate, mySingletonPrivate) + * + * MySingleton *MySingleton::self() + * { + * // returns the singleton; automatically creates a new instance if that has not happened yet. + * return &mySingletonPrivate->instance; + * } + * QString MySingleton::someFunction() + * { + * // Refencing the singleton directly is possible for your convenience + * return mySingletonPrivate->foo; + * } + * @endcode + * + * Instead of the above you can use also the following pattern (ignore the name of the namespace): + * @code + * namespace MySingleton + * { + * EXPORT_MACRO QString someFunction(); + * } + * @endcode + * in the .cpp file: + * @code + * class MySingletonPrivate + * { + * public: + * QString foo; + * }; + * + * PHONON_GLOBAL_STATIC(MySingletonPrivate, mySingletonPrivate) + * + * QString MySingleton::someFunction() + * { + * return mySingletonPrivate->foo; + * } + * @endcode + * + * Now code that wants to call someFunction() doesn't have to do + * @code + * MySingleton::self()->someFunction(); + * @endcode + * anymore but instead: + * @code + * MySingleton::someFunction(); + * @endcode + * + * @ingroup KDEMacros + */ +#define PHONON_GLOBAL_STATIC(TYPE, NAME) PHONON_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ()) + +/** + * @overload + * This is the same as PHONON_GLOBAL_STATIC, but can take arguments that are passed + * to the object's constructor + * + * @param TYPE The type of the global static object. Do not add a *. + * @param NAME The name of the function to get a pointer to the global static object. + * @param ARGS the list of arguments, between brackets + * + * Example: + * @code + * class A + * { + * public: + * A(const char *s, int i); + * ... + * }; + * + * PHONON_GLOBAL_STATIC_WITH_ARG(A, globalA, ("foo", 0)) + * // The above creates a new globally static variable named 'globalA' which you + * // can use as a pointer to an instance of A. + * + * void doSomething() + * { + * // The first time you access globalA a new instance of A will be created automatically. + * A *a = globalA; + * ... + * } + * @endcode + * + * @ingroup KDEMacros + */ +#define PHONON_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \ +static QBasicAtomicPointer<TYPE > _k_static_##NAME = Q_BASIC_ATOMIC_INITIALIZER(0); \ +static bool _k_static_##NAME##_destroyed; \ +static struct PHONON_GLOBAL_STATIC_STRUCT_NAME(NAME) \ +{ \ + bool isDestroyed() \ + { \ + return _k_static_##NAME##_destroyed; \ + } \ + inline operator TYPE*() \ + { \ + return operator->(); \ + } \ + inline TYPE *operator->() \ + { \ + TYPE *p = _k_static_##NAME; \ + if (!p) { \ + if (isDestroyed()) { \ + qFatal("Fatal Error: Accessed global static '%s *%s()' after destruction. " \ + "Defined at %s:%d", #TYPE, #NAME, __FILE__, __LINE__); \ + } \ + p = new TYPE ARGS; \ + if (!_k_static_##NAME.testAndSetOrdered(0, p)) { \ + delete p; \ + p = _k_static_##NAME; \ + } else { \ + static Phonon::CleanUpGlobalStatic cleanUpObject = { destroy }; \ + } \ + } \ + return p; \ + } \ + inline TYPE &operator*() \ + { \ + return *operator->(); \ + } \ + static void destroy() \ + { \ + _k_static_##NAME##_destroyed = true; \ + TYPE *x = _k_static_##NAME; \ + _k_static_##NAME = 0; \ + delete x; \ + } \ +} NAME; + +QT_END_NAMESPACE +#endif // PHONON_GLOBALSTATIC_P_H diff --git a/src/3rdparty/phonon/phonon/iodevicestream.cpp b/src/3rdparty/phonon/phonon/iodevicestream.cpp new file mode 100644 index 0000000..5376da3 --- /dev/null +++ b/src/3rdparty/phonon/phonon/iodevicestream.cpp @@ -0,0 +1,100 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#include "iodevicestream_p.h" +#include "abstractmediastream_p.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + +namespace Phonon +{ + +class IODeviceStreamPrivate : public AbstractMediaStreamPrivate +{ + Q_DECLARE_PUBLIC(IODeviceStream) + protected: + IODeviceStreamPrivate(QIODevice *_ioDevice) + : ioDevice(_ioDevice) + { + if (!ioDevice->isOpen()) { + ioDevice->open(QIODevice::ReadOnly); + } + Q_ASSERT(ioDevice->isOpen()); + Q_ASSERT(ioDevice->isReadable()); + streamSize = ioDevice->size(); + streamSeekable = !ioDevice->isSequential(); + } + + private: + QIODevice *ioDevice; +}; + +IODeviceStream::IODeviceStream(QIODevice *ioDevice, QObject *parent) + : AbstractMediaStream(*new IODeviceStreamPrivate(ioDevice), parent) +{ + Q_D(IODeviceStream); + d->ioDevice->reset(); +} + +IODeviceStream::~IODeviceStream() +{ +} + +void IODeviceStream::reset() +{ + Q_D(IODeviceStream); + d->ioDevice->reset(); + //resetDone(); +} + +void IODeviceStream::needData() +{ + quint32 size = 4096; + Q_D(IODeviceStream); + const QByteArray data = d->ioDevice->read(size); + if (data.isEmpty() && !d->ioDevice->atEnd()) { + error(Phonon::NormalError, d->ioDevice->errorString()); + } + writeData(data); + if (d->ioDevice->atEnd()) { + endOfData(); + } +} + +void IODeviceStream::seekStream(qint64 offset) +{ + Q_D(IODeviceStream); + d->ioDevice->seek(offset); + //seekStreamDone(); +} + +} // namespace Phonon + +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + +QT_END_NAMESPACE + +#include "moc_iodevicestream_p.cpp" + +// vim: sw=4 sts=4 et tw=100 diff --git a/src/3rdparty/phonon/phonon/iodevicestream_p.h b/src/3rdparty/phonon/phonon/iodevicestream_p.h new file mode 100644 index 0000000..5eb90bc --- /dev/null +++ b/src/3rdparty/phonon/phonon/iodevicestream_p.h @@ -0,0 +1,58 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_IODEVICESTREAM_P_H +#define PHONON_IODEVICESTREAM_P_H + +#include "abstractmediastream.h" + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + +class QIODevice; + +namespace Phonon +{ + +class IODeviceStreamPrivate; +class IODeviceStream : public AbstractMediaStream +{ + Q_OBJECT + Q_DECLARE_PRIVATE(IODeviceStream) + public: + explicit IODeviceStream(QIODevice *ioDevice, QObject *parent = 0); + ~IODeviceStream(); + + void reset(); + void needData(); + void seekStream(qint64); +}; +} // namespace Phonon + +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // PHONON_IODEVICESTREAM_P_H diff --git a/src/3rdparty/phonon/phonon/mediacontroller.cpp b/src/3rdparty/phonon/phonon/mediacontroller.cpp new file mode 100644 index 0000000..d094381 --- /dev/null +++ b/src/3rdparty/phonon/phonon/mediacontroller.cpp @@ -0,0 +1,239 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + Copyright (C) 2008 Ian Monroe <ian@monroe.nu> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#include "mediacontroller.h" +#include "mediaobject.h" +#include "addoninterface.h" +#include <QtCore/QList> +#include <QtCore/QVariant> +#include "frontendinterface_p.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_MEDIACONTROLLER + +namespace Phonon +{ + +class MediaControllerPrivate : public FrontendInterfacePrivate +{ + public: + MediaControllerPrivate(MediaObject *mp) : FrontendInterfacePrivate(mp) {} + + virtual void backendObjectChanged(QObject *); + MediaController *q; +}; + +MediaController::MediaController(MediaObject *mp) + : QObject(mp) + , d(new MediaControllerPrivate(mp)) +{ + d->q = this; + d->_backendObjectChanged(); +} + +void MediaControllerPrivate::backendObjectChanged(QObject *m_backendObject) +{ + QObject::connect(m_backendObject, SIGNAL(availableSubtitlesChanged()), q, SIGNAL(availableSubtitlesChanged())); + QObject::connect(m_backendObject, SIGNAL(availableAudioChannelsChanged()), q, SIGNAL(availableAudioChannelsChanged())); + QObject::connect(m_backendObject, SIGNAL(titleChanged(int)), q, SIGNAL(titleChanged(int))); + QObject::connect(m_backendObject, SIGNAL(availableTitlesChanged(int)), q, SIGNAL(availableTitlesChanged(int))); + QObject::connect(m_backendObject, SIGNAL(chapterChanged(int)), q, SIGNAL(chapterChanged(int))); + QObject::connect(m_backendObject, SIGNAL(availableChaptersChanged(int)), q, SIGNAL(availableChaptersChanged(int))); + QObject::connect(m_backendObject, SIGNAL(angleChanged(int)), q, SIGNAL(angleChanged(int))); + QObject::connect(m_backendObject, SIGNAL(availableAnglesChanged(int)), q, SIGNAL(availableAnglesChanged(int))); +} + +MediaController::~MediaController() +{ + delete d; +} + +#define IFACE \ + AddonInterface *iface = d->iface(); \ + if (!iface) return + +MediaController::Features MediaController::supportedFeatures() const +{ + if (!d || !d->media) { + return false; + } + IFACE false; + Features ret; + if (iface->hasInterface(AddonInterface::AngleInterface)) { + ret |= Angles; + } + if (iface->hasInterface(AddonInterface::ChapterInterface)) { + ret |= Chapters; + } + if (iface->hasInterface(AddonInterface::TitleInterface)) { + ret |= Titles; + } + return ret; +} + +int MediaController::availableTitles() const +{ + IFACE 0; + return iface->interfaceCall(AddonInterface::TitleInterface, + AddonInterface::availableTitles).toInt(); +} + +int MediaController::currentTitle() const +{ + IFACE 0; + return iface->interfaceCall(AddonInterface::TitleInterface, + AddonInterface::title).toInt(); +} + +void MediaController::setCurrentTitle(int titleNumber) +{ + IFACE; + iface->interfaceCall(AddonInterface::TitleInterface, + AddonInterface::setTitle, QList<QVariant>() << QVariant(titleNumber)); +} + +bool MediaController::autoplayTitles() const +{ + IFACE true; + return iface->interfaceCall(AddonInterface::TitleInterface, + AddonInterface::autoplayTitles).toBool(); +} + +void MediaController::setAutoplayTitles(bool b) +{ + IFACE; + iface->interfaceCall(AddonInterface::TitleInterface, + AddonInterface::setAutoplayTitles, QList<QVariant>() << QVariant(b)); +} + +void MediaController::nextTitle() +{ + setCurrentTitle(currentTitle() + 1); +} + +void MediaController::previousTitle() +{ + setCurrentTitle(currentTitle() - 1); +} + +int MediaController::availableChapters() const +{ + IFACE 0; + return iface->interfaceCall(AddonInterface::ChapterInterface, + AddonInterface::availableChapters).toInt(); +} + +int MediaController::currentChapter() const +{ + IFACE 0; + return iface->interfaceCall(AddonInterface::ChapterInterface, + AddonInterface::chapter).toInt(); +} + +void MediaController::setCurrentChapter(int titleNumber) +{ + IFACE; + iface->interfaceCall(AddonInterface::ChapterInterface, + AddonInterface::setChapter, QList<QVariant>() << QVariant(titleNumber)); +} + +int MediaController::availableAngles() const +{ + IFACE 0; + return iface->interfaceCall(AddonInterface::AngleInterface, + AddonInterface::availableAngles).toInt(); +} + +int MediaController::currentAngle() const +{ + IFACE 0; + return iface->interfaceCall(AddonInterface::AngleInterface, + AddonInterface::angle).toInt(); +} + +void MediaController::setCurrentAngle(int titleNumber) +{ + IFACE; + iface->interfaceCall(AddonInterface::AngleInterface, + AddonInterface::setAngle, QList<QVariant>() << QVariant(titleNumber)); +} + +AudioChannelDescription MediaController::currentAudioChannel() const +{ + IFACE AudioChannelDescription(); + return iface->interfaceCall(AddonInterface::AudioChannelInterface, + AddonInterface::currentAudioChannel).value<AudioChannelDescription>(); +} + +SubtitleDescription MediaController::currentSubtitle() const +{ + IFACE SubtitleDescription(); + return iface->interfaceCall(AddonInterface::SubtitleInterface, + AddonInterface::currentSubtitle).value<SubtitleDescription>(); +} + +QList<AudioChannelDescription> MediaController::availableAudioChannels() const +{ + QList<AudioChannelDescription> retList; + IFACE retList; + retList = iface->interfaceCall(AddonInterface::AudioChannelInterface, + AddonInterface::availableAudioChannels).value< QList<AudioChannelDescription> >(); + return retList; +} + +QList<SubtitleDescription> MediaController::availableSubtitles() const +{ + QList<SubtitleDescription> retList; + IFACE retList; + retList = iface->interfaceCall(AddonInterface::SubtitleInterface, + AddonInterface::availableSubtitles) + .value< QList<SubtitleDescription> >(); + return retList; +} + +void MediaController::setCurrentAudioChannel(const Phonon::AudioChannelDescription &stream) +{ + IFACE; + iface->interfaceCall(AddonInterface::AudioChannelInterface, + AddonInterface::setCurrentAudioChannel, QList<QVariant>() << qVariantFromValue(stream)); +} + +void MediaController::setCurrentSubtitle(const Phonon::SubtitleDescription &stream) +{ + IFACE; + iface->interfaceCall(AddonInterface::SubtitleInterface, + AddonInterface::setCurrentSubtitle, QList<QVariant>() << qVariantFromValue(stream)); +} + +#undef IFACE + +} // namespace Phonon + +#endif //QT_NO_PHONON_MEDIACONTROLLER + +QT_END_NAMESPACE + +#include "moc_mediacontroller.cpp" + +// vim: sw=4 sts=4 et tw=100 diff --git a/src/3rdparty/phonon/phonon/mediacontroller.h b/src/3rdparty/phonon/phonon/mediacontroller.h new file mode 100644 index 0000000..19aaf13 --- /dev/null +++ b/src/3rdparty/phonon/phonon/mediacontroller.h @@ -0,0 +1,188 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_MEDIACONTROLLER_H +#define PHONON_MEDIACONTROLLER_H + +#include "phonon_export.h" +#include "objectdescription.h" + +#include <QtCore/QObject> +#include <QtCore/QtGlobal> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_MEDIACONTROLLER + +namespace Phonon +{ +class MediaControllerPrivate; +class MediaObject; + +/** \class MediaController mediacontroller.h Phonon/MediaController + * \brief Controls optional features of a media file/device like title, chapter, angle. + * + * \ingroup Playback + * \author Matthias Kretz <kretz@kde.org> + */ +class PHONON_EXPORT MediaController : public QObject +{ + Q_OBJECT + Q_FLAGS(Features) + public: + enum Feature { + Angles = 1, + Chapters = 2, + Titles = 4 + }; + Q_DECLARE_FLAGS(Features, Feature) + + MediaController(MediaObject *parent); + ~MediaController(); + + Features supportedFeatures() const; + + int availableAngles() const; + int currentAngle() const; + + int availableChapters() const; + int currentChapter() const; + + int availableTitles() const; + int currentTitle() const; + + bool autoplayTitles() const; + + /** + * Returns the selected audio stream. + * + * \see availableAudioChannels + * \see setCurrentAudioChannel + */ + AudioChannelDescription currentAudioChannel() const; + + /** + * Returns the selected subtitle stream. + * + * \see availableSubtitles + * \see setCurrentSubtitle + */ + SubtitleDescription currentSubtitle() const; + + /** + * Returns the audio streams that can be selected by the user. The + * strings can directly be used in the user interface. + * + * \see selectedAudioChannel + * \see setCurrentAudioChannel + */ + QList<AudioChannelDescription> availableAudioChannels() const; + + /** + * Returns the subtitle streams that can be selected by the user. The + * strings can directly be used in the user interface. + * + * \see selectedSubtitle + * \see setCurrentSubtitle + */ + QList<SubtitleDescription> availableSubtitles() const; + + /** + * Selects an audio stream from the media. + * + * Some media formats allow multiple audio streams to be stored in + * the same file. Normally only one should be played back. + * + * \param stream Description of an audio stream + * + * \see availableAudioChannels() + * \see currentAudioChannel() + */ + void setCurrentAudioChannel(const Phonon::AudioChannelDescription &stream); + + /** + * Selects a subtitle stream from the media. + * + * Some media formats allow multiple subtitle streams to be stored in + * the same file. Normally only one should be displayed. + * + * \param stream description of a subtitle stream + * + * \see availableSubtitles() + * \see currentSubtitle() + */ + void setCurrentSubtitle(const Phonon::SubtitleDescription &stream); + + public Q_SLOTS: + void setCurrentAngle(int angleNumber); + void setCurrentChapter(int chapterNumber); + + /** + * Skips to the given title \p titleNumber. + * + * If it was playing before the title change it will start playback on the new title if + * autoplayTitles is enabled. + */ + void setCurrentTitle(int titleNumber); + void setAutoplayTitles(bool); + + /** + * Skips to the next title. + * + * If it was playing before the title change it will start playback on the next title if + * autoplayTitles is enabled. + */ + void nextTitle(); + + /** + * Skips to the previous title. + * + * If it was playing before the title change it will start playback on the previous title if + * autoplayTitles is enabled. + */ + void previousTitle(); + + Q_SIGNALS: + void availableSubtitlesChanged(); + void availableAudioChannelsChanged(); + void availableAnglesChanged(int availableAngles); + void angleChanged(int angleNumber); + void availableChaptersChanged(int availableChapters); + void chapterChanged(int chapterNumber); + void availableTitlesChanged(int availableTitles); + void titleChanged(int titleNumber); + + protected: + MediaControllerPrivate *const d; +}; + +} // namespace Phonon + +Q_DECLARE_OPERATORS_FOR_FLAGS(Phonon::MediaController::Features) + +#endif //QT_NO_PHONON_MEDIACONTROLLER + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // PHONON_MEDIACONTROLLER_H diff --git a/src/3rdparty/phonon/phonon/medianode.cpp b/src/3rdparty/phonon/phonon/medianode.cpp new file mode 100644 index 0000000..4693cb8 --- /dev/null +++ b/src/3rdparty/phonon/phonon/medianode.cpp @@ -0,0 +1,130 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). <thierry.bastian@trolltech.com> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#include "medianode.h" +#include "medianode_p.h" +#include "medianodedestructionhandler_p.h" +#include "factory_p.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + + MediaNode::MediaNode(MediaNodePrivate &dd) + : k_ptr(&dd) + { + k_ptr->q_ptr = this; + } + +bool MediaNode::isValid() const +{ + return const_cast<MediaNodePrivate *>(k_ptr)->backendObject() != 0; +} + + QList<Path> MediaNode::inputPaths() const + { + return k_ptr->inputPaths; + } + + QList<Path> MediaNode::outputPaths() const + { + return k_ptr->outputPaths; + } + + MediaNode::~MediaNode() + { + delete k_ptr; + } + + QObject *MediaNodePrivate::backendObject() + { + if (!m_backendObject && Factory::backend()) { + createBackendObject(); + } + return m_backendObject; + } + + MediaNodePrivate::~MediaNodePrivate() + { + foreach (MediaNodeDestructionHandler *handler, handlers) { + handler->phononObjectDestroyed(this); + } + Factory::deregisterFrontendObject(this); + delete m_backendObject; + m_backendObject = 0; + } + +void MediaNodePrivate::deleteBackendObject() +{ + if (m_backendObject && aboutToDeleteBackendObject()) { + delete m_backendObject; + m_backendObject = 0; + } +} + + MediaNodePrivate::MediaNodePrivate(MediaNodePrivate::CastId _castId) : castId(_castId), + m_backendObject(0) + { + Factory::registerFrontendObject(this); + } + + void MediaNodePrivate::addDestructionHandler(MediaNodeDestructionHandler *handler) + { + handlers.append(handler); + } + + void MediaNodePrivate::removeDestructionHandler(MediaNodeDestructionHandler *handler) + { + handlers.removeAll(handler); + } + + void MediaNodePrivate::addOutputPath(const Path &p) + { + outputPaths.append(p); + } + + void MediaNodePrivate::addInputPath(const Path &p) + { + inputPaths.append(p); + } + + void MediaNodePrivate::removeOutputPath(const Path &p) + { + int ret = outputPaths.removeAll(p); + Q_ASSERT(ret == 1); + Q_UNUSED(ret); + } + + void MediaNodePrivate::removeInputPath(const Path &p) + { + int ret = inputPaths.removeAll(p); + Q_ASSERT(ret == 1); + Q_UNUSED(ret); + } + + + +} // namespace Phonon + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/phonon/medianode.h b/src/3rdparty/phonon/phonon/medianode.h new file mode 100644 index 0000000..86931ac --- /dev/null +++ b/src/3rdparty/phonon/phonon/medianode.h @@ -0,0 +1,69 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). <thierry.bastian@trolltech.com> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_MEDIANODE_H +#define PHONON_MEDIANODE_H + +#include "phonondefs.h" +#include "phonon_export.h" +#include "path.h" + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + + class Path; + class MediaNodePrivate; + class PHONON_EXPORT MediaNode + { + friend class Path; + friend class PathPrivate; + friend PHONON_EXPORT Path createPath(MediaNode *source, MediaNode *sink); + K_DECLARE_PRIVATE(MediaNode) + public: + virtual ~MediaNode(); + /** + * Tells whether the backend provides an implementation of this + * class. + * + * \return \c true if backend provides an implementation + * \return \c false if the object is not implemented by the backend + */ + bool isValid() const; + + QList<Path> inputPaths() const; + QList<Path> outputPaths() const; + + protected: + MediaNode(MediaNodePrivate &dd); + MediaNodePrivate *const k_ptr; + }; + +} // namespace Phonon + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // PHONON_MEDIANODE_H diff --git a/src/3rdparty/phonon/phonon/medianode_p.h b/src/3rdparty/phonon/phonon/medianode_p.h new file mode 100644 index 0000000..d5424b7 --- /dev/null +++ b/src/3rdparty/phonon/phonon/medianode_p.h @@ -0,0 +1,145 @@ +/* This file is part of the KDE project +Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_MEDIANODE_P_H +#define PHONON_MEDIANODE_P_H + +#include <QtCore/QtGlobal> +#include <QtCore/QList> +#include <QtCore/QObject> +#include "path.h" +#include "phonon_export.h" + +QT_BEGIN_NAMESPACE + +class QObject; + +namespace Phonon +{ + class MediaNode; + class MediaNodeDestructionHandler; + + class PHONON_EXPORT MediaNodePrivate + { + Q_DECLARE_PUBLIC(MediaNode) + + friend class AudioOutputPrivate; + friend class FactoryPrivate; + + protected: + enum CastId { + MediaNodePrivateType, + AbstractAudioOutputPrivateType, + AudioOutputType + }; + public: + /** + * Returns the backend object. If the object does not exist it tries to + * create it before returning. + * + * \return the Iface object, might return \c 0 + */ + QObject *backendObject(); + + const CastId castId; + + protected: + MediaNodePrivate(CastId _castId = MediaNodePrivateType); + + virtual ~MediaNodePrivate(); + + /** + * \internal + * This method cleanly deletes the Iface object. It is called on + * destruction and before a backend change. + */ + void deleteBackendObject(); + + virtual bool aboutToDeleteBackendObject() = 0; + + /** + * \internal + * Creates the Iface object belonging to this class. For most cases the + * implementation is + * \code + * Q_Q(ClassName); + * m_iface = Factory::createClassName(this); + * return m_iface; + * \endcode + * + * This function should not be called except from slotCreateIface. + * + * \see slotCreateIface + */ + virtual void createBackendObject() = 0; + + public: + /** + * \internal + * This class has its own destroyed signal since some cleanup calls + * need the pointer to the backend object intact. The + * QObject::destroyed signals comes after the backend object was + * deleted. + * + * As this class cannot derive from QObject a simple handler + * interface is used. + */ + void addDestructionHandler(MediaNodeDestructionHandler *handler); + + /** + * \internal + * This class has its own destroyed signal since some cleanup calls + * need the pointer to the backend object intact. The + * QObject::destroyed signals comes after the backend object was + * deleted. + * + * As this class cannot derive from QObject a simple handler + * interface is used. + */ + void removeDestructionHandler(MediaNodeDestructionHandler *handler); + + void addOutputPath(const Path &); + void addInputPath(const Path &); + void removeOutputPath(const Path &); + void removeInputPath(const Path &); + + const QObject *qObject() const { return const_cast<MediaNodePrivate *>(this)->qObject(); } + virtual QObject *qObject() { return 0; } + + protected: + MediaNode *q_ptr; + public: + QObject *m_backendObject; + protected: + QList<Path> outputPaths; + QList<Path> inputPaths; + + private: + QList<MediaNodeDestructionHandler *> handlers; + Q_DISABLE_COPY(MediaNodePrivate) + }; + +} // namespace Phonon + +QT_END_NAMESPACE + +#endif // PHONON_MEDIANODE_P_H diff --git a/src/3rdparty/phonon/phonon/medianodedestructionhandler_p.h b/src/3rdparty/phonon/phonon/medianodedestructionhandler_p.h new file mode 100644 index 0000000..38c0907 --- /dev/null +++ b/src/3rdparty/phonon/phonon/medianodedestructionhandler_p.h @@ -0,0 +1,62 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef MEDIANODEDESTRUCTIONHANDLER_P_H +#define MEDIANODEDESTRUCTIONHANDLER_P_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +/** + * \internal + * + * Callback interface to keep track of Phonon frontend object destruction. + * + * \author Matthias Kretz <kretz@kde.org> + */ + +class MediaNodePrivate; + +class MediaNodeDestructionHandler +{ + friend class MediaNodePrivate; + +public: + virtual ~MediaNodeDestructionHandler() {} +protected: + /** + * \internal + * called from Base::~Base if this object was registered + * using BasePrivate::addDestructionHandler(). + */ + virtual void phononObjectDestroyed(MediaNodePrivate *) = 0; +}; +} + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // MEDIANODEDESTRUCTIONHANDLER_P_H diff --git a/src/3rdparty/phonon/phonon/mediaobject.cpp b/src/3rdparty/phonon/phonon/mediaobject.cpp new file mode 100644 index 0000000..de5fbc8 --- /dev/null +++ b/src/3rdparty/phonon/phonon/mediaobject.cpp @@ -0,0 +1,572 @@ +/* This file is part of the KDE project + Copyright (C) 2005-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ +#include "mediaobject.h" +#include "mediaobject_p.h" + +#include "factory_p.h" +#include "mediaobjectinterface.h" +#include "audiooutput.h" +#include "phonondefs_p.h" +#include "abstractmediastream.h" +#include "abstractmediastream_p.h" +#include "frontendinterface_p.h" + +#include <QtCore/QStringList> +#include <QtCore/QUrl> +#include <QtCore/QTimer> + +#include "phononnamespace_p.h" +#include "platform_p.h" + +#define PHONON_CLASSNAME MediaObject +#define PHONON_INTERFACENAME MediaObjectInterface + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +PHONON_OBJECT_IMPL + +MediaObject::~MediaObject() +{ + K_D(MediaObject); + if (d->m_backendObject) { + switch (state()) { + case PlayingState: + case BufferingState: + case PausedState: + stop(); + break; + case ErrorState: + case StoppedState: + case LoadingState: + break; + } + } +} + +Phonon::State MediaObject::state() const +{ + K_D(const MediaObject); +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + if (d->errorOverride) { + return d->state; + } + if (d->ignoreLoadingToBufferingStateChange) { + return BufferingState; + } + if (d->ignoreErrorToLoadingStateChange) { + return LoadingState; + } +#endif // QT_NO_PHONON_ABSTRACTMEDIASTREAM + if (!d->m_backendObject) { + return d->state; + } + return INTERFACE_CALL(state()); +} + +PHONON_INTERFACE_SETTER(setTickInterval, tickInterval, qint32) +PHONON_INTERFACE_GETTER(qint32, tickInterval, d->tickInterval) +PHONON_INTERFACE_GETTER(bool, hasVideo, false) +PHONON_INTERFACE_GETTER(bool, isSeekable, false) +PHONON_INTERFACE_GETTER(qint64, currentTime, d->currentTime) + +static inline bool isPlayable(const MediaSource::Type t) +{ + return t != MediaSource::Invalid && t != MediaSource::Empty; +} + +void MediaObject::play() +{ + K_D(MediaObject); + if (d->backendObject() && isPlayable(d->mediaSource.type())) { + INTERFACE_CALL(play()); + } +} + +void MediaObject::pause() +{ + K_D(MediaObject); + if (d->backendObject() && isPlayable(d->mediaSource.type())) { + INTERFACE_CALL(pause()); + } +} + +void MediaObject::stop() +{ + K_D(MediaObject); + if (d->backendObject() && isPlayable(d->mediaSource.type())) { + INTERFACE_CALL(stop()); + } +} + +void MediaObject::seek(qint64 time) +{ + K_D(MediaObject); + if (d->backendObject() && isPlayable(d->mediaSource.type())) { + INTERFACE_CALL(seek(time)); + } +} + +QString MediaObject::errorString() const +{ + if (state() == Phonon::ErrorState) { + K_D(const MediaObject); +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + if (d->errorOverride) { + return d->errorString; + } +#endif // QT_NO_PHONON_ABSTRACTMEDIASTREAM + return INTERFACE_CALL(errorString()); + } + return QString(); +} + +ErrorType MediaObject::errorType() const +{ + if (state() == Phonon::ErrorState) { + K_D(const MediaObject); +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + if (d->errorOverride) { + return d->errorType; + } +#endif // QT_NO_PHONON_ABSTRACTMEDIASTREAM + return INTERFACE_CALL(errorType()); + } + return Phonon::NoError; +} + +QStringList MediaObject::metaData(Phonon::MetaData f) const +{ + switch (f) { + case ArtistMetaData: + return metaData(QLatin1String("ARTIST" )); + case AlbumMetaData: + return metaData(QLatin1String("ALBUM" )); + case TitleMetaData: + return metaData(QLatin1String("TITLE" )); + case DateMetaData: + return metaData(QLatin1String("DATE" )); + case GenreMetaData: + return metaData(QLatin1String("GENRE" )); + case TracknumberMetaData: + return metaData(QLatin1String("TRACKNUMBER")); + case DescriptionMetaData: + return metaData(QLatin1String("DESCRIPTION")); + case MusicBrainzDiscIdMetaData: + return metaData(QLatin1String("MUSICBRAINZ_DISCID")); + } + return QStringList(); +} + +QStringList MediaObject::metaData(const QString &key) const +{ + K_D(const MediaObject); + return d->metaData.values(key); +} + +QMultiMap<QString, QString> MediaObject::metaData() const +{ + K_D(const MediaObject); + return d->metaData; +} + +PHONON_INTERFACE_GETTER(qint32, prefinishMark, d->prefinishMark) +PHONON_INTERFACE_SETTER(setPrefinishMark, prefinishMark, qint32) + +PHONON_INTERFACE_GETTER(qint32, transitionTime, d->transitionTime) +PHONON_INTERFACE_SETTER(setTransitionTime, transitionTime, qint32) + +qint64 MediaObject::totalTime() const +{ + K_D(const MediaObject); + if (!d->m_backendObject) { + return -1; + } + return INTERFACE_CALL(totalTime()); +} + +qint64 MediaObject::remainingTime() const +{ + K_D(const MediaObject); + if (!d->m_backendObject) { + return -1; + } + qint64 ret = INTERFACE_CALL(remainingTime()); + if (ret < 0) { + return -1; + } + return ret; +} + +MediaSource MediaObject::currentSource() const +{ + K_D(const MediaObject); + return d->mediaSource; +} + +void MediaObject::setCurrentSource(const MediaSource &newSource) +{ + K_D(MediaObject); + if (!k_ptr->backendObject()) { + d->mediaSource = newSource; + return; + } + + pDebug() << Q_FUNC_INFO << newSource.url(); + + stop(); // first call stop as that often is the expected state + // for setting a new URL + + d->mediaSource = newSource; +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + d->kiofallback = 0; // kiofallback auto-deletes +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + +//X if (url.scheme() == "http") { +//X d->kiofallback = Platform::createMediaStream(url, this); +//X if (d->kiofallback) { +//X ... +//X return; +//X } +//X } + +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + if (d->mediaSource.type() == MediaSource::Stream) { + Q_ASSERT(d->mediaSource.stream()); + d->mediaSource.stream()->d_func()->setMediaObjectPrivate(d); + } +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + + INTERFACE_CALL(setSource(d->mediaSource)); +} + +void MediaObject::clear() +{ + K_D(MediaObject); + d->sourceQueue.clear(); + setCurrentSource(MediaSource()); +} + +QList<MediaSource> MediaObject::queue() const +{ + K_D(const MediaObject); + return d->sourceQueue; +} + +void MediaObject::setQueue(const QList<MediaSource> &sources) +{ + K_D(MediaObject); + d->sourceQueue.clear(); + enqueue(sources); +} + +void MediaObject::setQueue(const QList<QUrl> &urls) +{ + K_D(MediaObject); + d->sourceQueue.clear(); + enqueue(urls); +} + +void MediaObject::enqueue(const MediaSource &source) +{ + K_D(MediaObject); + if (!isPlayable(d->mediaSource.type())) { + // the current source is nothing valid so this source needs to become the current one + setCurrentSource(source); + } else { + d->sourceQueue << source; + } +} + +void MediaObject::enqueue(const QList<MediaSource> &sources) +{ + foreach (const MediaSource &m, sources) { + enqueue(m); + } +} + +void MediaObject::enqueue(const QList<QUrl> &urls) +{ + foreach (const QUrl &url, urls) { + enqueue(url); + } +} + +void MediaObject::clearQueue() +{ + K_D(MediaObject); + d->sourceQueue.clear(); +} + +bool MediaObjectPrivate::aboutToDeleteBackendObject() +{ + //pDebug() << Q_FUNC_INFO; + prefinishMark = pINTERFACE_CALL(prefinishMark()); + transitionTime = pINTERFACE_CALL(transitionTime()); + //pDebug() << Q_FUNC_INFO; + if (m_backendObject) { + state = pINTERFACE_CALL(state()); + currentTime = pINTERFACE_CALL(currentTime()); + tickInterval = pINTERFACE_CALL(tickInterval()); + } + return true; +} + +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM +void MediaObjectPrivate::streamError(Phonon::ErrorType type, const QString &text) +{ + Q_Q(MediaObject); + State lastState = q->state(); + errorOverride = true; + errorType = type; + errorString = text; + state = ErrorState; + QMetaObject::invokeMethod(q, "stateChanged", Qt::QueuedConnection, Q_ARG(Phonon::State, Phonon::ErrorState), Q_ARG(Phonon::State, lastState)); + //emit q->stateChanged(ErrorState, lastState); +} + +void MediaObjectPrivate::_k_stateChanged(Phonon::State newstate, Phonon::State oldstate) +{ + Q_Q(MediaObject); + if (mediaSource.type() != MediaSource::Url) { + // special handling only necessary for URLs because of the fallback + emit q->stateChanged(newstate, oldstate); + return; + } + + if (errorOverride) { + errorOverride = false; + if (newstate == ErrorState) { + return; + } + oldstate = ErrorState; + } + + // backend MediaObject reached ErrorState, try a KioMediaSource + if (newstate == Phonon::ErrorState && !kiofallback) { + kiofallback = Platform::createMediaStream(mediaSource.url(), q); + if (!kiofallback) { + pDebug() << "backend MediaObject reached ErrorState, no KIO fallback available"; + emit q->stateChanged(newstate, oldstate); + return; + } + pDebug() << "backend MediaObject reached ErrorState, trying Platform::createMediaStream now"; + ignoreLoadingToBufferingStateChange = false; + ignoreErrorToLoadingStateChange = false; + switch (oldstate) { + case Phonon::BufferingState: + // play() has already been called, we need to make sure it is called + // on the backend with the KioMediaStream MediaSource now, too + ignoreLoadingToBufferingStateChange = true; + break; + case Phonon::LoadingState: + ignoreErrorToLoadingStateChange = true; + // no extras + break; + default: + pError() << "backend MediaObject reached ErrorState after " << oldstate + << ". It seems a KioMediaStream will not help here, trying anyway."; + emit q->stateChanged(Phonon::LoadingState, oldstate); + break; + } + kiofallback->d_func()->setMediaObjectPrivate(this); + MediaSource mediaSource(kiofallback); + mediaSource.setAutoDelete(true); + pINTERFACE_CALL(setSource(mediaSource)); + if (oldstate == Phonon::BufferingState) { + q->play(); + } + return; + } else if (ignoreLoadingToBufferingStateChange && + kiofallback && + oldstate == Phonon::LoadingState) { + if (newstate != Phonon::BufferingState) { + emit q->stateChanged(newstate, Phonon::BufferingState); + } + return; + } else if (ignoreErrorToLoadingStateChange && kiofallback && oldstate == ErrorState) { + if (newstate != LoadingState) { + emit q->stateChanged(newstate, Phonon::LoadingState); + } + return; + } + + emit q->stateChanged(newstate, oldstate); +} +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + +void MediaObjectPrivate::_k_aboutToFinish() +{ + Q_Q(MediaObject); + pDebug() << Q_FUNC_INFO; + +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + kiofallback = 0; // kiofallback auto-deletes +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + + if (sourceQueue.isEmpty()) { + emit q->aboutToFinish(); + if (sourceQueue.isEmpty()) { + return; + } + } + + mediaSource = sourceQueue.head(); + pINTERFACE_CALL(setNextSource(mediaSource)); +} + +void MediaObjectPrivate::_k_currentSourceChanged(const MediaSource &source) +{ + Q_Q(MediaObject); + pDebug() << Q_FUNC_INFO; + + if (!sourceQueue.isEmpty() && sourceQueue.head() == source) + sourceQueue.dequeue(); + + emit q->currentSourceChanged(source); +} + +void MediaObjectPrivate::setupBackendObject() +{ + Q_Q(MediaObject); + Q_ASSERT(m_backendObject); + //pDebug() << Q_FUNC_INFO; + +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + QObject::connect(m_backendObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)), q, SLOT(_k_stateChanged(Phonon::State, Phonon::State))); +#else + QObject::connect(m_backendObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)), q, SIGNAL(stateChanged(Phonon::State, Phonon::State))); +#endif // QT_NO_PHONON_ABSTRACTMEDIASTREAM + QObject::connect(m_backendObject, SIGNAL(tick(qint64)), q, SIGNAL(tick(qint64))); + QObject::connect(m_backendObject, SIGNAL(seekableChanged(bool)), q, SIGNAL(seekableChanged(bool))); +#ifndef QT_NO_PHONON_VIDEO + QObject::connect(m_backendObject, SIGNAL(hasVideoChanged(bool)), q, SIGNAL(hasVideoChanged(bool))); +#endif //QT_NO_PHONON_VIDEO + QObject::connect(m_backendObject, SIGNAL(bufferStatus(int)), q, SIGNAL(bufferStatus(int))); + QObject::connect(m_backendObject, SIGNAL(finished()), q, SIGNAL(finished())); + QObject::connect(m_backendObject, SIGNAL(aboutToFinish()), q, SLOT(_k_aboutToFinish())); + QObject::connect(m_backendObject, SIGNAL(prefinishMarkReached(qint32)), q, SIGNAL(prefinishMarkReached(qint32))); + QObject::connect(m_backendObject, SIGNAL(totalTimeChanged(qint64)), q, SIGNAL(totalTimeChanged(qint64))); + QObject::connect(m_backendObject, SIGNAL(metaDataChanged(const QMultiMap<QString, QString> &)), + q, SLOT(_k_metaDataChanged(const QMultiMap<QString, QString> &))); + QObject::connect(m_backendObject, SIGNAL(currentSourceChanged(const MediaSource&)), + q, SLOT(_k_currentSourceChanged(const MediaSource&))); + + // set up attributes + pINTERFACE_CALL(setTickInterval(tickInterval)); + pINTERFACE_CALL(setPrefinishMark(prefinishMark)); + pINTERFACE_CALL(setTransitionTime(transitionTime)); + + switch(state) + { + case LoadingState: + case StoppedState: + case ErrorState: + break; + case PlayingState: + case BufferingState: + QTimer::singleShot(0, q, SLOT(_k_resumePlay())); + break; + case PausedState: + QTimer::singleShot(0, q, SLOT(_k_resumePause())); + break; + } + const State backendState = pINTERFACE_CALL(state()); + if (state != backendState && state != ErrorState) { + // careful: if state is ErrorState we might be switching from a + // MediaObject to a ByteStream for KIO fallback. In that case the state + // change to ErrorState was already suppressed. + pDebug() << "emitting a state change because the backend object has been replaced"; + emit q->stateChanged(backendState, state); + state = backendState; + } + +#ifndef QT_NO_PHONON_MEDIACONTROLLER + foreach (FrontendInterfacePrivate *f, interfaceList) { + f->_backendObjectChanged(); + } +#endif //QT_NO_PHONON_MEDIACONTROLLER + + // set up attributes + if (isPlayable(mediaSource.type())) { +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + if (mediaSource.type() == MediaSource::Stream) { + Q_ASSERT(mediaSource.stream()); + mediaSource.stream()->d_func()->setMediaObjectPrivate(this); + } +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + pINTERFACE_CALL(setSource(mediaSource)); + } +} + +void MediaObjectPrivate::_k_resumePlay() +{ + qobject_cast<MediaObjectInterface *>(m_backendObject)->play(); + if (currentTime > 0) { + qobject_cast<MediaObjectInterface *>(m_backendObject)->seek(currentTime); + } +} + +void MediaObjectPrivate::_k_resumePause() +{ + pINTERFACE_CALL(pause()); + if (currentTime > 0) { + qobject_cast<MediaObjectInterface *>(m_backendObject)->seek(currentTime); + } +} + +void MediaObjectPrivate::_k_metaDataChanged(const QMultiMap<QString, QString> &newMetaData) +{ + metaData = newMetaData; + emit q_func()->metaDataChanged(); +} + +void MediaObjectPrivate::phononObjectDestroyed(MediaNodePrivate *bp) +{ + // this method is called from Phonon::Base::~Base(), meaning the AudioPath + // dtor has already been called, also virtual functions don't work anymore + // (therefore qobject_cast can only downcast from Base) + Q_ASSERT(bp); + Q_UNUSED(bp); +} + +MediaObject *createPlayer(Phonon::Category category, const MediaSource &source) +{ + MediaObject *mo = new MediaObject; + AudioOutput *ao = new AudioOutput(category, mo); + createPath(mo, ao); + if (isPlayable(source.type())) { + mo->setCurrentSource(source); + } + return mo; +} + +} //namespace Phonon + +QT_END_NAMESPACE + +#include "moc_mediaobject.cpp" + +#undef PHONON_CLASSNAME +#undef PHONON_INTERFACENAME +// vim: sw=4 tw=100 et diff --git a/src/3rdparty/phonon/phonon/mediaobject.dox b/src/3rdparty/phonon/phonon/mediaobject.dox new file mode 100644 index 0000000..cc6fe5f --- /dev/null +++ b/src/3rdparty/phonon/phonon/mediaobject.dox @@ -0,0 +1,71 @@ +/** +\page phonon_MediaObject The MediaObject class + +\section phonon_MediaObject_derived Media Data Producing Class + +There is the class that produces the media data (often called a source in +media frameworks). + +\section phonon_MediaObject_requiredfunctions Required Functions +\li qint32 \ref phonon_MediaObject_prefinishMark "prefinishMark()" +\li void \ref phonon_MediaObject_setPrefinishMark "setPrefinishMark(qint32)" + +\section phonon_MediaObject_optionalfunctions Optional Functions +\li qint64 \ref phonon_MediaObject_remainingTime "remainingTime()" + +\section phonon_MediaObject_signals Signals +\li void \ref phonon_MediaObject_totalTimeChanged "totalTimeChanged(qint64 totalTime)" +\li void \ref phonon_MediaObject_prefinishMarkReached "prefinishMarkReached(qint32 msec)" +\li void \ref phonon_MediaObject_finished "finished()" + +\section phonon_MediaObject_memberdocs Member Function Documentation + +\see \ref phonon_MediaObject_totalTimeChanged + +\subsection phonon_MediaObject_prefinishMark qint32 prefinishMark() +Returns the time in milliseconds the \ref phonon_MediaObject_prefinishMarkReached +"prefinishMarkReached" signal is emitted before the playback if finished and \ref +phonon_MediaObject_finished "finished" is emitted. + +\subsection phonon_MediaObject_setPrefinishMark void setPrefinishMark(qint32 msec) +Sets the time in milliseconds the \ref phonon_MediaObject_prefinishMarkReached +"prefinishMarkReached" signal is emitted before the playback if finished and \ref +phonon_MediaObject_finished "finished" is emitted. + +\param msec The time in milliseconds. If the value is less than or equal to 0 +the \ref phonon_MediaObject_prefinishMarkReached "prefinishMarkReached" signal is disabled. + +\subsection phonon_MediaObject_remainingTime qint64 remainingTime() +Get the remaining time (in milliseconds) of the file currently being played. If +the method is not implemented in the backend the frontend will use the +difference between \link Phonon::MediaObjectInterface::totalTime() MediaObjectInterface::totalTime() \endlink and +\link Phonon::MediaObjectInterface::currentTime() MediaObjectInterface::currentTime() \endlink. + +\section phonon_MediaObject_signaldocs Signals Documentation +\subsection phonon_MediaObject_prefinishMarkReached void prefinishMarkReached(qint32 msec) +Emitted when the file has finished playing on its own. +I.e. it is not emitted if you call stop(), pause() or +load(), but only on end-of-file or a critical error. +void finished() + +\param msec The remaining time until the playback finishes + +\subsection phonon_MediaObject_totalTimeChanged void totalTimeChanged(qint64 totalTime) +This signal is emitted as soon as the length of the media file is +known or has changed. For most non-local media data the length of +the media can only be known after some time. At that time the +totalTime function can not return useful information. You have +to wait for this signal to know the real length. + +\param totalTimeChanged The total time of the media file in milliseconds. + +\see \link Phonon::MediaObjectInterface::totalTime() MediaObjectInterface::totalTime() \endlink + +\subsection phonon_MediaObject_finished void finished() +This signal is emitted when the playback of the media finished (on its own). +It is not emitted if stop() or pause() are called - only on end-of-file or a +critical error (for example the media data stream is corrupted and playback of +the media has to be finished). + +\see \ref phonon_MediaObject_prefinishMarkReached +*/ diff --git a/src/3rdparty/phonon/phonon/mediaobject.h b/src/3rdparty/phonon/phonon/mediaobject.h new file mode 100644 index 0000000..5cbddbb --- /dev/null +++ b/src/3rdparty/phonon/phonon/mediaobject.h @@ -0,0 +1,625 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ +#ifndef Phonon_MEDIAOBJECT_H +#define Phonon_MEDIAOBJECT_H + +#include "medianode.h" +#include "mediasource.h" +#include "phonon_export.h" +#include "phonondefs.h" +#include "phononnamespace.h" + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + class MediaObjectPrivate; + + /** \class MediaObject mediaobject.h Phonon/MediaObject + * \short Interface for media playback of a given URL. + * + * This class is the most important class in %Phonon. Use it to open a media + * file at an arbitrary location, a CD or DVD or to stream media data from + * the application to the backend. + * + * This class controls the state (play, pause, stop, seek) + * and you can use it to get a lot of information about the media data. + * + * Notice that most functions of this class are asynchronous. + * That means if you call play() the object only starts playing when the + * stateChanged() signal tells you that the object changed into PlayingState. + * The states you can expect are documented for those methods. + * + * A common usage example is the following: + * \code + * media = new MediaObject(this); + * connect(media, SIGNAL(finished()), SLOT(slotFinished()); + * media->setCurrentSource("/home/username/music/filename.ogg"); + * media->play(); + * \endcode + * + * If you want to play more that one media file (one after another) you can + * either tell MediaObject about all those files + * \code + * media->setCurrentSource(":/sounds/startsound.ogg"); + * media->enqueue("/home/username/music/song.mp3"); + * media->enqueue(":/sounds/endsound.ogg"); + * \endcode + * or provide the next file just in time: + * \code + * media->setCurrentSource(":/sounds/startsound.ogg"); + * connect(media, SIGNAL(aboutToFinish()), SLOT(enqueueNextSource())); + * } + * + * void enqueueNextSource() + * { + * media->enqueue("/home/username/music/song.mp3"); + * } + * \endcode + * + * \ingroup Playback + * \ingroup Recording + * \author Matthias Kretz <kretz@kde.org> + */ + class PHONON_EXPORT MediaObject : public QObject, public MediaNode + { + friend class FrontendInterfacePrivate; + Q_OBJECT + K_DECLARE_PRIVATE(MediaObject) + PHONON_OBJECT(MediaObject) + /** + * \brief Defines the time between media sources. + * + * A positive transition time defines a gap of silence between queued + * media sources. + * + * A transition time of 0 ms requests gapless playback (sample precise + * queueing of the next source). + * + * A negative transition time defines a crossfade between the queued + * media sources. + * + * Defaults to 0 (gapless playback). + * + * \warning This feature might not work reliably on every platform. + */ + Q_PROPERTY(qint32 transitionTime READ transitionTime WRITE setTransitionTime) + + /** + * \brief Get a signal before playback finishes. + * + * This property specifies the time in milliseconds the + * prefinishMarkReached signal is + * emitted before the playback finishes. A value of \c 0 disables the + * signal. + * + * Defaults to \c 0 (disabled). + * + * \warning For some media data the total time cannot be determined + * accurately, therefore the accuracy of the prefinishMarkReached signal + * can be bad sometimes. Still, it is better to use this method than to + * look at totalTime() and currentTime() to emulate the behaviour + * because the backend might have more information available than your + * application does through totalTime and currentTime. + * + * \see prefinishMarkReached + */ + Q_PROPERTY(qint32 prefinishMark READ prefinishMark WRITE setPrefinishMark) + + /** + * \brief The time interval in milliseconds between two ticks. + * + * The %tick interval is the time that elapses between the emission of two tick signals. + * If you set the interval to \c 0 the tick signal gets disabled. + * + * Defaults to \c 0 (disabled). + * + * \warning The back-end is free to choose a different tick interval close + * to what you asked for. This means that the following code \em may \em fail: + * \code + * int x = 200; + * media->setTickInterval(x); + * Q_ASSERT(x == producer->tickInterval()); + * \endcode + * On the other hand the following is guaranteed: + * \code + * int x = 200; + * media->setTickInterval(x); + * Q_ASSERT(x >= producer->tickInterval() && + * x <= 2 * producer->tickInterval()); + * \endcode + * + * \see tick + */ + Q_PROPERTY(qint32 tickInterval READ tickInterval WRITE setTickInterval) + public: + /** + * Destroys the MediaObject. + */ + ~MediaObject(); + + /** + * Get the current state. + * + * @return The state of the object. + * + * @see State + * \see stateChanged + */ + State state() const; + + /** + * Check whether the media data includes a video stream. + * + * \warning This information cannot be known immediately. It is best + * to also listen to the hasVideoChanged signal. + * + * \code + * connect(media, SIGNAL(hasVideoChanged(bool)), hasVideoChanged(bool)); + * media->setCurrentSource("somevideo.avi"); + * media->hasVideo(); // returns false; + * } + * + * void hasVideoChanged(bool b) + * { + * // b == true + * media->hasVideo(); // returns true; + * } + * \endcode + * + * \return \c true if the media contains video data. \c false + * otherwise. + * + * \see hasVideoChanged + */ + bool hasVideo() const; + + /** + * Check whether the current media may be seeked. + * + * \warning This information cannot be known immediately. It is best + * to also listen to the hasVideoChanged signal. + * + * \code + * connect(media, SIGNAL(hasVideoChanged(bool)), hasVideoChanged(bool)); + * media->setCurrentSource("somevideo.avi"); + * media->hasVideo(); // returns false; + * } + * + * void hasVideoChanged(bool b) + * { + * // b == true + * media->hasVideo(); // returns true; + * } + * \endcode + * + * \return \c true when the current media may be seeked. \c false + * otherwise. + * + * \see seekableChanged() + */ + bool isSeekable() const; + + qint32 tickInterval() const; + + /** + * Returns the strings associated with the given \p key. + * + * Backends should use the keys specified in the Ogg Vorbis + * documentation: http://xiph.org/vorbis/doc/v-comment.html + * + * Therefore the following should work with every backend: + * + * A typical usage looks like this: + * \code + * setMetaArtist (media->metaData("ARTIST" )); + * setMetaAlbum (media->metaData("ALBUM" )); + * setMetaTitle (media->metaData("TITLE" )); + * setMetaDate (media->metaData("DATE" )); + * setMetaGenre (media->metaData("GENRE" )); + * setMetaTrack (media->metaData("TRACKNUMBER")); + * setMetaComment(media->metaData("DESCRIPTION")); + * \endcode + * + * For Audio CDs you can query + * \code + * metaData("MUSICBRAINZ_DISCID"); + * \endcode + * to get a DiscID hash that you can use with the MusicBrainz + * service: + * http://musicbrainz.org/doc/ClientHOWTO + */ + QStringList metaData(const QString &key) const; + + /** + * Returns the strings associated with the given \p key. + * + * Same as above except that the keys are defined in the + * Phonon::MetaData enum. + */ + QStringList metaData(Phonon::MetaData key) const; + + /** + * Returns all meta data. + */ + QMultiMap<QString, QString> metaData() const; + + /** + * Returns a human-readable description of the last error that occurred. + */ + QString errorString() const; + + /** + * Tells your program what to do about the error. + * + * \see Phonon::ErrorType + */ + ErrorType errorType() const; + + /** + * Returns the current media source. + * + * \see setCurrentSource + */ + MediaSource currentSource() const; + + /** + * Set the media source the MediaObject should use. + * + * \param source The MediaSource object to the media data. You can + * just as well use a QUrl or QString (for a local file) here. + * Setting an empty (invalid) source, will stop and remove the + * current source. + * + * \code + * QUrl url("http://www.example.com/music.ogg"); + * media->setCurrentSource(url); + * \endcode + * + * \see currentSource + */ + void setCurrentSource(const MediaSource &source); + + /** + * Returns the queued media sources. This does list does not include + * the current source (returned by currentSource). + */ + QList<MediaSource> queue() const; + + /** + * Set the MediaSources to play when the current media has finished. + * + * This function will overwrite the current queue. + * + * \see clearQueue + * \see enqueue + */ + void setQueue(const QList<MediaSource> &sources); + + /** + * Set the MediaSources to play when the current media has finished. + * + * This function overwrites the current queue. + * + * \see clearQueue + * \see enqueue + */ + void setQueue(const QList<QUrl> &urls); + + /** + * Appends one source to the queue. Use this function to provide + * the next source just in time after the aboutToFinish signal was + * emitted. + * + * \see aboutToFinish + * \see setQueue + * \see clearQueue + */ + void enqueue(const MediaSource &source); + + /** + * Appends multiple sources to the queue. + * + * \see setQueue + * \see clearQueue + */ + void enqueue(const QList<MediaSource> &sources); + + /** + * Appends multiple sources to the queue. + * + * \see setQueue + * \see clearQueue + */ + void enqueue(const QList<QUrl> &urls); + + /** + * Clears the queue of sources. + */ + void clearQueue(); + + /** + * Get the current time (in milliseconds) of the file currently being played. + * + * \return The current time in milliseconds. + * + * \see tick + */ + qint64 currentTime() const; + + /** + * Get the total time (in milliseconds) of the file currently being played. + * + * \return The total time in milliseconds. + * + * \see totalTimeChanged + */ + qint64 totalTime() const; + + /** + * Get the remaining time (in milliseconds) of the file currently being played. + * + * \return The remaining time in milliseconds. + */ + qint64 remainingTime() const; + + qint32 prefinishMark() const; + void setPrefinishMark(qint32 msecToEnd); + + qint32 transitionTime() const; + void setTransitionTime(qint32 msec); + + public Q_SLOTS: + + void setTickInterval(qint32 newTickInterval); + + /** + * Requests playback of the media data to start. Playback only + * starts when stateChanged() signals that it goes into PlayingState, + * though. + * + * \par Possible states right after this call: + * \li BufferingState + * \li PlayingState + * \li ErrorState + */ + void play(); + + /** + * Requests playback to pause. If it was paused before nothing changes. + * + * \par Possible states right after this call: + * \li PlayingState + * \li PausedState + * \li ErrorState + */ + void pause(); + + /** + * Requests playback to stop. If it was stopped before nothing changes. + * + * \par Possible states right after this call: + * \li the state it was in before (e.g. PlayingState) + * \li StoppedState + * \li ErrorState + */ + void stop(); + + /** + * Requests a seek to the time indicated. + * + * You can only seek if state() == PlayingState, BufferingState or PausedState. + * + * The call is asynchronous, so currentTime can still be the old + * value right after this method was called. If all you need is a + * slider that shows the current position and allows the user to + * seek use the class SeekSlider. + * + * @param time The time in milliseconds where to continue playing. + * + * \par Possible states right after this call: + * \li BufferingState + * \li PlayingState + * \li ErrorState + * + * \see SeekSlider + */ + void seek(qint64 time); + + /** + * Stops and removes all playing and enqueued media sources. + * + * \see setCurrentSource + */ + void clear(); + + Q_SIGNALS: + /** + * Emitted when the state of the MediaObject has changed. + * In case you're not interested in the old state you can also + * connect to a slot that only has one State argument. + * + * @param newstate The state the Player is in now. + * @param oldstate The state the Player was in before. + */ + void stateChanged(Phonon::State newstate, Phonon::State oldstate); + + /** + * This signal gets emitted every tickInterval milliseconds. + * + * @param time The position of the media file in milliseconds. + * + * @see setTickInterval, tickInterval + */ + void tick(qint64 time); + + /** + * This signal is emitted whenever the audio/video data that is + * being played is associated with new meta data. E.g. for radio + * streams this happens when the next song is played. + * + * You can get the new meta data with the metaData methods. + */ + void metaDataChanged(); + + /** + * Emitted whenever the return value of isSeekable() changes. + * + * Normally you'll check isSeekable() first and then let this signal + * tell you whether seeking is possible now or not. That way you + * don't have to poll isSeekable(). + * + * \param isSeekable \p true if the stream is seekable (i.e. calling + * seek() works) + * \p false if the stream is not seekable (i.e. + * all calls to seek() will be ignored) + */ + void seekableChanged(bool isSeekable); + + /** + * Emitted whenever the return value of hasVideo() changes. + * + * Normally you'll check hasVideo() first and then let this signal + * tell you whether video is available now or not. That way you + * don't have to poll hasVideo(). + * + * \param hasVideo \p true The stream contains video and adding a + * VideoWidget will show a video. + * \p false There is no video data in the stream and + * adding a VideoWidget will show an empty (black) + * VideoWidget. + */ +#ifndef QT_NO_PHONON_VIDEO + void hasVideoChanged(bool hasVideo); +#endif //QT_NO_PHONON_VIDEO + + /** + * Tells about the status of the buffer. + * + * You can use this signal to show a progress bar to the user when + * in BufferingState: + * + * \code + * progressBar->setRange(0, 100); // this is the default + * connect(media, SIGNAL(bufferStatus(int)), progressBar, SLOT(setValue(int))); + * \endcode + * + * \param percentFilled A number between 0 and 100 telling you how + * much the buffer is filled. + */ // other names: bufferingProgress + void bufferStatus(int percentFilled); + + /** + * Emitted when the object has finished playback. + * It is not emitted if you call stop(), pause() or + * load(), but only on end-of-queue or a critical error. + * + * \warning This signal is not emitted when the current source has + * finished and there's another source in the queue. It is only + * emitted when the queue is empty. + * + * \see currentSourceChanged + * \see aboutToFinish + * \see prefinishMarkReached + */ + void finished(); + + /** + * Emitted when the MediaObject makes a transition to the next + * MediaSource in the queue(). + * + * In other words, it is emitted when an individual MediaSource is + * finished. + * + * \param newSource The source that starts to play at the time the + * signal is emitted. + */ + void currentSourceChanged(const Phonon::MediaSource &newSource); + + /** + * Emitted before the playback of the whole queue stops. When this + * signal is emitted you still have time to provide the next + * MediaSource (using enqueue()) so that playback continues. + * + * This signal can be used to provide the next MediaSource just in + * time for the transition still to work. + * + * \see enqueue + */ + void aboutToFinish(); + + /** + * Emitted when there are only \p msecToEnd milliseconds left + * for playback. + * + * \param msecToEnd The remaining time until the playback queue finishes. + * + * \warning This signal is not emitted when there is another source in the queue. + * It is only emitted when the queue is empty. + * + * \see setPrefinishMark + * \see prefinishMark + * \see aboutToFinish + * \see finished + */ + void prefinishMarkReached(qint32 msecToEnd); + + /** + * This signal is emitted as soon as the total time of the media file is + * known or has changed. For most non-local media data the total + * time of the media can only be known after some time. At that time the + * totalTime function can not return useful information. You have + * to wait for this signal to know the real total time. + * + * \param newTotalTime The length of the media file in milliseconds. + * + * \see totalTime + */ + void totalTimeChanged(qint64 newTotalTime); + + protected: + //MediaObject(Phonon::MediaObjectPrivate &dd, QObject *parent); + + private: + Q_PRIVATE_SLOT(k_func(), void _k_resumePlay()) + Q_PRIVATE_SLOT(k_func(), void _k_resumePause()) + Q_PRIVATE_SLOT(k_func(), void _k_metaDataChanged(const QMultiMap<QString, QString> &)) +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + Q_PRIVATE_SLOT(k_func(), void _k_stateChanged(Phonon::State, Phonon::State)) +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + Q_PRIVATE_SLOT(k_func(), void _k_aboutToFinish()) + Q_PRIVATE_SLOT(k_func(), void _k_currentSourceChanged(const MediaSource &)) + }; + + /** + * Convenience function to create a MediaObject and AudioOutput connected by + * a path. + */ + PHONON_EXPORT MediaObject *createPlayer(Phonon::Category category, const MediaSource &source = MediaSource()); +} //namespace Phonon + +QT_END_NAMESPACE +QT_END_HEADER + +// vim: sw=4 ts=4 tw=80 +#endif // Phonon_MEDIAOBJECT_H diff --git a/src/3rdparty/phonon/phonon/mediaobject_p.h b/src/3rdparty/phonon/phonon/mediaobject_p.h new file mode 100644 index 0000000..5419341 --- /dev/null +++ b/src/3rdparty/phonon/phonon/mediaobject_p.h @@ -0,0 +1,113 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef MEDIAOBJECT_P_H +#define MEDIAOBJECT_P_H + +#include "mediaobject.h" +#include "medianode_p.h" +#include <QtCore/QString> +#include "medianodedestructionhandler_p.h" +#include "mediasource.h" +#include <QtCore/QQueue> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +class KioFallback; +class KioFallbackImpl; +class FrontendInterfacePrivate; + +class MediaObjectPrivate : public MediaNodePrivate, private MediaNodeDestructionHandler +{ + friend class KioFallbackImpl; + friend class AbstractMediaStream; + friend class AbstractMediaStreamPrivate; + Q_DECLARE_PUBLIC(MediaObject) + public: + virtual QObject *qObject() { return q_func(); } + QList<FrontendInterfacePrivate *> interfaceList; + protected: + virtual bool aboutToDeleteBackendObject(); + virtual void createBackendObject(); + virtual void phononObjectDestroyed(MediaNodePrivate *); + PHONON_EXPORT void setupBackendObject(); + + void _k_resumePlay(); + void _k_resumePause(); + void _k_metaDataChanged(const QMultiMap<QString, QString> &); + void _k_aboutToFinish(); + void _k_currentSourceChanged(const MediaSource &); +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + void streamError(Phonon::ErrorType, const QString &); + PHONON_EXPORT void _k_stateChanged(Phonon::State, Phonon::State); +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + + MediaObjectPrivate() + : currentTime(0), + tickInterval(0), + metaData(), + errorString(), + prefinishMark(0), + transitionTime(0), // gapless playback +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + kiofallback(0), +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + state(Phonon::LoadingState) +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + , errorType(Phonon::NormalError), + errorOverride(false), + ignoreLoadingToBufferingStateChange(false), + ignoreErrorToLoadingStateChange(false) +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + { + } + + qint64 currentTime; + qint32 tickInterval; + QMultiMap<QString, QString> metaData; + QString errorString; + qint32 prefinishMark; + qint32 transitionTime; +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + AbstractMediaStream *kiofallback; +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + State state +#ifdef QT_NO_PHONON_ABSTRACTMEDIASTREAM + ; +#else + : 8; + ErrorType errorType : 4; + bool errorOverride : 1; + bool ignoreLoadingToBufferingStateChange : 1; + bool ignoreErrorToLoadingStateChange : 1; +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + MediaSource mediaSource; + QQueue<MediaSource> sourceQueue; +}; +} + +QT_END_NAMESPACE + +#endif // MEDIAOBJECT_P_H +// vim: sw=4 ts=4 tw=80 diff --git a/src/3rdparty/phonon/phonon/mediaobjectinterface.h b/src/3rdparty/phonon/phonon/mediaobjectinterface.h new file mode 100644 index 0000000..a5e2c51 --- /dev/null +++ b/src/3rdparty/phonon/phonon/mediaobjectinterface.h @@ -0,0 +1,242 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_MEDIAOBJECTINTERFACE_H +#define PHONON_MEDIAOBJECTINTERFACE_H + +#include "mediaobject.h" +#include <QtCore/QObject> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +class StreamInterface; + +/** \class MediaObjectInterface mediaobjectinterface.h Phonon/MediaObjectInterface + * \short Backend interface for media sources. + * + * The backend implementation has to provide two signals, that are not defined + * in this interface: + * <ul> + * <li>\anchor phonon_MediaObjectInterface_stateChanged + * <b>void stateChanged(\ref Phonon::State newstate, \ref Phonon::State oldstate)</b> + * + * Emitted when the state of the MediaObject has changed. + * In case you're not interested in the old state you can also + * connect to a slot that only has one State argument. + * + * \param newstate The state the Player is in now. + * \param oldstate The state the Player was in before. + * </li> + * <li>\anchor phonon_MediaObjectInterface_tick + * <b>void tick(qint64 time)</b> + * + * This signal gets emitted every tickInterval milliseconds. + * + * \param time The position of the media file in milliseconds. + * + * \see setTickInterval() + * \see tickInterval() + * </li> + * </ul> + * + * \author Matthias Kretz <kretz@kde.org> + * \see MediaObject + */ +class MediaObjectInterface +{ + public: + virtual ~MediaObjectInterface() {} + + /** + * Requests the playback to start. + * + * This method is only called if the state transition to \ref PlayingState is possible. + * + * The backend should react immediately + * by either going into \ref PlayingState or \ref BufferingState if the + * former is not possible. + */ + virtual void play() = 0; + + /** + * Requests the playback to pause. + * + * This method is only called if the state transition to \ref PausedState is possible. + * + * The backend should react as fast as possible. Go to \ref PausedState + * as soon as playback is paused. + */ + virtual void pause() = 0; + + /** + * Requests the playback to be stopped. + * + * This method is only called if the state transition to \ref StoppedState is possible. + * + * The backend should react as fast as possible. Go to \ref StoppedState + * as soon as playback is stopped. + * + * A subsequent call to play() will start playback at the beginning of + * the media. + */ + virtual void stop() = 0; + + /** + * Requests the playback to be seeked to the given time. + * + * The backend does not have to finish seeking while in this function + * (i.e. the backend does not need to block the thread until the seek is + * finished; even worse it might lead to deadlocks when using a + * ByteStream which gets its data from the thread this function would + * block). + * + * As soon as the seek is done the currentTime() function and + * the tick() signal will report it. + * + * \param milliseconds The time where playback should seek to in + * milliseconds. + */ + virtual void seek(qint64 milliseconds) = 0; + + /** + * Return the time interval in milliseconds between two ticks. + * + * \returns Returns the tick interval that it was set to (might not + * be the same as you asked for). + */ + virtual qint32 tickInterval() const = 0; + /** + * Change the interval the tick signal is emitted. If you set \p + * interval to 0 the signal gets disabled. + * + * \param interval tick interval in milliseconds + * + * \returns Returns the tick interval that it was set to (might not + * be the same as you asked for). + */ + virtual void setTickInterval(qint32 interval) = 0; + + /** + * Check whether the media data includes a video stream. + * + * \return returns \p true if the media contains video data + */ + virtual bool hasVideo() const = 0; + /** + * If the current media may be seeked returns true. + * + * \returns whether the current media may be seeked. + */ + virtual bool isSeekable() const = 0; + /** + * Get the current time (in milliseconds) of the file currently being played. + */ + virtual qint64 currentTime() const = 0; + /** + * Get the current state. + */ + virtual Phonon::State state() const = 0; + + /** + * A translated string describing the error. + */ + virtual QString errorString() const = 0; + + /** + * Tells your program what to do about the error. + * + * \see Phonon::ErrorType + */ + virtual Phonon::ErrorType errorType() const = 0; + + /** + * Returns the total time of the media in milliseconds. + * + * If the total time is not know return -1. Do not block until it is + * known, instead emit the totalTimeChanged signal as soon as the total + * time is known or changes. + */ + virtual qint64 totalTime() const = 0; + + /** + * Returns the current source. + */ + virtual MediaSource source() const = 0; + + /** + * Sets the current source. When this function is called the MediaObject is + * expected to stop all current activity and start loading the new + * source (i.e. go into LoadingState). + * + * It is expected that the + * backend now starts preloading the media data, filling the audio + * and video buffers and making all media meta data available. It + * will also trigger the totalTimeChanged signal. + * + * If the backend does not know how to handle the source it needs to + * change state to Phonon::ErrorState. Don't bother about handling KIO + * URLs. It is enough to handle AbstractMediaStream sources correctly. + * + * \warning Keep the MediaSource object around as long as the backend + * uses the AbstractMediaStream returned by the MediaSource. In case + * that no other reference to the MediaSource exists and it is set to + * MediaSource::autoDelete, the AbstractMediaStream is deleted when the + * last MediaSource ref is deleted. + */ + virtual void setSource(const MediaSource &) = 0; + + /** + * Sets the next source to be used for transitions. When a next source + * is set playback should continue with the new source. In that case + * finished and prefinishMarkReached are not emitted. + * + * \param source The source to transition to (crossfade/gapless/gap). If + * \p source is an invalid MediaSource object then the queue is empty + * and the playback should stop normally. + * + * \warning Keep the MediaSource object around as long as the backend + * uses the AbstractMediaStream returned by the MediaSource. In case + * that no other reference to the MediaSource exists and it is set to + * MediaSource::autoDelete, the AbstractMediaStream is deleted when the + * last MediaSource ref is deleted. + */ + virtual void setNextSource(const MediaSource &source) = 0; + + virtual qint64 remainingTime() const { return totalTime() - currentTime(); } + virtual qint32 prefinishMark() const = 0; + virtual void setPrefinishMark(qint32) = 0; + + virtual qint32 transitionTime() const = 0; + virtual void setTransitionTime(qint32) = 0; +}; +} + +Q_DECLARE_INTERFACE(Phonon::MediaObjectInterface, "MediaObjectInterface3.phonon.kde.org") + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // PHONON_MEDIAOBJECTINTERFACE_H +// vim: sw=4 ts=4 tw=80 diff --git a/src/3rdparty/phonon/phonon/mediasource.cpp b/src/3rdparty/phonon/phonon/mediasource.cpp new file mode 100644 index 0000000..0a21c87 --- /dev/null +++ b/src/3rdparty/phonon/phonon/mediasource.cpp @@ -0,0 +1,232 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#include "mediasource.h" +#include "mediasource_p.h" +#include "iodevicestream_p.h" +#include "abstractmediastream_p.h" + +#include <QtCore/QFileInfo> +#include <QtCore/QFile> +#include <QtCore/QFSFileEngine> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + +MediaSource::MediaSource(MediaSourcePrivate &dd) + : d(&dd) +{ +} + +MediaSource::MediaSource() + : d(new MediaSourcePrivate(Empty)) +{ +} + +MediaSource::MediaSource(const QString &filename) + : d(new MediaSourcePrivate(LocalFile)) +{ + const QFileInfo fileInfo(filename); + if (fileInfo.exists()) { + bool localFs = QAbstractFileEngine::LocalDiskFlag & QFSFileEngine(filename).fileFlags(QAbstractFileEngine::LocalDiskFlag); + if (localFs) { + d->url = QUrl::fromLocalFile(fileInfo.absoluteFilePath()); + } else { +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + // it's a Qt resource -> use QFile + d->type = Stream; + d->ioDevice = new QFile(filename); + d->setStream(new IODeviceStream(d->ioDevice, d->ioDevice)); +#else + d->type = Invalid; +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + } + } else { + d->url = filename; + if (d->url.isValid()) { + d->type = Url; + } else { + d->type = Invalid; + } + } +} + +MediaSource::MediaSource(const QUrl &url) + : d(new MediaSourcePrivate(Url)) +{ + if (url.isValid()) { + d->url = url; + } else { + d->type = Invalid; + } +} + +MediaSource::MediaSource(Phonon::DiscType dt, const QString &deviceName) + : d(new MediaSourcePrivate(Disc)) +{ + if (dt == NoDisc) { + d->type = Invalid; + return; + } + d->discType = dt; + d->deviceName = deviceName; +} + +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM +MediaSource::MediaSource(AbstractMediaStream *stream) + : d(new MediaSourcePrivate(Stream)) +{ + if (stream) { + d->setStream(stream); + } else { + d->type = Invalid; + } +} + +MediaSource::MediaSource(QIODevice *ioDevice) + : d(new MediaSourcePrivate(Stream)) +{ + if (ioDevice) { + d->setStream(new IODeviceStream(ioDevice, ioDevice)); + d->ioDevice = ioDevice; + } else { + d->type = Invalid; + } +} +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + +/* post 4.0 +MediaSource::MediaSource(const QList<MediaSource> &mediaList) + : d(new MediaSourcePrivate(Link)) +{ + d->linkedSources = mediaList; + foreach (MediaSource ms, mediaList) { + Q_ASSERT(ms.type() != Link); + } +} + +QList<MediaSource> MediaSource::substreams() const +{ + return d->linkedSources; +} +*/ + +MediaSource::~MediaSource() +{ +} + +MediaSourcePrivate::~MediaSourcePrivate() +{ +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + if (autoDelete) { + delete stream; + delete ioDevice; + } +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM +} + +MediaSource::MediaSource(const MediaSource &rhs) + : d(rhs.d) +{ +} + +MediaSource &MediaSource::operator=(const MediaSource &rhs) +{ + d = rhs.d; + return *this; +} + +bool MediaSource::operator==(const MediaSource &rhs) const +{ + return d == rhs.d; +} + +void MediaSource::setAutoDelete(bool autoDelete) +{ + d->autoDelete = autoDelete; +} + +bool MediaSource::autoDelete() const +{ + return d->autoDelete; +} + +MediaSource::Type MediaSource::type() const +{ +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + if (d->type == Stream && d->stream == 0) { + return Invalid; + } +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + return d->type; +} + +QString MediaSource::fileName() const +{ + return d->url.toLocalFile(); +} + +QUrl MediaSource::url() const +{ + return d->url; +} + +Phonon::DiscType MediaSource::discType() const +{ + return d->discType; +} + +QString MediaSource::deviceName() const +{ + return d->deviceName; +} + +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM +AbstractMediaStream *MediaSource::stream() const +{ + return d->stream; +} + +void MediaSourcePrivate::setStream(AbstractMediaStream *s) +{ + stream = s; +} +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + + +//X AudioCaptureDevice MediaSource::audioCaptureDevice() const +//X { +//X return d->audioCaptureDevice; +//X } +//X +//X VideoCaptureDevice MediaSource::videoCaptureDevice() const +//X { +//X return d->videoCaptureDevice; +//X } + +} // namespace Phonon + +QT_END_NAMESPACE + +// vim: sw=4 sts=4 et tw=100 diff --git a/src/3rdparty/phonon/phonon/mediasource.h b/src/3rdparty/phonon/phonon/mediasource.h new file mode 100644 index 0000000..da010d9 --- /dev/null +++ b/src/3rdparty/phonon/phonon/mediasource.h @@ -0,0 +1,279 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_MEDIASOURCE_H +#define PHONON_MEDIASOURCE_H + +#include "phonon_export.h" +#include "phononnamespace.h" +#include "objectdescription.h" +#include <QtCore/QSharedData> +#include <QtCore/QString> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +class QUrl; +class QIODevice; + +namespace Phonon +{ + +class MediaSourcePrivate; +class AbstractMediaStream; + +/** \class MediaSource mediasource.h Phonon/MediaSource + * Note that all constructors of this class are implicit, so that you can simply write + * \code + * MediaObject m; + * QString fileName("/home/foo/bar.ogg"); + * QUrl url("http://www.example.com/stream.mp3"); + * QBuffer *someBuffer; + * m.setCurrentSource(fileName); + * m.setCurrentSource(url); + * m.setCurrentSource(someBuffer); + * m.setCurrentSource(Phonon::Cd); + * \endcode + * + * \ingroup Playback + * \ingroup Recording + * \author Matthias Kretz <kretz@kde.org> + */ +class PHONON_EXPORT MediaSource +{ + friend class StreamInterface; + public: + /** + * Identifies the type of media described by the MediaSource object. + * + * \see MediaSource::type() + */ + enum Type { + /** + * The MediaSource object does not describe any valid source. + */ + Invalid = -1, + /** + * The MediaSource object describes a local file. + */ + LocalFile, + /** + * The MediaSource object describes a URL, which can be both a local file and a file on + * the network. + */ + Url, + /** + * The MediaSource object describes a disc. + */ + Disc, + /** + * The MediaSource object describes a data stream. + * + * This is also the type used for QIODevices. + * + * \see AbstractMediaStream + */ + Stream, + /** + * An empty MediaSource. + * + * It can be used to unload the current media from a MediaObject. + * + * \see MediaSource() + */ + Empty +/* post 4.0: + / ** + * Links multiple MediaSource objects together. + * / + Link +*/ + }; + + /** + * Creates an empty MediaSource. + * + * An empty MediaSource is considered valid and can be set on a MediaObject to unload its + * current media. + * + * \see Empty + */ + MediaSource(); + + /** + * Creates a MediaSource object for a local file or a Qt resource. + * + * \param fileName file name of a local media file or a Qt resource that was compiled in. + */ + MediaSource(const QString &fileName); //krazy:exclude=explicit + + /** + * Creates a MediaSource object for a URL. + * + * \param url URL to a media file or stream. + */ + MediaSource(const QUrl &url); //krazy:exclude=explicit + + /** + * Creates a MediaSource object for discs. + * + * \param discType See \ref DiscType + * \param deviceName A platform dependent device name. This can be useful if the computer + * has more than one CD drive. It is recommended to use Solid to retrieve the device name in + * a portable way. + */ + MediaSource(Phonon::DiscType discType, const QString &deviceName = QString()); //krazy:exclude=explicit + +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + /** + * Creates a MediaSource object for a data stream. + * + * Your application can provide the media data by subclassing AbstractMediaStream and + * passing a pointer to that object. %Phonon will never delete the \p stream. + * + * \param stream The AbstractMediaStream subclass to provide the media data. + * + * \see setAutoDelete + */ + MediaSource(AbstractMediaStream *stream); //krazy:exclude=explicit + + /** + * Creates a MediaSource object for a QIODevice. + * + * This constructor can be very handy in the combination of QByteArray and QBuffer. + * + * \param ioDevice An arbitrary readable QIODevice subclass. If the device is not opened + * MediaSource will open it as QIODevice::ReadOnly. Sequential I/O devices are possible, + * too. For those MediaObject::isSeekable() will have to return false obviously. + * + * \see setAutoDelete + */ + MediaSource(QIODevice *ioDevice); //krazy:exclude=explicit +#endif + + /** + * Creates a MediaSource object for capture devices. + */ + //MediaSource(const AudioCaptureDevice &, const VideoCaptureDevice &); + + /** + * Destroys the MediaSource object. + */ + ~MediaSource(); + + /** + * Constructs a copy of \p rhs. + * + * This constructor is fast thanks to explicit sharing. + */ + MediaSource(const MediaSource &rhs); + + /** + * Assigns \p rhs to this MediaSource and returns a reference to this MediaSource. + * + * This operation is fast thanks to explicit sharing. + */ + MediaSource &operator=(const MediaSource &rhs); + + /** + * Returns \p true if this MediaSource is equal to \p rhs; otherwise returns \p false. + */ + bool operator==(const MediaSource &rhs) const; + + /** + * Tell the MediaSource to take ownership of the AbstractMediaStream or QIODevice that was + * passed in the constructor. + * + * The default setting is \p false, for safety. If you turn it on, you should only access + * the AbstractMediaStream/QIODevice object as long as you yourself keep a MediaSource + * object around. As long as you keep the MediaSource object wrapping the stream/device + * the object will not get deleted. + * + * \see autoDelete + */ + void setAutoDelete(bool enable); + + /** + * Returns the setting of the auto-delete option. The default is \p false. + * + * \see setAutoDelete + */ + bool autoDelete() const; + + /** + * Returns the type of the MediaSource (depends on the constructor that was used). + * + * \see Type + */ + Type type() const; + + /** + * Returns the file name of the MediaSource if type() == LocalFile; otherwise returns + * QString(). + */ + QString fileName() const; + + /** + * Returns the url of the MediaSource if type() == URL or type() == LocalFile; otherwise + * returns QUrl(). + */ + QUrl url() const; + + /** + * Returns the disc type of the MediaSource if type() == Disc; otherwise returns \ref + * NoDisc. + */ + Phonon::DiscType discType() const; + + /** + * Returns the device name of the MediaSource if type() == Disc; otherwise returns + * QString(). + */ + QString deviceName() const; + +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + /** + * Returns the media stream of the MediaSource if type() == Stream; otherwise returns 0. + * QIODevices are handled as streams, too. + */ + AbstractMediaStream *stream() const; +#endif + + //AudioCaptureDevice audioCaptureDevice() const; + //VideoCaptureDevice videoCaptureDevice() const; + +/* post 4.0: + MediaSource(const QList<MediaSource> &mediaList); + QList<MediaSource> substreams() const; +*/ + + protected: + QExplicitlySharedDataPointer<MediaSourcePrivate> d; + MediaSource(MediaSourcePrivate &); +}; + +} // namespace Phonon + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // PHONON_MEDIASOURCE_H diff --git a/src/3rdparty/phonon/phonon/mediasource_p.h b/src/3rdparty/phonon/phonon/mediasource_p.h new file mode 100644 index 0000000..bfac7ad --- /dev/null +++ b/src/3rdparty/phonon/phonon/mediasource_p.h @@ -0,0 +1,89 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef MEDIASOURCE_P_H +#define MEDIASOURCE_P_H + +#include "mediasource.h" +#include "objectdescription.h" +#include "abstractmediastream.h" + +#include <QtCore/QUrl> +#include <QtCore/QString> +#include <QtCore/QSharedData> +#include <QtCore/QPointer> + +QT_BEGIN_NAMESPACE + +class QFile; + +namespace Phonon +{ + +class PHONON_EXPORT MediaSourcePrivate : public QSharedData +{ + public: + MediaSourcePrivate(MediaSource::Type t) + : type(t), discType(NoDisc), +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + stream(0), + ioDevice(0), +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + autoDelete(false) + { + } + + virtual ~MediaSourcePrivate(); + +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + void setStream(AbstractMediaStream *s); +#endif + + MediaSource::Type type; + QUrl url; + Phonon::DiscType discType; + QString deviceName; + +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + // The AbstractMediaStream(2) may be deleted at any time by the application. If that happens + // stream will be 0 automatically, but streamEventQueue will stay valid as we hold a + // reference to it. This is necessary to avoid a races when setting the MediaSource while + // another thread deletes the AbstractMediaStream2. StreamInterface(2) will then just get a + // StreamEventQueue where nobody answers. + QPointer<AbstractMediaStream> stream; + +// AudioCaptureDevice audioCaptureDevice; +// VideoCaptureDevice videoCaptureDevice; + QIODevice *ioDevice; +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + //QList<MediaSource> linkedSources; + bool autoDelete; +}; + +} // namespace Phonon + +QT_END_NAMESPACE + +#endif // MEDIASOURCE_P_H +// vim: sw=4 sts=4 et tw=100 + + diff --git a/src/3rdparty/phonon/phonon/objectdescription.cpp b/src/3rdparty/phonon/phonon/objectdescription.cpp new file mode 100644 index 0000000..3296792 --- /dev/null +++ b/src/3rdparty/phonon/phonon/objectdescription.cpp @@ -0,0 +1,140 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2008 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#include "objectdescription.h" +#include "objectdescription_p.h" + +#include <QtCore/QObject> +#include <QtCore/QSet> +#include "factory_p.h" +#include <QtCore/QStringList> +#include "backendinterface.h" +#include "platformplugin.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + +ObjectDescriptionData::ObjectDescriptionData(int index, const QHash<QByteArray, QVariant> &properties) + : d(new ObjectDescriptionPrivate(index, properties)) +{ +} + +ObjectDescriptionData::ObjectDescriptionData(ObjectDescriptionPrivate *dd) + : d(dd) +{ +} + +ObjectDescriptionData::~ObjectDescriptionData() +{ + delete d; +} + +bool ObjectDescriptionData::operator==(const ObjectDescriptionData &otherDescription) const +{ + if (!isValid()) { + return !otherDescription.isValid(); + } + if (!otherDescription.isValid()) { + return false; + } + return *d == *otherDescription.d; +} + +int ObjectDescriptionData::index() const +{ + if (!isValid()) { + return -1; + } + return d->index; +} + +QString ObjectDescriptionData::name() const +{ + if (!isValid()) { + return QString(); + } + return d->name; +} + +QString ObjectDescriptionData::description() const +{ + if (!isValid()) { + return QString(); + } + return d->description; +} + +QVariant ObjectDescriptionData::property(const char *name) const +{ + if (!isValid()) { + return QVariant(); + } + return d->properties.value(name); +} + +QList<QByteArray> ObjectDescriptionData::propertyNames() const +{ + if (!isValid()) { + return QList<QByteArray>(); + } + return d->properties.keys(); +} + +bool ObjectDescriptionData::isValid() const +{ + return d != 0; +} + +ObjectDescriptionData *ObjectDescriptionData::fromIndex(ObjectDescriptionType type, int index) +{ + // prefer to get the ObjectDescriptionData from the platform plugin for audio devices +#ifndef QT_NO_PHONON_PLATFORMPLUGIN + if (type == AudioOutputDeviceType || type == AudioCaptureDeviceType) { + PlatformPlugin *platformPlugin = Factory::platformPlugin(); + if (platformPlugin) { + QList<int> indexes = platformPlugin->objectDescriptionIndexes(type); + if (indexes.contains(index)) { + QHash<QByteArray, QVariant> properties = platformPlugin->objectDescriptionProperties(type, index); + return new ObjectDescriptionData(index, properties); + } + } + } +#endif //QT_NO_PHONON_PLATFORMPLUGIN + + QObject *b = Factory::backend(); + BackendInterface *iface = qobject_cast<BackendInterface *>(b); + if (iface) { + QList<int> indexes = iface->objectDescriptionIndexes(type); + if (indexes.contains(index)) { + QHash<QByteArray, QVariant> properties = iface->objectDescriptionProperties(type, index); + return new ObjectDescriptionData(index, properties); + } + } + return new ObjectDescriptionData(0); // invalid +} + +} //namespace Phonon + +QT_END_NAMESPACE +// vim: sw=4 ts=4 diff --git a/src/3rdparty/phonon/phonon/objectdescription.h b/src/3rdparty/phonon/phonon/objectdescription.h new file mode 100644 index 0000000..108f02c --- /dev/null +++ b/src/3rdparty/phonon/phonon/objectdescription.h @@ -0,0 +1,342 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_OBJECTDESCRIPTION_H +#define PHONON_OBJECTDESCRIPTION_H + +#include "phonon_export.h" + +#include <QtCore/QExplicitlySharedDataPointer> +#include <QtCore/QtDebug> +#include <QtCore/QList> +#include <QtCore/QSharedData> +#include <QtCore/QString> +#include <QtCore/QVariant> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + class ObjectDescriptionPrivate; + + /** + * Defines the type of information that is contained in a ObjectDescription + * object. + * + * \ingroup Backend + */ + enum ObjectDescriptionType + { + /** + * Audio output devices. This can be soundcards (with different drivers), soundservers or + * other virtual outputs like playback on a different computer on the + * network. + * + * For Hardware devices the backend should use libkaudiodevicelist + * (AudioDevice and AudioDeviceEnumerator) which will list removable + * devices even when they are unplugged and provide a unique identifier + * that can make backends use the same identifiers. + */ + AudioOutputDeviceType, + + /** + * Lists all processing effects the backend supports. + */ + EffectType, + AudioChannelType, + SubtitleType, + + /** + * Audio capture devices. This can be soundcards (with different drivers), soundservers or + * other virtual inputs like capture on a different computer on the + * network. + * + * For Hardware devices the backend should use libkaudiodevicelist + * (AudioDevice and AudioDeviceEnumerator) which will list removable + * devices even when they are unplugged and provide a unique identifier + * that can make backends use the same identifiers. + */ + AudioCaptureDeviceType + + //VideoOutputDeviceType, + //VideoCaptureDeviceType, + //AudioCodecType, + //VideoCodecType, + //ContainerFormatType, + //VisualizationType, + }; + +/** \internal + * \class ObjectDescriptionData objectdescription.h Phonon/ObjectDescription + * \brief Data class for objects describing devices or features of the backend. + * + * \author Matthias Kretz <kretz@kde.org> + * \see BackendCapabilities + */ +class PHONON_EXPORT ObjectDescriptionData : public QSharedData //krazy:exclude=dpointer (it's protected, which should be fine for this type of class) +{ + public: + /** + * Returns \c true if this ObjectDescription describes the same + * as \p otherDescription; otherwise returns \c false. + */ + bool operator==(const ObjectDescriptionData &otherDescription) const; + + /** + * Returns the name of the capture source. + * + * \return A string that should be presented to the user to + * choose the capture source. + */ + QString name() const; + + /** + * Returns a description of the capture source. This text should + * make clear what sound source this is, which is sometimes hard + * to describe or understand from just the name. + * + * \return A string describing the capture source. + */ + QString description() const; + + /** + * Returns a named property. + * + * If the property is not set an invalid value is returned. + * + * \see propertyNames() + */ + QVariant property(const char *name) const; + + /** + * Returns all names that return valid data when property() is called. + * + * \see property() + */ + QList<QByteArray> propertyNames() const; + + /** + * Returns \c true if the Tuple is valid (index != -1); otherwise returns + * \c false. + */ + bool isValid() const; + + /** + * A unique identifier for this device/. Used internally + * to distinguish between the devices/. + * + * \return An integer that uniquely identifies every device/ + */ + int index() const; + + static ObjectDescriptionData *fromIndex(ObjectDescriptionType type, int index); + + ~ObjectDescriptionData(); + + ObjectDescriptionData(ObjectDescriptionPrivate * = 0); + ObjectDescriptionData(int index, const QHash<QByteArray, QVariant> &properties); + + protected: + ObjectDescriptionPrivate *const d; + + private: + ObjectDescriptionData &operator=(const ObjectDescriptionData &rhs); +}; + +template<ObjectDescriptionType T> class ObjectDescriptionModel; + +/** \class ObjectDescription objectdescription.h Phonon/ObjectDescription + * \short Provides a tuple of enduser visible name and description. + * + * Some parts give the enduser choices, e.g. what source to capture audio from. + * These choices are described by the name and description methods of this class + * and identified with the id method. Subclasses then define additional + * information like which audio and video choices belong together. + * + * \ingroup Frontend + * \author Matthias Kretz <kretz@kde.org> + */ +template<ObjectDescriptionType T> +class ObjectDescription +{ + public: + /** + * Returns a new description object that describes the + * device/effect/codec/... with the given \p index. + */ + static inline ObjectDescription<T> fromIndex(int index) { //krazy:exclude=inline + return ObjectDescription<T>(QExplicitlySharedDataPointer<ObjectDescriptionData>(ObjectDescriptionData::fromIndex(T, index))); + } + + /** + * Returns \c true if this ObjectDescription describes the same + * as \p otherDescription; otherwise returns \c false. + */ + inline bool operator==(const ObjectDescription &otherDescription) const { //krazy:exclude=inline + return *d == *otherDescription.d; + } + + /** + * Returns \c false if this ObjectDescription describes the same + * as \p otherDescription; otherwise returns \c true. + */ + inline bool operator!=(const ObjectDescription &otherDescription) const { //krazy:exclude=inline + return !operator==(otherDescription); + } + + /** + * Returns the name of the capture source. + * + * \return A string that should be presented to the user to + * choose the capture source. + */ + inline QString name() const { return d->name(); } //krazy:exclude=inline + + /** + * Returns a description of the capture source. This text should + * make clear what sound source this is, which is sometimes hard + * to describe or understand from just the name. + * + * \return A string describing the capture source. + */ + inline QString description() const { return d->description(); } //krazy:exclude=inline + + /** + * Returns a named property. + * + * If the property is not set an invalid value is returned. + * + * \see propertyNames() + */ + inline QVariant property(const char *name) const { return d->property(name); } //krazy:exclude=inline + + /** + * Returns all names that return valid data when property() is called. + * + * \see property() + */ + inline QList<QByteArray> propertyNames() const { return d->propertyNames(); } //krazy:exclude=inline + + /** + * Returns \c true if the Tuple is valid (index != -1); otherwise returns + * \c false. + */ + inline bool isValid() const { return d->isValid(); } //krazy:exclude=inline + + /** + * A unique identifier for this device/. Used internally + * to distinguish between the devices/. + * + * \return An integer that uniquely identifies every device/ + */ + inline int index() const { return d->index(); } //krazy:exclude=inline + + ObjectDescription() : d(new ObjectDescriptionData(0)) {} + ObjectDescription(int index, const QHash<QByteArray, QVariant> &properties) : d(new ObjectDescriptionData(index, properties)) {} + + protected: + friend class ObjectDescriptionModel<T>; + ObjectDescription(const QExplicitlySharedDataPointer<ObjectDescriptionData> &dd) : d(dd) {} + QExplicitlySharedDataPointer<ObjectDescriptionData> d; +}; + +template<ObjectDescriptionType T> +inline QDebug operator<<(QDebug s, const ObjectDescription<T> &o) //krazy:exclude=inline +{ + return s << o.name(); +} + +/** + * \ingroup BackendInformation + */ +typedef ObjectDescription<AudioOutputDeviceType> AudioOutputDevice; +/** + * \ingroup BackendInformation + */ +#ifndef QT_NO_PHONON_AUDIOCAPTURE +typedef ObjectDescription<AudioCaptureDeviceType> AudioCaptureDevice; +#endif //QT_NO_PHONON_AUDIOCAPTURE +/** + * \ingroup BackendInformation + */ +//typedef ObjectDescription<VideoOutputDeviceType> VideoOutputDevice; +/** + * \ingroup BackendInformation + */ +//typedef ObjectDescription<VideoCaptureDeviceType> VideoCaptureDevice; +/** + * \ingroup BackendInformation + */ +#ifndef QT_NO_PHONON_EFFECT +typedef ObjectDescription<EffectType> EffectDescription; +#endif //QT_NO_PHONON_EFFECT + +/** + * \ingroup BackendInformation + */ +//typedef ObjectDescription<AudioCodecType> AudioCodecDescription; +/** + * \ingroup BackendInformation + */ +//typedef ObjectDescription<VideoCodecType> VideoCodecDescription; +/** + * \ingroup BackendInformation + */ +//typedef ObjectDescription<ContainerFormatType> ContainerFormatDescription; +/** + * \ingroup BackendInformation + */ +//typedef ObjectDescription<VisualizationType> VisualizationDescription; +#ifndef QT_NO_PHONON_MEDIACONTROLLER +typedef ObjectDescription<AudioChannelType> AudioChannelDescription; +typedef ObjectDescription<SubtitleType> SubtitleDescription; +#endif //QT_NO_PHONON_MEDIACONTROLLER + +} //namespace Phonon + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(Phonon::AudioOutputDevice) +Q_DECLARE_METATYPE(QList<Phonon::AudioOutputDevice>) + +#ifndef QT_NO_PHONON_AUDIOCAPTURE +Q_DECLARE_METATYPE(Phonon::AudioCaptureDevice) +Q_DECLARE_METATYPE(QList<Phonon::AudioCaptureDevice>) +#endif //QT_NO_PHONON_AUDIOCAPTURE + +#ifndef QT_NO_PHONON_EFFECT +Q_DECLARE_METATYPE(QList<Phonon::EffectDescription>) +Q_DECLARE_METATYPE(Phonon::EffectDescription) +#endif //QT_NO_PHONON_EFFECT + + +#ifndef QT_NO_PHONON_MEDIACONTROLLER +Q_DECLARE_METATYPE(Phonon::AudioChannelDescription) +Q_DECLARE_METATYPE(Phonon::SubtitleDescription) +Q_DECLARE_METATYPE(QList<Phonon::AudioChannelDescription>) +Q_DECLARE_METATYPE(QList<Phonon::SubtitleDescription>) +#endif //QT_NO_PHONON_MEDIACONTROLLER + +QT_END_HEADER + +#endif // PHONON_OBJECTDESCRIPTION_H diff --git a/src/3rdparty/phonon/phonon/objectdescription_p.h b/src/3rdparty/phonon/phonon/objectdescription_p.h new file mode 100644 index 0000000..1069f11 --- /dev/null +++ b/src/3rdparty/phonon/phonon/objectdescription_p.h @@ -0,0 +1,64 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_OBJECTDESCRIPTION_P_H +#define PHONON_OBJECTDESCRIPTION_P_H + +#include <QtCore/QByteRef> +#include <QtCore/QHash> +#include <QtCore/QString> +#include <QtCore/QVariant> +#include "phononnamespace_p.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + class ObjectDescriptionPrivate + { + public: + ObjectDescriptionPrivate(int _index, const QHash<QByteArray, QVariant> &_properties) + : index(_index), + name(_properties["name"].toString()), + description(_properties["description"].toString()), + properties(_properties) + { + } + + bool operator==(const ObjectDescriptionPrivate &rhs) const + { + if (index == rhs.index && (name != rhs.name || description != rhs.description)) + pError() << "Same index (" << index << + "), but different name/description. This is a bug in the Phonon backend."; + return index == rhs.index;// && name == rhs.name && description == rhs.description; + } + + int index; + QString name, description; + QHash<QByteArray, QVariant> properties; + }; +} // namespace Phonon + +QT_END_NAMESPACE + +#endif // PHONON_OBJECTDESCRIPTION_P_H +// vim: sw=4 ts=4 tw=80 diff --git a/src/3rdparty/phonon/phonon/objectdescriptionmodel.cpp b/src/3rdparty/phonon/phonon/objectdescriptionmodel.cpp new file mode 100644 index 0000000..e989d0c --- /dev/null +++ b/src/3rdparty/phonon/phonon/objectdescriptionmodel.cpp @@ -0,0 +1,392 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#include "objectdescriptionmodel.h" +#include "objectdescriptionmodel_p.h" +#include "phonondefs_p.h" +#include "platform_p.h" +#include <QtCore/QList> +#include "objectdescription.h" +#include "phononnamespace_p.h" +#include <QtCore/QMimeData> +#include <QtCore/QStringList> +#include <QtGui/QIcon> +#include "factory_p.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_OBJECTDESCRIPTIONMODEL + + +static const uint qt_meta_data_Phonon__ObjectDescriptionModel[] = { + + // content: + 1, // revision + 0, // classname + 0, 0, // classinfo + 0, 0, // methods + 0, 0, // properties + 0, 0, // enums/sets + + 0 // eod +}; + +static const char qt_meta_stringdata_Phonon__ObjectDescriptionModel_AudioOutputDeviceType[] = { "Phonon::AudioOutputDevice\0" }; +static const char qt_meta_stringdata_Phonon__ObjectDescriptionModel_AudioCaptureDeviceType[] = { "Phonon::AudioCaptureDevice\0" }; +static const char qt_meta_stringdata_Phonon__ObjectDescriptionModel_EffectType[] = { "Phonon::EffectDescription\0" }; +static const char qt_meta_stringdata_Phonon__ObjectDescriptionModel_AudioChannelType[] = { "Phonon::AudioChannelDescription\0" }; +static const char qt_meta_stringdata_Phonon__ObjectDescriptionModel_SubtitleType[] = { "Phonon::SubtitleDescription\0" }; +/* +static const char qt_meta_stringdata_Phonon__ObjectDescriptionModel_VideoOutputDeviceType[] = { "Phonon::VideoOutputDevice\0" }; +static const char qt_meta_stringdata_Phonon__ObjectDescriptionModel_VideoCaptureDeviceType[] = { "Phonon::VideoCaptureDevice\0" }; +static const char qt_meta_stringdata_Phonon__ObjectDescriptionModel_AudioCodecType[] = { "Phonon::AudioCodecDescription\0" }; +static const char qt_meta_stringdata_Phonon__ObjectDescriptionModel_VideoCodecType[] = { "Phonon::VideoCodecDescription\0" }; +static const char qt_meta_stringdata_Phonon__ObjectDescriptionModel_ContainerFormatType[] = { "Phonon::ContainerFormatDescription\0" }; +static const char qt_meta_stringdata_Phonon__ObjectDescriptionModel_VisualizationType[] = { "Phonon::VisualizationDescription\0" }; +*/ + +namespace Phonon +{ + +#if !defined(Q_CC_MINGW) || __MINGW32_MAJOR_VERSION >= 4 + +template<> const QMetaObject ObjectDescriptionModel<AudioOutputDeviceType>::staticMetaObject = { + { &QAbstractListModel::staticMetaObject, qt_meta_stringdata_Phonon__ObjectDescriptionModel_AudioOutputDeviceType, + qt_meta_data_Phonon__ObjectDescriptionModel, 0 } +}; +template<> const QMetaObject ObjectDescriptionModel<EffectType>::staticMetaObject = { + { &QAbstractListModel::staticMetaObject, qt_meta_stringdata_Phonon__ObjectDescriptionModel_EffectType, + qt_meta_data_Phonon__ObjectDescriptionModel, 0 } +}; +template<> const QMetaObject ObjectDescriptionModel<AudioChannelType>::staticMetaObject = { + { &QAbstractListModel::staticMetaObject, qt_meta_stringdata_Phonon__ObjectDescriptionModel_AudioChannelType, + qt_meta_data_Phonon__ObjectDescriptionModel, 0 } +}; +template<> const QMetaObject ObjectDescriptionModel<SubtitleType>::staticMetaObject = { + { &QAbstractListModel::staticMetaObject, qt_meta_stringdata_Phonon__ObjectDescriptionModel_SubtitleType, + qt_meta_data_Phonon__ObjectDescriptionModel, 0 } +}; +template<> const QMetaObject ObjectDescriptionModel<AudioCaptureDeviceType>::staticMetaObject = { + { &QAbstractListModel::staticMetaObject, qt_meta_stringdata_Phonon__ObjectDescriptionModel_AudioCaptureDeviceType, + qt_meta_data_Phonon__ObjectDescriptionModel, 0 } +}; +/*template<> const QMetaObject ObjectDescriptionModel<VideoOutputDeviceType>::staticMetaObject = { + { &QAbstractListModel::staticMetaObject, qt_meta_stringdata_Phonon__ObjectDescriptionModel_VideoOutputDeviceType, + qt_meta_data_Phonon__ObjectDescriptionModel, 0 } +}; +template<> const QMetaObject ObjectDescriptionModel<VideoCaptureDeviceType>::staticMetaObject = { + { &QAbstractListModel::staticMetaObject, qt_meta_stringdata_Phonon__ObjectDescriptionModel_VideoCaptureDeviceType, + qt_meta_data_Phonon__ObjectDescriptionModel, 0 } +}; +template<> const QMetaObject ObjectDescriptionModel<AudioCodecType>::staticMetaObject = { + { &QAbstractListModel::staticMetaObject, qt_meta_stringdata_Phonon__ObjectDescriptionModel_AudioCodecType, + qt_meta_data_Phonon__ObjectDescriptionModel, 0 } +}; +template<> const QMetaObject ObjectDescriptionModel<VideoCodecType>::staticMetaObject = { + { &QAbstractListModel::staticMetaObject, qt_meta_stringdata_Phonon__ObjectDescriptionModel_VideoCodecType, + qt_meta_data_Phonon__ObjectDescriptionModel, 0 } +}; +template<> const QMetaObject ObjectDescriptionModel<ContainerFormatType>::staticMetaObject = { + { &QAbstractListModel::staticMetaObject, qt_meta_stringdata_Phonon__ObjectDescriptionModel_ContainerFormatType, + qt_meta_data_Phonon__ObjectDescriptionModel, 0 } +}; +template<> const QMetaObject ObjectDescriptionModel<VisualizationType>::staticMetaObject = { + { &QAbstractListModel::staticMetaObject, qt_meta_stringdata_Phonon__ObjectDescriptionModel_VisualizationType, + qt_meta_data_Phonon__ObjectDescriptionModel, 0 } +};*/ + +template<ObjectDescriptionType type> +const QMetaObject *ObjectDescriptionModel<type>::metaObject() const +{ + return &staticMetaObject; +} + +template<ObjectDescriptionType type> +void *ObjectDescriptionModel<type>::qt_metacast(const char *_clname) +{ + if (!_clname) { + return 0; + } + if (!strcmp(_clname, ObjectDescriptionModel<type>::staticMetaObject.className())) { + return static_cast<void *>(const_cast<ObjectDescriptionModel<type> *>(this)); + } + return QAbstractListModel::qt_metacast(_clname); +} + +/* +template<ObjectDescriptionType type> +int ObjectDescriptionModel<type>::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + return QAbstractListModel::qt_metacall(_c, _id, _a); +} +*/ +#endif + +int ObjectDescriptionModelData::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + return d->data.size(); +} + +QVariant ObjectDescriptionModelData::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() >= d->data.size() || index.column() != 0) + return QVariant(); + + switch(role) + { + case Qt::EditRole: + case Qt::DisplayRole: + return d->data.at(index.row())->name(); + break; + case Qt::ToolTipRole: + return d->data.at(index.row())->description(); + break; + case Qt::DecorationRole: + { + QVariant icon = d->data.at(index.row())->property("icon"); + if (icon.isValid()) { + if (icon.type() == QVariant::String) { + return Platform::icon(icon.toString()); + } else if (icon.type() == QVariant::Icon) { + return icon; + } + } + } + return QVariant(); + default: + return QVariant(); +} +} + +Qt::ItemFlags ObjectDescriptionModelData::flags(const QModelIndex &index) const +{ + if(!index.isValid() || index.row() >= d->data.size() || index.column() != 0) { + return Qt::ItemIsDropEnabled; + } + + QVariant available = d->data.at(index.row())->property("available"); + if (available.isValid() && available.type() == QVariant::Bool && !available.toBool()) { + return Qt::ItemIsSelectable | Qt::ItemIsDragEnabled; + } + return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled; +} + +QList<int> ObjectDescriptionModelData::tupleIndexOrder() const +{ + QList<int> ret; + for (int i = 0; i < d->data.size(); ++i) { + ret.append(d->data.at(i)->index()); + } + return ret; +} + +int ObjectDescriptionModelData::tupleIndexAtPositionIndex(int positionIndex) const +{ + return d->data.at(positionIndex)->index(); +} + +QMimeData *ObjectDescriptionModelData::mimeData(ObjectDescriptionType type, const QModelIndexList &indexes) const +{ + QMimeData *mimeData = new QMimeData; + QByteArray encodedData; + QDataStream stream(&encodedData, QIODevice::WriteOnly); + QModelIndexList::const_iterator end = indexes.constEnd(); + QModelIndexList::const_iterator index = indexes.constBegin(); + for(; index!=end; ++index) { + if ((*index).isValid()) { + stream << d->data.at((*index).row())->index(); + } + } + //pDebug() << Q_FUNC_INFO << "setting mimeData to" << mimeTypes(type).first() << "=>" << encodedData.toHex(); + mimeData->setData(mimeTypes(type).first(), encodedData); + return mimeData; +} + +void ObjectDescriptionModelData::moveUp(const QModelIndex &index) +{ + if (!index.isValid() || index.row() >= d->data.size() || index.row() < 1 || index.column() != 0) + return; + + emit d->model->layoutAboutToBeChanged(); + QModelIndex above = index.sibling(index.row() - 1, index.column()); + d->data.swap(index.row(), above.row()); + QModelIndexList from, to; + from << index << above; + to << above << index; + d->model->changePersistentIndexList(from, to); + emit d->model->layoutChanged(); +} + +void ObjectDescriptionModelData::moveDown(const QModelIndex &index) +{ + if (!index.isValid() || index.row() >= d->data.size() - 1 || index.column() != 0) + return; + + emit d->model->layoutAboutToBeChanged(); + QModelIndex below = index.sibling(index.row() + 1, index.column()); + d->data.swap(index.row(), below.row()); + QModelIndexList from, to; + from << index << below; + to << below << index; + d->model->changePersistentIndexList(from, to); + emit d->model->layoutChanged(); +} + +ObjectDescriptionModelData::ObjectDescriptionModelData(QAbstractListModel *model) + : d(new ObjectDescriptionModelDataPrivate(model)) +{ +} + +ObjectDescriptionModelData::~ObjectDescriptionModelData() +{ + delete d; +} + +void ObjectDescriptionModelData::setModelData(const QList<QExplicitlySharedDataPointer<ObjectDescriptionData> > &newData) +{ + d->data = newData; + d->model->reset(); +} + +QList<QExplicitlySharedDataPointer<ObjectDescriptionData> > ObjectDescriptionModelData::modelData() const +{ + return d->data; +} + +QExplicitlySharedDataPointer<ObjectDescriptionData> ObjectDescriptionModelData::modelData(const QModelIndex &index) const +{ + if (!index.isValid() || index.row() >= d->data.size() || index.column() != 0) { + return QExplicitlySharedDataPointer<ObjectDescriptionData>(new ObjectDescriptionData(0)); + } + return d->data.at(index.row()); +} + +Qt::DropActions ObjectDescriptionModelData::supportedDropActions() const +{ + //pDebug() << Q_FUNC_INFO; + return Qt::MoveAction; +} + +bool ObjectDescriptionModelData::dropMimeData(ObjectDescriptionType type, const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &parent) +{ + Q_UNUSED(action); + Q_UNUSED(column); + Q_UNUSED(parent); + //pDebug() << Q_FUNC_INFO << data << action << row << column << parent; + + QString format = mimeTypes(type).first(); + if (!data->hasFormat(format)) { + return false; + } + + if (row == -1) { + row = d->data.size(); + } + + QByteArray encodedData = data->data(format); + QDataStream stream(&encodedData, QIODevice::ReadOnly); + QList<QExplicitlySharedDataPointer<ObjectDescriptionData> > toInsert; + while (!stream.atEnd()) { + int otherIndex; + stream >> otherIndex; + ObjectDescriptionData *obj = ObjectDescriptionData::fromIndex(type, otherIndex); + + if (obj->isValid()) { + toInsert << QExplicitlySharedDataPointer<ObjectDescriptionData>(obj); + } else { + delete obj; + } + } + d->model->beginInsertRows(QModelIndex(), row, row + toInsert.size() - 1); + foreach (const QExplicitlySharedDataPointer<ObjectDescriptionData> &obj, toInsert) { + d->data.insert(row, obj); + } + d->model->endInsertRows(); + return true; +} + + +bool ObjectDescriptionModelData::removeRows(int row, int count, const QModelIndex &parent) +{ + //pDebug() << Q_FUNC_INFO << row << count << parent; + if (parent.isValid() || row + count > d->data.size()) { + return false; + } + d->model->beginRemoveRows(parent, row, row + count - 1); + for (;count > 0; --count) { + d->data.removeAt(row); + } + d->model->endRemoveRows(); + return true; +} + +/* +template<ObjectDescriptionType type> +bool ObjectDescriptionModel<type>::insertRows(int row, int count, const QModelIndex &parent) +{ + pDebug() << Q_FUNC_INFO << row << count << parent; + if (parent.isValid() || row < 0 || row > d->data.size()) { + return false; + } + beginInsertRows(parent, row, row + count - 1); + for (;count > 0; --count) { + d->data.insert(row, ObjectDescription<type>()); + } + endInsertRows(); + return true; +} +*/ + +QStringList ObjectDescriptionModelData::mimeTypes(ObjectDescriptionType type) const +{ + return QStringList(QLatin1String("application/x-phonon-objectdescription") + QString::number(static_cast<int>(type))); +} + +#if !defined(Q_CC_MINGW) || __MINGW32_MAJOR_VERSION >= 4 +#if !defined(Q_CC_MSVC) || _MSC_VER > 1300 || defined(Q_CC_INTEL) +#define INSTANTIATE_META_FUNCTIONS(type) \ +template const QMetaObject *ObjectDescriptionModel<type>::metaObject() const; \ +template void *ObjectDescriptionModel<type>::qt_metacast(const char *) + +INSTANTIATE_META_FUNCTIONS(AudioOutputDeviceType); +INSTANTIATE_META_FUNCTIONS(AudioCaptureDeviceType); +INSTANTIATE_META_FUNCTIONS(EffectType); +INSTANTIATE_META_FUNCTIONS(AudioChannelType); +INSTANTIATE_META_FUNCTIONS(SubtitleType); +#endif +/*INSTANTIATE_META_FUNCTIONS(VideoOutputDeviceType); +INSTANTIATE_META_FUNCTIONS(VideoCaptureDeviceType); +INSTANTIATE_META_FUNCTIONS(AudioCodecType); +INSTANTIATE_META_FUNCTIONS(VideoCodecType); +INSTANTIATE_META_FUNCTIONS(ContainerFormatType); +INSTANTIATE_META_FUNCTIONS(VisualizationType); +*/ +#endif //Q_CC_MINGW +} // namespace Phonon + +#endif //QT_NO_PHONON_OBJECTDESCRIPTIONMODEL + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/phonon/objectdescriptionmodel.h b/src/3rdparty/phonon/phonon/objectdescriptionmodel.h new file mode 100644 index 0000000..84dc0bb --- /dev/null +++ b/src/3rdparty/phonon/phonon/objectdescriptionmodel.h @@ -0,0 +1,380 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_OBJECTDESCRIPTIONMODEL_H +#define PHONON_OBJECTDESCRIPTIONMODEL_H + +#include "phonon_export.h" +#include "phonondefs.h" +#include "objectdescription.h" +#include <QtCore/QList> +#include <QtCore/QModelIndex> +#include <QtCore/QStringList> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_OBJECTDESCRIPTIONMODEL + +namespace Phonon +{ + class ObjectDescriptionModelDataPrivate; + + /** \internal + * \class ObjectDescriptionModelData objectdescriptionmodel.h Phonon/ObjectDescriptionModelData + * \brief Data class for models for ObjectDescription objects. + * + * \author Matthias Kretz <kretz@kde.org> + */ + class PHONON_EXPORT ObjectDescriptionModelData + { + public: + /** + * Returns the number of rows in the model. This value corresponds + * to the size of the list passed through setModelData. + * + * \param parent The optional \p parent argument is used in most models to specify + * the parent of the rows to be counted. Because this is a list if a + * valid parent is specified the result will always be 0. + * + * Reimplemented from QAbstractItemModel. + * + * \see QAbstractItemModel::rowCount + */ + int rowCount(const QModelIndex &parent = QModelIndex()) const; + + /** + * Returns data from the item with the given \p index for the specified + * \p role. + * If the view requests an invalid index, an invalid variant is + * returned. + * + * Reimplemented from QAbstractItemModel. + * + * \see QAbstractItemModel::data + * \see Qt::ItemDataRole + */ + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + /** + * Reimplemented to show unavailable devices as disabled (but still + * selectable). + */ + Qt::ItemFlags flags(const QModelIndex &index) const; + + /** + * Returns a list of indexes in the same order as they are in the + * model. The indexes come from the ObjectDescription::index + * method. + * + * This is useful to let the user define a list of preference. + */ + QList<int> tupleIndexOrder() const; + + /** + * Returns the ObjectDescription::index for the tuple + * at the given position \p positionIndex. For example a + * QComboBox will give you the currentIndex as the + * position in the list. But to select the according + * AudioOutputDevice using AudioOutputDevice::fromIndex + * you can use this method. + * + * \param positionIndex The position in the list. + */ + int tupleIndexAtPositionIndex(int positionIndex) const; + + /** + * Returns the MIME data that dropMimeData() can use to create new + * items. + */ + QMimeData *mimeData(ObjectDescriptionType type, const QModelIndexList &indexes) const; + + /** + * Moves the item at the given \p index up. In the resulting list + * the items at index.row() and index.row() - 1 are swapped. + * + * Connected views are updated automatically. + */ + void moveUp(const QModelIndex &index); + + /** + * Moves the item at the given \p index down. In the resulting list + * the items at index.row() and index.row() + 1 are swapped. + * + * Connected views are updated automatically. + */ + void moveDown(const QModelIndex &index); + + void setModelData(const QList<QExplicitlySharedDataPointer<ObjectDescriptionData> > &data); + QList<QExplicitlySharedDataPointer<ObjectDescriptionData> > modelData() const; + QExplicitlySharedDataPointer<ObjectDescriptionData> modelData(const QModelIndex &index) const; + Qt::DropActions supportedDropActions() const; + bool dropMimeData(ObjectDescriptionType type, const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + QStringList mimeTypes(ObjectDescriptionType type) const; + + ObjectDescriptionModelData(QAbstractListModel *); + protected: + ~ObjectDescriptionModelData(); + //ObjectDescriptionModelData(ObjectDescriptionModelDataPrivate *dd); + ObjectDescriptionModelDataPrivate *const d; + }; + + /** \class ObjectDescriptionModel objectdescriptionmodel.h Phonon/ObjectDescriptionModel + * \short The ObjectDescriptionModel class provides a model from + * a list of ObjectDescription objects. + * + * ObjectDescriptionModel is a readonly model that supplies a list + * using ObjectDescription::name() for the text and + * ObjectDescription::description() for the tooltip. If set the properties + * "icon" and "available" are used to set the decoration and disable the + * item (disabled only visually, you can still select and drag it). + * + * It also provides the methods moveUp() and moveDown() to order the list. + * Additionally drag and drop is possible so that + * QAbstractItemView::InternalMove can be used. + * The resulting order of the ObjectDescription::index() values can then be + * retrieved using tupleIndexOrder(). + * + * An example use case would be to give the user a QComboBox to select + * the output device: + * \code + * QComboBox *cb = new QComboBox(parentWidget); + * ObjectDescriptionModel *model = new ObjectDescriptionModel(cb); + * model->setModelData(BackendCapabilities::availableAudioOutputDevices()); + * cb->setModel(model); + * cb->setCurrentIndex(0); // select first entry + * \endcode + * + * And to retrieve the selected AudioOutputDevice: + * \code + * int cbIndex = cb->currentIndex(); + * AudioOutputDevice selectedDevice = model->modelData(cbIndex); + * \endcode + * + * \ingroup Frontend + * \author Matthias Kretz <kretz@kde.org> + */ + template<ObjectDescriptionType type> + class ObjectDescriptionModel : public QAbstractListModel + { + public: + Q_OBJECT_CHECK + +/* MinGW 3.4.x gives an ICE when trying to instantiate one of the + ObjectDescriptionModel<foo> classes because it can't handle + half exported classes correct. gcc 4.3.x has a fix for this but + we currently there's no official gcc 4.3 on windows available. + Because of this we need this little hack + */ +#if !defined(Q_CC_MINGW) || __MINGW32_MAJOR_VERSION >= 4 + /** \internal */ + static PHONON_EXPORT const QMetaObject staticMetaObject; + /** \internal */ + PHONON_EXPORT const QMetaObject *metaObject() const; + /** \internal */ + PHONON_EXPORT void *qt_metacast(const char *_clname); + //int qt_metacall(QMetaObject::Call _c, int _id, void **_a); +#endif + + /** + * Returns the number of rows in the model. This value corresponds + * to the size of the list passed through setModelData. + * + * \param parent The optional \p parent argument is used in most models to specify + * the parent of the rows to be counted. Because this is a list if a + * valid parent is specified the result will always be 0. + * + * Reimplemented from QAbstractItemModel. + * + * \see QAbstractItemModel::rowCount + */ + inline int rowCount(const QModelIndex &parent = QModelIndex()) const { return d->rowCount(parent); } //krazy:exclude=inline + + /** + * Returns data from the item with the given \p index for the specified + * \p role. + * If the view requests an invalid index, an invalid variant is + * returned. + * + * Reimplemented from QAbstractItemModel. + * + * \see QAbstractItemModel::data + * \see Qt::ItemDataRole + */ + inline QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const { return d->data(index, role); } //krazy:exclude=inline + + /** + * Reimplemented to show unavailable devices as disabled (but still + * selectable). + */ + inline Qt::ItemFlags flags(const QModelIndex &index) const { return d->flags(index); } //krazy:exclude=inline + + /** + * Returns a list of indexes in the same order as they are in the + * model. The indexes come from the ObjectDescription::index + * method. + * + * This is useful to let the user define a list of preference. + */ + inline QList<int> tupleIndexOrder() const { return d->tupleIndexOrder(); } //krazy:exclude=inline + + /** + * Returns the ObjectDescription::index for the tuple + * at the given position \p positionIndex. For example a + * QComboBox will give you the currentIndex as the + * position in the list. But to select the according + * AudioOutputDevice using AudioOutputDevice::fromIndex + * you can use this method. + * + * \param positionIndex The position in the list. + */ + inline int tupleIndexAtPositionIndex(int positionIndex) const { return d->tupleIndexAtPositionIndex(positionIndex); } //krazy:exclude=inline + + /** + * Returns the MIME data that dropMimeData() can use to create new + * items. + */ + inline QMimeData *mimeData(const QModelIndexList &indexes) const { return d->mimeData(type, indexes); } //krazy:exclude=inline + + /** + * Moves the item at the given \p index up. In the resulting list + * the items at index.row() and index.row() - 1 are swapped. + * + * Connected views are updated automatically. + */ + inline void moveUp(const QModelIndex &index) { d->moveUp(index); } //krazy:exclude=inline + + /** + * Moves the item at the given \p index down. In the resulting list + * the items at index.row() and index.row() + 1 are swapped. + * + * Connected views are updated automatically. + */ + inline void moveDown(const QModelIndex &index) { d->moveDown(index); } //krazy:exclude=inline + + /** + * Constructs a ObjectDescription model with the + * given \p parent. + */ + explicit inline ObjectDescriptionModel(QObject *parent = 0) : QAbstractListModel(parent), d(new ObjectDescriptionModelData(this)) {} //krazy:exclude=inline + + /** + * Constructs a ObjectDescription model with the + * given \p parent and the given \p data. + */ + explicit inline ObjectDescriptionModel(const QList<ObjectDescription<type> > &data, QObject *parent = 0) //krazy:exclude=inline + : QAbstractListModel(parent), d(new ObjectDescriptionModelData(this)) { setModelData(data); } + + /** + * Sets the model data using the list provided by \p data. + * + * All previous model data is cleared. + */ + inline void setModelData(const QList<ObjectDescription<type> > &data) { //krazy:exclude=inline + QList<QExplicitlySharedDataPointer<ObjectDescriptionData> > list; + Q_FOREACH (const ObjectDescription<type> &desc, data) { + list << desc.d; + } + d->setModelData(list); + } + + /** + * Returns the model data. + * + * As the order of the list might have changed this can be different + * to what was set using setModelData(). + */ + inline QList<ObjectDescription<type> > modelData() const { //krazy:exclude=inline + QList<ObjectDescription<type> > ret; + QList<QExplicitlySharedDataPointer<ObjectDescriptionData> > list = d->modelData(); + Q_FOREACH (const QExplicitlySharedDataPointer<ObjectDescriptionData> &data, list) { + ret << ObjectDescription<type>(data); + } + return ret; + } + + /** + * Returns one ObjectDescription of the model data for the given \p index. + */ + inline ObjectDescription<type> modelData(const QModelIndex &index) const { return ObjectDescription<type>(d->modelData(index)); } //krazy:exclude=inline + + /** + * This model supports drag and drop to copy or move + * items. + */ + inline Qt::DropActions supportedDropActions() const { return d->supportedDropActions(); } //krazy:exclude=inline + + /** + * Accept drops from other models of the same ObjectDescriptionType. + * + * If a valid \p parent is given the dropped items will be inserted + * above that item. + */ + inline bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { //krazy:exclude=inline + return d->dropMimeData(type, data, action, row, column, parent); + } + + /** + * Removes count rows starting with the given row. + * + * If a valid \p parent is given no rows are removed since this is a + * list model. + * + * Returns true if the rows were successfully removed; otherwise returns false. + */ + inline bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) { //krazy:exclude=inline + return d->removeRows(row, count, parent); + } + + /** + * Returns a list of supported drag and drop MIME types. Currently + * it only supports one type used internally. + */ + inline QStringList mimeTypes() const { return d->mimeTypes(type); } //krazy:exclude=inline + + protected: + ObjectDescriptionModelData *const d; + }; + + typedef ObjectDescriptionModel<AudioOutputDeviceType> AudioOutputDeviceModel; + typedef ObjectDescriptionModel<AudioCaptureDeviceType> AudioCaptureDeviceModel; + typedef ObjectDescriptionModel<EffectType> EffectDescriptionModel; + typedef ObjectDescriptionModel<AudioChannelType> AudioChannelDescriptionModel; + typedef ObjectDescriptionModel<SubtitleType> SubtitleDescriptionModel; +/* + typedef ObjectDescriptionModel<VideoOutputDeviceType> VideoOutputDeviceModel; + typedef ObjectDescriptionModel<VideoCaptureDeviceType> VideoCaptureDeviceModel; + typedef ObjectDescriptionModel<AudioCodecType> AudioCodecDescriptionModel; + typedef ObjectDescriptionModel<VideoCodecType> VideoCodecDescriptionModel; + typedef ObjectDescriptionModel<ContainerFormatType> ContainerFormatDescriptionModel; + typedef ObjectDescriptionModel<VisualizationType> VisualizationDescriptionModel;*/ + +} + +#endif //QT_NO_PHONON_OBJECTDESCRIPTIONMODEL + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // PHONON_OBJECTDESCRIPTIONMODEL_H +// vim: sw=4 ts=4 tw=80 diff --git a/src/3rdparty/phonon/phonon/objectdescriptionmodel_p.h b/src/3rdparty/phonon/phonon/objectdescriptionmodel_p.h new file mode 100644 index 0000000..f036bc8 --- /dev/null +++ b/src/3rdparty/phonon/phonon/objectdescriptionmodel_p.h @@ -0,0 +1,65 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_OBJECTDESCRIPTIONMODEL_P_H +#define PHONON_OBJECTDESCRIPTIONMODEL_P_H + +#include "objectdescriptionmodel.h" +#include <QtCore/QList> +#include "objectdescription.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_OBJECTDESCRIPTIONMODEL + +namespace Phonon +{ + +class ListModelHelper : public QAbstractListModel +{ + public: + using QAbstractListModel::layoutAboutToBeChanged; + using QAbstractListModel::changePersistentIndexList; + using QAbstractListModel::layoutChanged; + using QAbstractListModel::reset; + using QAbstractListModel::beginInsertRows; + using QAbstractListModel::endInsertRows; + using QAbstractListModel::beginRemoveRows; + using QAbstractListModel::endRemoveRows; +}; + +class ObjectDescriptionModelDataPrivate +{ + public: + ObjectDescriptionModelDataPrivate(QAbstractListModel *m) : model(reinterpret_cast<ListModelHelper *>(m)) {} + QList<QExplicitlySharedDataPointer<ObjectDescriptionData> > data; + ListModelHelper *model; +}; + +} + +#endif //QT_NO_PHONON_OBJECTDESCRIPTIONMODEL + +QT_END_NAMESPACE + +#endif // PHONON_OBJECTDESCRIPTIONMODEL_P_H +// vim: sw=4 ts=4 tw=80 diff --git a/src/3rdparty/phonon/phonon/org.kde.Phonon.AudioOutput.xml b/src/3rdparty/phonon/phonon/org.kde.Phonon.AudioOutput.xml new file mode 100644 index 0000000..2498337 --- /dev/null +++ b/src/3rdparty/phonon/phonon/org.kde.Phonon.AudioOutput.xml @@ -0,0 +1,32 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node> + <interface name="org.kde.Phonon.AudioOutput"> + <property name="volume" type="d" access="readwrite"/> + <property name="muted" type="b" access="readwrite"/> + <property name="outputDeviceIndex" type="i" access="readwrite"/> + <signal name="volumeChanged"> + <arg type="d" direction="out"/> + </signal> + <signal name="mutedChanged"> + <arg type="b" direction="out"/> + </signal> + <signal name="outputDeviceIndexChanged"> + <arg type="i" direction="out"/> + </signal> + <signal name="nameChanged"> + <arg name="newName" type="s" direction="out"/> + </signal> + <signal name="newOutputAvailable"> + <arg name="service" type="s" direction="out"/> + <arg name="path" type="s" direction="out"/> + </signal> + <signal name="outputDestroyed"> + </signal> + <method name="category"> + <arg type="s" direction="out"/> + </method> + <method name="name"> + <arg type="s" direction="out"/> + </method> + </interface> +</node> diff --git a/src/3rdparty/phonon/phonon/path.cpp b/src/3rdparty/phonon/phonon/path.cpp new file mode 100644 index 0000000..b46d30a --- /dev/null +++ b/src/3rdparty/phonon/phonon/path.cpp @@ -0,0 +1,472 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#include "path.h" +#include "path_p.h" + +#include "phononnamespace_p.h" +#include "backendinterface.h" +#include "factory_p.h" +#include "medianode.h" +#include "medianode_p.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + +class ConnectionTransaction +{ + public: + ConnectionTransaction(BackendInterface *b, const QSet<QObject*> &x) : backend(b), list(x) + { + success = backend->startConnectionChange(list); + } + ~ConnectionTransaction() + { + backend->endConnectionChange(list); + } + operator bool() + { + return success; + } + private: + bool success; + BackendInterface *const backend; + const QSet<QObject*> list; +}; + +PathPrivate::~PathPrivate() +{ +#ifndef QT_NO_PHONON_EFFECT + foreach (Effect *e, effects) { + e->k_ptr->removeDestructionHandler(this); + } + delete effectsParent; +#endif +} + +Path::~Path() +{ +} + +Path::Path() + : d(new PathPrivate) +{ +} + +Path::Path(const Path &rhs) + : d(rhs.d) +{ +} + +bool Path::isValid() const +{ + return d->sourceNode != 0 && d->sinkNode != 0; +} + +#ifndef QT_NO_PHONON_EFFECT +Effect *Path::insertEffect(const EffectDescription &desc, Effect *insertBefore) +{ + if (!d->effectsParent) { + d->effectsParent = new QObject; + } + Effect *e = new Effect(desc, d->effectsParent); + if (!e->isValid()) { + delete e; + return 0; + } + bool success = insertEffect(e, insertBefore); + if (!success) { + delete e; + return 0; + } + return e; +} + +bool Path::insertEffect(Effect *newEffect, Effect *insertBefore) +{ + QObject *newEffectBackend = newEffect ? newEffect->k_ptr->backendObject() : 0; + if (!isValid() || !newEffectBackend || d->effects.contains(newEffect) || + (insertBefore && (!d->effects.contains(insertBefore) || !insertBefore->k_ptr->backendObject()))) { + return false; + } + QObject *leftNode = 0; + QObject *rightNode = 0; + const int insertIndex = insertBefore ? d->effects.indexOf(insertBefore) : d->effects.size(); + if (insertIndex == 0) { + //prepend + leftNode = d->sourceNode->k_ptr->backendObject(); + } else { + leftNode = d->effects[insertIndex - 1]->k_ptr->backendObject(); + } + + if (insertIndex == d->effects.size()) { + //append + rightNode = d->sinkNode->k_ptr->backendObject(); + } else { + Q_ASSERT(insertBefore); + rightNode = insertBefore->k_ptr->backendObject(); + } + + QList<QObjectPair> disconnections, connections; + disconnections << QObjectPair(leftNode, rightNode); + connections << QObjectPair(leftNode, newEffectBackend) + << QObjectPair(newEffectBackend, rightNode); + + if (d->executeTransaction(disconnections, connections)) { + newEffect->k_ptr->addDestructionHandler(d.data()); + d->effects.insert(insertIndex, newEffect); + return true; + } else { + return false; + } +} + +bool Path::removeEffect(Effect *effect) +{ + return d->removeEffect(effect); +} + +QList<Effect *> Path::effects() const +{ + return d->effects; +} +#endif //QT_NO_PHONON_EFFECT + +bool Path::reconnect(MediaNode *source, MediaNode *sink) +{ + if (!source || !sink || !source->k_ptr->backendObject() || !sink->k_ptr->backendObject()) { + return false; + } + + QList<QObjectPair> disconnections, connections; + + //backend objects + QObject *bnewSource = source->k_ptr->backendObject(); + QObject *bnewSink = sink->k_ptr->backendObject(); + QObject *bcurrentSource = d->sourceNode ? d->sourceNode->k_ptr->backendObject() : 0; + QObject *bcurrentSink = d->sinkNode ? d->sinkNode->k_ptr->backendObject() : 0; + + if (bnewSource != bcurrentSource) { + //we need to change the source +#ifndef QT_NO_PHONON_EFFECT + MediaNode *next = d->effects.isEmpty() ? sink : d->effects.first(); +#else + MediaNode *next = sink; +#endif //QT_NO_PHONON_EFFECT + QObject *bnext = next->k_ptr->backendObject(); + if (bcurrentSource) + disconnections << QObjectPair(bcurrentSource, bnext); + connections << QObjectPair(bnewSource, bnext); + } + + if (bnewSink != bcurrentSink) { +#ifndef QT_NO_PHONON_EFFECT + MediaNode *previous = d->effects.isEmpty() ? source : d->effects.last(); +#else + MediaNode *previous = source; +#endif //QT_NO_PHONON_EFFECT + QObject *bprevious = previous->k_ptr->backendObject(); + if (bcurrentSink) + disconnections << QObjectPair(bprevious, bcurrentSink); + QObjectPair pair(bprevious, bnewSink); + if (!connections.contains(pair)) //avoid connecting twice + connections << pair; + } + + if (d->executeTransaction(disconnections, connections)) { + + //everything went well: let's update the path and the sink node + if (d->sinkNode != sink) { + if (d->sinkNode) { + d->sinkNode->k_ptr->removeInputPath(*this); + d->sinkNode->k_ptr->removeDestructionHandler(d.data()); + } + sink->k_ptr->addInputPath(*this); + d->sinkNode = sink; + d->sinkNode->k_ptr->addDestructionHandler(d.data()); + } + + //everything went well: let's update the path and the source node + if (d->sourceNode != source) { + source->k_ptr->addOutputPath(*this); + if (d->sourceNode) { + d->sourceNode->k_ptr->removeOutputPath(*this); + d->sourceNode->k_ptr->removeDestructionHandler(d.data()); + } + d->sourceNode = source; + d->sourceNode->k_ptr->addDestructionHandler(d.data()); + } + return true; + } else { + return false; + } +} + +bool Path::disconnect() +{ + if (!isValid()) { + return false; + } + + QObjectList list; + if (d->sourceNode) + list << d->sourceNode->k_ptr->backendObject(); +#ifndef QT_NO_PHONON_EFFECT + foreach(Effect *e, d->effects) { + list << e->k_ptr->backendObject(); + } +#endif + if (d->sinkNode) { + list << d->sinkNode->k_ptr->backendObject(); + } + + //lets build the disconnection list + QList<QObjectPair> disco; + if (list.count() >=2 ) { + QObjectList::const_iterator it = list.begin(); + for(;it+1 != list.end();++it) { + disco << QObjectPair(*it, *(it+1)); + } + } + + if (d->executeTransaction(disco, QList<QObjectPair>())) { + //everything went well, let's remove the reference + //to the paths from the source and sink + if (d->sourceNode) { + d->sourceNode->k_ptr->removeOutputPath(*this); + d->sourceNode->k_ptr->removeDestructionHandler(d.data()); + } + d->sourceNode = 0; + +#ifndef QT_NO_PHONON_EFFECT + foreach(Effect *e, d->effects) { + e->k_ptr->removeDestructionHandler(d.data()); + } + d->effects.clear(); +#endif + + if (d->sinkNode) { + d->sinkNode->k_ptr->removeInputPath(*this); + d->sinkNode->k_ptr->removeDestructionHandler(d.data()); + } + d->sinkNode = 0; + return true; + } else { + return false; + } +} + +MediaNode *Path::source() const +{ + return d->sourceNode; +} + +MediaNode *Path::sink() const +{ + return d->sinkNode; +} + + + +bool PathPrivate::executeTransaction( const QList<QObjectPair> &disconnections, const QList<QObjectPair> &connections) +{ + QSet<QObject*> nodesForTransaction; + foreach(const QObjectPair &pair, disconnections) { + nodesForTransaction << pair.first; + nodesForTransaction << pair.second; + } + foreach(const QObjectPair &pair, connections) { + nodesForTransaction << pair.first; + nodesForTransaction << pair.second; + } + BackendInterface *backend = qobject_cast<BackendInterface *>(Factory::backend()); + if (!backend) + return false; + + ConnectionTransaction transaction(backend, nodesForTransaction); + if (!transaction) + return false; + + QList<QObjectPair>::const_iterator it = disconnections.begin(); + for(;it != disconnections.end();++it) { + const QObjectPair &pair = *it; + if (!backend->disconnectNodes(pair.first, pair.second)) { + + //Error: a disconnection failed + QList<QObjectPair>::const_iterator it2 = disconnections.begin(); + for(; it2 != it; ++it2) { + const QObjectPair &pair = *it2; + bool success = backend->connectNodes(pair.first, pair.second); + Q_ASSERT(success); //a failure here means it is impossible to reestablish the connection + Q_UNUSED(success); + } + return false; + } + } + + for(it = connections.begin(); it != connections.end();++it) { + const QObjectPair &pair = *it; + if (!backend->connectNodes(pair.first, pair.second)) { + //Error: a connection failed + QList<QObjectPair>::const_iterator it2 = connections.begin(); + for(; it2 != it; ++it2) { + const QObjectPair &pair = *it2; + bool success = backend->disconnectNodes(pair.first, pair.second); + Q_ASSERT(success); //a failure here means it is impossible to reestablish the connection + Q_UNUSED(success); + } + + //and now let's reconnect the nodes that were disconnected: rollback + foreach(const QObjectPair &pair, disconnections) { + bool success = backend->connectNodes(pair.first, pair.second); + Q_ASSERT(success); //a failure here means it is impossible to reestablish the connection + Q_UNUSED(success); + } + + return false; + + } + } + return true; +} + +#ifndef QT_NO_PHONON_EFFECT +bool PathPrivate::removeEffect(Effect *effect) +{ + if (!effects.contains(effect)) + return false; + + QObject *leftNode = 0; + QObject *rightNode = 0; + const int index = effects.indexOf(effect); + if (index == 0) { + leftNode = sourceNode->k_ptr->backendObject(); //append + } else { + leftNode = effects[index - 1]->k_ptr->backendObject(); + } + if (index == effects.size()-1) { + rightNode = sinkNode->k_ptr->backendObject(); //prepend + } else { + rightNode = effects[index + 1]->k_ptr->backendObject(); + } + + QList<QObjectPair> disconnections, connections; + QObject *beffect = effect->k_ptr->backendObject(); + disconnections << QObjectPair(leftNode, beffect) << QObjectPair(beffect, rightNode); + connections << QObjectPair(leftNode, rightNode); + + if (executeTransaction(disconnections, connections)) { + effect->k_ptr->removeDestructionHandler(this); + effects.removeAt(index); + return true; + } + return false; +} +#endif //QT_NO_PHONON_EFFECT + + +void PathPrivate::phononObjectDestroyed(MediaNodePrivate *mediaNodePrivate) +{ + Q_ASSERT(mediaNodePrivate); + if (mediaNodePrivate == sinkNode->k_ptr || mediaNodePrivate == sourceNode->k_ptr) { + //let's first disconnectq the path from its source and sink + QObject *bsink = sinkNode->k_ptr->backendObject(); + QObject *bsource = sourceNode->k_ptr->backendObject(); + QList<QObjectPair> disconnections; +#ifndef QT_NO_PHONON_EFFECT + disconnections << QObjectPair(bsource, effects.isEmpty() ? bsink : effects.first()->k_ptr->backendObject()); + if (!effects.isEmpty()) + disconnections << QObjectPair(effects.last()->k_ptr->backendObject(), bsink); +#else + disconnections << QObjectPair(bsource, bsink); +#endif //QT_NO_PHONON_EFFECT + + executeTransaction(disconnections, QList<QObjectPair>()); + + Path p; //temporary path + p.d = this; + if (mediaNodePrivate == sinkNode->k_ptr) { + sourceNode->k_ptr->removeOutputPath(p); + sourceNode->k_ptr->removeDestructionHandler(this); + } else { + sinkNode->k_ptr->removeInputPath(p); + sinkNode->k_ptr->removeDestructionHandler(this); + } + sourceNode = 0; + sinkNode = 0; + } else { +#ifndef QT_NO_PHONON_EFFECT + foreach (Effect *e, effects) { + if (e->k_ptr == mediaNodePrivate) { + removeEffect(e); + } + } +#endif //QT_NO_PHONON_EFFECT + } +} + +Path createPath(MediaNode *source, MediaNode *sink) +{ + Path p; + if (!p.reconnect(source, sink)) { + const QObject *const src = source ? (source->k_ptr->qObject() +#ifndef QT_NO_DYNAMIC_CAST + ? source->k_ptr->qObject() : dynamic_cast<QObject *>(source) +#endif + ) : 0; + const QObject *const snk = sink ? (sink->k_ptr->qObject() +#ifndef QT_NO_DYNAMIC_CAST + ? sink->k_ptr->qObject() : dynamic_cast<QObject *>(sink) +#endif + ) : 0; + pWarning() << "Phonon::createPath: Cannot connect " + << (src ? src->metaObject()->className() : "") + << '(' << (src ? (src->objectName().isEmpty() ? "no objectName" : qPrintable(src->objectName())) : "null") << ") to " + << (snk ? snk->metaObject()->className() : "") + << '(' << (snk ? (snk->objectName().isEmpty() ? "no objectName" : qPrintable(snk->objectName())) : "null") + << ")."; + } + return p; +} + + +Path & Path::operator=(const Path &other) +{ + d = other.d; + return *this; +} + +bool Path::operator==(const Path &other) const +{ + return d == other.d; +} + +bool Path::operator!=(const Path &other) const +{ + return !operator==(other); +} + +} // namespace Phonon + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/phonon/path.h b/src/3rdparty/phonon/phonon/path.h new file mode 100644 index 0000000..eeabe82 --- /dev/null +++ b/src/3rdparty/phonon/phonon/path.h @@ -0,0 +1,243 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_PATH_H +#define PHONON_PATH_H + +#include "phonon_export.h" +#include "objectdescription.h" + +#include <QtCore/QExplicitlySharedDataPointer> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +template<class T> class QList; + +namespace Phonon +{ + +class PathPrivate; +class Effect; +class MediaNode; + +/** \class Path path.h Phonon/Path + * \short Connection object providing convenient effect insertion + * + * \code +MediaObject *media = new MediaObject; +AudioOutput *output = new AudioOutput(Phonon::MusicCategory); +Path path = Phonon::createPath(media, output); +Q_ASSERT(path.isValid()); // for this simple case the path should always be + //valid - there are unit tests to ensure it +// insert an effect +QList<EffectDescription> effectList = BackendCapabilities::availableAudioEffects(); +if (!effectList.isEmpty()) { + Effect *effect = path.insertEffect(effectList.first()); +} + * \endcode + * \ingroup Playback + * \ingroup Recording + * \author Matthias Kretz <kretz@kde.org> + * \author Thierry Bastian <thierry.bastian@trolltech.com> + */ +class PHONON_EXPORT Path +{ + friend class FactoryPrivate; + public: + /** + * Destroys this reference to the Path. If the path was valid the connection is not broken + * as both the source and the sink MediaNodes still keep a reference to the Path. + * + * \see disconnect + */ + ~Path(); + + /** + * Creates an invalid path. + * + * You can still make it a valid path by calling reconnect. To create a path you should use + * createPath, though. + * + * \see createPath + * \see isValid + */ + Path(); + + /** + * Constructs a copy of the given path. + * + * This constructor is fast thanks to explicit sharing. + */ + Path(const Path &); + + /** + * Returns whether the path object connects two MediaNodes or not. + * + * \return \p true when the path connects two MediaNodes + * \return \p false when the path is disconnected + */ + bool isValid() const; + //MediaStreamTypes mediaStreamTypes() const; + +#ifndef QT_NO_PHONON_EFFECT + /** + * Creates and inserts an effect into the path. + * + * You may insert effects of the same class as often as you like, + * but if you insert the same object, the call will fail. + * + * \param desc The EffectDescription object for the effect to be inserted. + * + * \param insertBefore If you already inserted an effect you can + * tell with this parameter in which order the data gets + * processed. If this is \c 0 the effect is appended at the end of + * the processing list. If the effect has not been inserted before + * the method will do nothing and return \c false. + * + * \return Returns a pointer to the effect object if it could be inserted + * at the specified position. If \c 0 is returned the effect was not + * inserted. + * + * \see removeEffect + * \see effects + */ + Effect *insertEffect(const EffectDescription &desc, Effect *insertBefore = 0); + + /** + * Inserts an effect into the path. + * + * You may insert effects of the same class as often as you like, + * but if you insert the same object, the call will fail. + * + * \param newEffect An Effect object. + * + * \param insertBefore If you already inserted an effect you can + * tell with this parameter in which order the data gets + * processed. If this is \c 0 the effect is appended at the end of + * the processing list. If the effect has not been inserted before + * the method will do nothing and return \c false. + * + * \return Returns whether the effect could be inserted at the + * specified position. If \c false is returned the effect was not + * inserted. + * + * \see removeEffect + * \see effects + */ + bool insertEffect(Effect *newEffect, Effect *insertBefore = 0); + + /** + * Removes an effect from the path. + * + * If the effect gets deleted while it is still connected the effect + * will be removed automatically. + * + * \param effect The effect to be removed. + * + * \return Returns whether the call was successful. If it returns + * \c false the effect could not be found in the path, meaning it + * has not been inserted before. + * + * \see insertEffect + * \see effects + */ + bool removeEffect(Effect *effect); + + /** + * Returns a list of Effect objects that are currently + * used as effects. The order in the list determines the order the + * signal is sent through the effects. + * + * \return A list with all current effects. + * + * \see insertEffect + * \see removeEffect + */ + QList<Effect *> effects() const; +#endif //QT_NO_PHONON_EFFECT + + /** + * Tries to change the MediaNodes the path is connected to. + * + * If reconnect fails the old connection is kept. + */ + bool reconnect(MediaNode *source, MediaNode *sink); + + /** + * Disconnects the path from the MediaNodes it was connected to. This invalidates the path + * (isValid returns \p false then). + */ + bool disconnect(); + + /** + * Assigns \p p to this Path and returns a reference to this Path. + * + * This operation is fast thanks to explicit sharing. + */ + Path &operator=(const Path &p); + + /** + * Returns \p true if this Path is equal to \p p; otherwise returns \p false; + */ + bool operator==(const Path &p) const; + + /** + * Returns \p true if this Path is not equal to \p p; otherwise returns \p false; + */ + bool operator!=(const Path &p) const; + + /** + * Returns the source MediaNode used by the path. + */ + MediaNode *source() const; + + /** + * Returns the sink MediaNode used by the path. + */ + MediaNode *sink() const; + + + protected: + friend class PathPrivate; + QExplicitlySharedDataPointer<PathPrivate> d; +}; + +/** + * \relates Path + * Creates a new Path connecting two MediaNodes. + * + * The implementation will automatically select the right format and media type. E.g. connecting a + * MediaObject and AudioOutput will create a Path object connecting the audio. This might be + * represented as PCM or perhaps even AC3 depending on the AudioOutput object. + * + * \param source The MediaNode to connect an output from + * \param sink The MediaNode to connect to. + */ +PHONON_EXPORT Path createPath(MediaNode *source, MediaNode *sink); + +} // namespace Phonon + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // PHONON_PATH_H diff --git a/src/3rdparty/phonon/phonon/path_p.h b/src/3rdparty/phonon/phonon/path_p.h new file mode 100644 index 0000000..1345ad5 --- /dev/null +++ b/src/3rdparty/phonon/phonon/path_p.h @@ -0,0 +1,79 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PATH_P_H +#define PATH_P_H + +#include "path.h" +#include <QtCore/QPair> +#include <QtCore/QList> +#include <QtCore/QSharedData> +#include "effect.h" +#include "medianodedestructionhandler_p.h" + +QT_BEGIN_NAMESPACE + +class QObject; + +namespace Phonon +{ + +class MediaNode; +typedef QPair<QObject*, QObject*> QObjectPair; + + +class PathPrivate : public QSharedData, private MediaNodeDestructionHandler +{ + friend class Path; + public: + PathPrivate() + : sourceNode(0), sinkNode(0) +#ifndef QT_NO_PHONON_EFFECT + , effectsParent(0) +#endif //QT_NO_PHONON_EFFECT + { + } + + ~PathPrivate(); + + MediaNode *sourceNode; + MediaNode *sinkNode; + + protected: + void phononObjectDestroyed(MediaNodePrivate *); + +#ifndef QT_NO_PHONON_EFFECT + QObject *effectsParent; // used as parent for Effects created in insertEffect + QList<Effect *> effects; +#endif + private: + bool executeTransaction( const QList<QObjectPair> &disconnections, const QList<QObjectPair> &connections); +#ifndef QT_NO_PHONON_EFFECT + bool removeEffect(Effect *effect); +#endif +}; + +} // namespace Phonon + +QT_END_NAMESPACE + +#endif // PATH_P_H diff --git a/src/3rdparty/phonon/phonon/phonon_export.h b/src/3rdparty/phonon/phonon/phonon_export.h new file mode 100644 index 0000000..e579f67 --- /dev/null +++ b/src/3rdparty/phonon/phonon/phonon_export.h @@ -0,0 +1,58 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_EXPORT_H +#define PHONON_EXPORT_H + +#include <QtCore/QtGlobal> + +#ifndef PHONON_EXPORT +# if defined Q_WS_WIN +# ifdef MAKE_PHONON_LIB /* We are building this library */ +# define PHONON_EXPORT Q_DECL_EXPORT +# else /* We are using this library */ +# define PHONON_EXPORT Q_DECL_IMPORT +# endif +# else /* UNIX */ +# define PHONON_EXPORT Q_DECL_EXPORT +# endif +#endif + +#ifndef PHONON_EXPORT_DEPRECATED +# define PHONON_EXPORT_DEPRECATED Q_DECL_DEPRECATED PHONON_EXPORT +#endif + +// QT_(BEGIN|END)_NAMESPACE appeared in 4.4 +#ifndef QT_BEGIN_NAMESPACE +# define QT_BEGIN_NAMESPACE +#endif +#ifndef QT_END_NAMESPACE +# define QT_END_NAMESPACE +#endif + +// silence syncqt +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +QT_END_NAMESPACE +QT_END_HEADER + +#endif diff --git a/src/3rdparty/phonon/phonon/phonondefs.h b/src/3rdparty/phonon/phonon/phonondefs.h new file mode 100644 index 0000000..d1a114a --- /dev/null +++ b/src/3rdparty/phonon/phonon/phonondefs.h @@ -0,0 +1,144 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONONDEFS_H +#define PHONONDEFS_H + +#include <QtCore/QtGlobal> +#include "phonon_export.h" + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +#ifdef PHONON_BACKEND_VERSION_4_3 +# ifndef PHONON_BACKEND_VERSION_4_2 +# define PHONON_BACKEND_VERSION_4_2 +# endif +#endif + +// the following inlines are correct - exclude per line doesn't work for multiline-macros so exclude +// the whole file for inline checks +//krazy:excludeall=inline +#define K_DECLARE_PRIVATE(Class) \ + inline Class##Private* k_func() { return reinterpret_cast<Class##Private *>(k_ptr); } \ + inline const Class##Private* k_func() const { return reinterpret_cast<const Class##Private *>(k_ptr); } \ + friend class Class##Private; + +/** + * \internal + * Used in class declarations to provide the needed functions. This is used for + * abstract base classes. + * + * \param classname The Name of the class this macro is used for. + * + * Example: + * \code + * class AbstractEffect : public QObject + * { + * Q _OBJECT + * Q_PROPERTY(int propertyA READ propertyA WRITE setPropertyA) + * PHONON_ABSTRACTBASE(AbstractEffect) + * public: + * int propertyA() const; + * void setPropertyA(int); + * }; + * \endcode + * + * \see PHONON_OBJECT + * \see PHONON_HEIR + */ +#define PHONON_ABSTRACTBASE(classname) \ +protected: \ + /** + * \internal + * Constructor that is called from derived classes. + * + * \param parent Standard QObject parent. + */ \ + classname(classname ## Private &dd, QObject *parent); \ +private: + +/** + * \internal + * Used in class declarations to provide the needed functions. This is used for + * classes that inherit QObject directly. + * + * \param classname The Name of the class this macro is used for. + * + * Example: + * \code + * class EffectSettings : public QObject + * { + * Q _OBJECT + * Q_PROPERTY(int propertyA READ propertyA WRITE setPropertyA) + * PHONON_OBJECT(EffectSettings) + * public: + * int propertyA() const; + * void setPropertyA(int); + * }; + * \endcode + * + * \see PHONON_ABSTRACTBASE + * \see PHONON_HEIR + */ +#define PHONON_OBJECT(classname) \ +public: \ + /** + * Constructs an object with the given \p parent. + */ \ + classname(QObject *parent = 0); \ +private: + +/** + * \internal + * Used in class declarations to provide the needed functions. This is used for + * classes that inherit another Phonon object. + * + * \param classname The Name of the class this macro is used for. + * + * Example: + * \code + * class ConcreteEffect : public AbstractEffect + * { + * Q _OBJECT + * Q_PROPERTY(int propertyB READ propertyB WRITE setPropertyB) + * PHONON_HEIR(ConcreteEffect) + * public: + * int propertyB() const; + * void setPropertyB(int); + * }; + * \endcode + * + * \see PHONON_ABSTRACTBASE + * \see PHONON_OBJECT + */ +#define PHONON_HEIR(classname) \ +public: \ + /** + * Constructs an object with the given \p parent. + */ \ + classname(QObject *parent = 0); \ + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // PHONONDEFS_H diff --git a/src/3rdparty/phonon/phonon/phonondefs_p.h b/src/3rdparty/phonon/phonon/phonondefs_p.h new file mode 100644 index 0000000..09037b5 --- /dev/null +++ b/src/3rdparty/phonon/phonon/phonondefs_p.h @@ -0,0 +1,369 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONONDEFS_P_H +#define PHONONDEFS_P_H + +#include <QtCore/QMetaType> +#include "medianode_p.h" + +#define K_D(Class) Class##Private *const d = k_func() + +#define PHONON_CONCAT_HELPER_INTERNAL(x, y) x ## y +#define PHONON_CONCAT_HELPER(x, y) PHONON_CONCAT_HELPER_INTERNAL(x, y) + +#define PHONON_PRIVATECLASS \ +protected: \ + virtual bool aboutToDeleteBackendObject(); \ + virtual void createBackendObject(); \ + /** + * \internal + * After construction of the Iface object this method is called + * throughout the complete class hierarchy in order to set up the + * properties that were already set on the public interface. + * + * An example implementation could look like this: + * \code + * ParentClassPrivate::setupBackendObject(); + * m_iface->setPropertyA(d->propertyA); + * m_iface->setPropertyB(d->propertyB); + * \endcode + */ \ + void setupBackendObject(); + +#define PHONON_PRIVATEABSTRACTCLASS \ +protected: \ + virtual bool aboutToDeleteBackendObject(); \ + /** + * \internal + * After construction of the Iface object this method is called + * throughout the complete class hierarchy in order to set up the + * properties that were already set on the public interface. + * + * An example implementation could look like this: + * \code + * ParentClassPrivate::setupBackendObject(); + * m_iface->setPropertyA(d->propertyA); + * m_iface->setPropertyB(d->propertyB); + * \endcode + */ \ + void setupBackendObject(); + +#define PHONON_ABSTRACTBASE_IMPL \ +PHONON_CLASSNAME::PHONON_CLASSNAME(PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) &dd, QObject *parent) \ + : QObject(parent), \ + MediaNode(dd) \ +{ \ +} + +#define PHONON_OBJECT_IMPL \ +PHONON_CLASSNAME::PHONON_CLASSNAME(QObject *parent) \ + : QObject(parent), \ + MediaNode(*new PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private)()) \ +{ \ +} \ +void PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private)::createBackendObject() \ +{ \ + if (m_backendObject) \ + return; \ + Q_Q(PHONON_CLASSNAME); \ + m_backendObject = Factory::PHONON_CONCAT_HELPER(create, PHONON_CLASSNAME)(q); \ + if (m_backendObject) { \ + setupBackendObject(); \ + } \ +} + +#define PHONON_HEIR_IMPL(parentclass) \ +PHONON_CLASSNAME::PHONON_CLASSNAME(QObject *parent) \ + : parentclass(*new PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private), parent) \ +{ \ +} \ +void PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private)::createBackendObject() \ +{ \ + if (m_backendObject) \ + return; \ + Q_Q(PHONON_CLASSNAME); \ + m_backendObject = Factory::PHONON_CONCAT_HELPER(create, PHONON_CLASSNAME)(q); \ + if (m_backendObject) { \ + setupBackendObject(); \ + } \ +} + +#define BACKEND_GET(returnType, returnVar, methodName) \ +QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar)) +#define BACKEND_GET1(returnType, returnVar, methodName, varType1, var1) \ +QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar), Q_ARG(varType1, var1)) +#define BACKEND_GET2(returnType, returnVar, methodName, varType1, var1, varType2, var2) \ +QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar), Q_ARG(varType1, var1), Q_ARG(varType2, var2)) +#define BACKEND_CALL(methodName) \ +QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection) +#define BACKEND_CALL1(methodName, varType1, var1) \ +QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1)) +#define BACKEND_CALL2(methodName, varType1, var1, varType2, var2) \ +QMetaObject::invokeMethod(d->m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1), Q_ARG(varType2, var2)) + +#define pBACKEND_GET(returnType, returnVar, methodName) \ +QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar)) +#define pBACKEND_GET1(returnType, returnVar, methodName, varType1, var1) \ +QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar), Q_ARG(varType1, var1)) +#define pBACKEND_GET2(returnType, returnVar, methodName, varType1, var1, varType2, var2) \ +QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_RETURN_ARG(returnType, returnVar), Q_ARG(varType1, var1), Q_ARG(varType2, var2)) +#define pBACKEND_CALL(methodName) \ +QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection) +#define pBACKEND_CALL1(methodName, varType1, var1) \ +QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1)) +#define pBACKEND_CALL2(methodName, varType1, var1, varType2, var2) \ +QMetaObject::invokeMethod(m_backendObject, methodName, Qt::DirectConnection, Q_ARG(varType1, var1), Q_ARG(varType2, var2)) + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace + { + class NoIface; + + /// All template arguments are valid + template<typename T> struct IsValid { enum { Result = true }; }; + /// except NoIface + template<> struct IsValid<NoIface> { enum { Result = false }; }; + + template<class T> inline T my_cast(QObject *o) { return qobject_cast<T>(o); } + template<class T> inline T my_cast(const QObject *o) { return qobject_cast<T>(o); } + + template<> inline NoIface *my_cast<NoIface *>(QObject *) { return 0; } + template<> inline NoIface *my_cast<NoIface *>(const QObject *) { return 0; } + } // anonymous namespace + + /** + * \internal + * + * \brief Helper class to cast the backend object to the correct version of the interface. + * + * Additions to the backend interfaces cannot be done by adding virtual methods as that would + * break the binary interface. So the old class is renamed and a new class with the old name + * inheriting the old class is added, containing all the new virtual methods. + * Example: + * \code + class FooInterface + { + public: + virtual ~FooInterface() {} + virtual oldMethod() = 0; + }; + Q_DECLARE_INTERFACE(FooInterface, "FooInterface0.phonon.kde.org") + * \endcode + * becomes + * \code + class FooInterface0 + { + public: + virtual ~FooInterface0() {} + virtual oldMethod() = 0; + }; + class FooInterface : public FooInterface0 + { + public: + virtual newMethod() = 0; + }; + Q_DECLARE_INTERFACE(FooInterface0, "FooInterface0.phonon.kde.org") + Q_DECLARE_INTERFACE(FooInterface, "FooInterface1.phonon.kde.org") + * \endcode + * + * With this, backends compiled against the old header can be qobject_casted to FooInterface0, + * but not to FooInterface. On the other hand backends compiled against the new header (they first + * need to implement newMethod) can only be qobject_casted to FooInterface but not to + * FooInterface0. (The qobject_cast relies on the string in Q_DECLARE_INTERFACE and not the + * class name which is why it behaves that way.) + * + * Now, in order to call oldMethod, the code needs to try to cast to both FooInterface and + * FooInterface0 (new backends will work with the former, old backends with the latter) and then + * if one of them in non-zero call oldMethod on it. + * + * To call newMethod only a cast to FooInterface needs to be done. + * + * The Iface class does all this for you for up to three (for now) interface revisions. Just + * create an object like this: + * \code + Iface<FooInterface0, FooInterface> iface0(d); + if (iface0) { + iface0->oldMethod(); + } + Iface<FooInterface> iface(d); + if (iface) { + iface->newMethod(); + } + * \endcode + * + * This becomes a bit more convenient if you add macros like this: + * \code + #define IFACES1 FooInterface + #define IFACES0 FooInterface0, IFACES1 + * \endcode + * which you can use like this: + * \code + Iface<IFACES0> iface0(d); + if (iface0) { + iface0->oldMethod(); + } + Iface<IFACES1> iface(d); + if (iface) { + iface->newMethod(); + } + * \endcode + * With the next revision you can then change the macros to + * \code + #define IFACES2 FooInterface + #define IFACES1 FooInterface1, IFACES2 + #define IFACES0 FooInterface0, IFACES1 + * \endcode + * + * \author Matthias Kretz <kretz@kde.org> + */ + template<class T0, class T1 = NoIface, class T2 = NoIface> + class Iface + { + public: + static inline T0 *cast(MediaNodePrivate *const d) + { + if (IsValid<T1>::Result) { + T0 *ret; + if (IsValid<T2>::Result) { + ret = reinterpret_cast<T0 *>(my_cast<T2 *>(d->m_backendObject)); + if (ret) return ret; + } + ret = reinterpret_cast<T0 *>(my_cast<T1 *>(d->m_backendObject)); + if (ret) return ret; + } + return qobject_cast<T0 *>(d->m_backendObject); + } + + static inline const T0 *cast(const MediaNodePrivate *const d) + { + if (IsValid<T1>::Result) { + const T0 *ret; + if (IsValid<T2>::Result) { + ret = reinterpret_cast<const T0 *>(my_cast<T2 *>(d->m_backendObject)); + if (ret) return ret; + } + ret = reinterpret_cast<const T0 *>(my_cast<T1 *>(d->m_backendObject)); + if (ret) return ret; + } + return qobject_cast<T0 *>(d->m_backendObject); + } + + inline Iface(MediaNodePrivate *const d) : iface(cast(d)) {} + inline operator T0 *() { return iface; } + inline operator const T0 *() const { return iface; } + inline T0 *operator->() { Q_ASSERT(iface); return iface; } + inline const T0 *operator->() const { Q_ASSERT(iface); return iface; } + private: + T0 *const iface; + }; + + template<class T0, class T1 = NoIface, class T2 = NoIface> + class ConstIface + { + public: + inline ConstIface(const MediaNodePrivate *const d) : iface(Iface<T0, T1, T2>::cast(d)) {} + inline operator const T0 *() const { return iface; } + inline const T0 *operator->() const { Q_ASSERT(iface); return iface; } + private: + const T0 *const iface; + }; +} // namespace Phonon + +QT_END_NAMESPACE + +#define INTERFACE_CALL(function) \ +Iface<PHONON_INTERFACENAME >::cast(d)->function + +#define pINTERFACE_CALL(function) \ +Iface<PHONON_INTERFACENAME >::cast(this)->function + +#define PHONON_GETTER(rettype, name, retdefault) \ +rettype PHONON_CLASSNAME::name() const \ +{ \ + const PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \ + if (!d->m_backendObject) \ + return retdefault; \ + rettype ret; \ + BACKEND_GET(rettype, ret, #name); \ + return ret; \ +} + +#define PHONON_INTERFACE_GETTER(rettype, name, retdefault) \ +rettype PHONON_CLASSNAME::name() const \ +{ \ + const PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \ + if (!d->m_backendObject) \ + return retdefault; \ + return Iface<PHONON_INTERFACENAME >::cast(d)->name(); \ +} + +#define PHONON_GETTER1(rettype, name, retdefault, argtype1, argvar1) \ +rettype PHONON_CLASSNAME::name(argtype1 argvar1) const \ +{ \ + const PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \ + if (!d->m_backendObject) \ + return retdefault; \ + rettype ret; \ + BACKEND_GET1(rettype, ret, #name, const QObject *, argvar1->k_ptr->backendObject()); \ + return ret; \ +} + +#define PHONON_INTERFACE_GETTER1(rettype, name, retdefault, argtype1, argvar1) \ +rettype PHONON_CLASSNAME::name(argtype1 argvar1) const \ +{ \ + const PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \ + if (!d->m_backendObject) \ + return retdefault; \ + return Iface<PHONON_INTERFACENAME >::cast(d)->name(argvar1->k_ptr->backendObject()); \ +} + +#define PHONON_SETTER(functionname, privatevar, argtype1) \ +void PHONON_CLASSNAME::functionname(argtype1 x) \ +{ \ + PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \ + d->privatevar = x; \ + if (k_ptr->backendObject()) { \ + BACKEND_CALL1(#functionname, argtype1, x); \ + } \ +} + +#define PHONON_INTERFACE_SETTER(functionname, privatevar, argtype1) \ +void PHONON_CLASSNAME::functionname(argtype1 x) \ +{ \ + PHONON_CONCAT_HELPER(PHONON_CLASSNAME, Private) *d = k_func(); \ + d->privatevar = x; \ + if (k_ptr->backendObject()) { \ + Iface<PHONON_INTERFACENAME >::cast(d)->functionname(x); \ + } \ +} + +#ifndef METATYPE_QLIST_INT_DEFINED +#define METATYPE_QLIST_INT_DEFINED +// Want this exactly once, see phonondefs_p.h kcm/outputdevicechoice.cpp +Q_DECLARE_METATYPE(QList<int>) +#endif + +#endif // PHONONDEFS_P_H diff --git a/src/3rdparty/phonon/phonon/phononnamespace.cpp b/src/3rdparty/phonon/phonon/phononnamespace.cpp new file mode 100644 index 0000000..f594d3c --- /dev/null +++ b/src/3rdparty/phonon/phonon/phononnamespace.cpp @@ -0,0 +1,92 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#include "phononnamespace.h" +#include "phononnamespace_p.h" +#include "phonondefs_p.h" + +#include "factory_p.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + /*! + Returns the version number of Phonon at run-time as a string (for + example, "4.0.0"). This may be a different version than the + version the application was compiled against. + + \sa PHONON_VERSION_STR + */ + const char *phononVersion() + { + return PHONON_VERSION_STR; + } + + QString categoryToString(Category c) + { + switch(c) + { + case Phonon::NoCategory: + break; + case Phonon::NotificationCategory: + return QCoreApplication::translate("Phonon::", "Notifications"); + case Phonon::MusicCategory: + return QCoreApplication::translate("Phonon::", "Music"); + case Phonon::VideoCategory: + return QCoreApplication::translate("Phonon::", "Video"); + case Phonon::CommunicationCategory: + return QCoreApplication::translate("Phonon::", "Communication"); + case Phonon::GameCategory: + return QCoreApplication::translate("Phonon::", "Games"); + case Phonon::AccessibilityCategory: + return QCoreApplication::translate("Phonon::", "Accessibility"); + } + return QString(); + } +} + +static int registerPhononMetaTypes() +{ + qRegisterMetaType<Phonon::State>(); + qRegisterMetaType<Phonon::ErrorType>(); + qRegisterMetaType<Phonon::Category>(); + + // need those for QSettings + qRegisterMetaType<QList<int> >(); + qRegisterMetaTypeStreamOperators<QList<int> >("QList<int>"); + + return 0; // something +} + +#ifdef Q_CONSTRUCTOR_FUNCTION +Q_CONSTRUCTOR_FUNCTION(registerPhononMetaTypes) +#else +static const int _Phonon_registerMetaTypes = registerPhononMetaTypes(); +#endif + +QT_END_NAMESPACE + +// vim: sw=4 ts=4 diff --git a/src/3rdparty/phonon/phonon/phononnamespace.h b/src/3rdparty/phonon/phonon/phononnamespace.h new file mode 100644 index 0000000..0bbf4f4 --- /dev/null +++ b/src/3rdparty/phonon/phonon/phononnamespace.h @@ -0,0 +1,306 @@ +/* This file is part of the KDE project + Copyright (C) 2005-2008 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONONNAMESPACE_H +#define PHONONNAMESPACE_H + +#include "phonon_export.h" + +/** + * Helper macro that can be used like + * \code + * #if (PHONON_VERSION >= PHONON_VERSION_CHECK(4, 4, 0)) + * \endcode + */ +#define PHONON_VERSION_CHECK(major, minor, patch) ((major<<16)|(minor<<8)|(patch)) + +/** + * PHONON_VERSION is (major << 16) + (minor << 8) + patch. + */ +#define PHONON_VERSION PHONON_VERSION_CHECK(4, 3, 1) + +/** + * PHONON_VERSION_STR is "major.minor.patch". E.g. "4.2.1" + */ +#define PHONON_VERSION_STR "4.3.1" + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +class QString; + +/** + * \brief The %KDE Multimedia classes + * + * In this Namespace you find the classes to access Multimedia functions for + * audio and video playback. Those classes are not dependent + * on any specific framework (like they were in pre KDE4 times) but rather use + * exchangeable backends to do the work. + * + * If you want to write a new backend take a look at \ref phonon_backend_development_page. + * + * \author Matthias Kretz <kretz@kde.org> + */ +namespace Phonon +{ + PHONON_EXPORT const char *phononVersion(); + + /** + * Enum to identify the media discs supported by MediaObject. + * + * \see MediaSource(Phonon::DiscType, const QString &deviceName) + */ + enum DiscType { + /** + * No disc was selected. This is only useful as a return value from + * MediaSource::distType(); + */ + NoDisc = -1, + /** + * Identifies Audio CDs. + */ + Cd = 0, + /** + * Identifies DVDs (not arbitrary data DVDs, only movie DVDs). + */ + Dvd = 1, + /** + * Identifies Video CDs. + */ + Vcd = 2 + }; + + /** + * Provided as keys for \ref MediaObject::metaData for convenience, in addition to the strings defined in + * the Ogg Vorbis specification. + */ + enum MetaData { + /** + * The artist generally considered responsible for the work. In popular + * music this is usually the performing band or singer. For classical + * music it would be the composer. For an audio book it would be the + * author of the original text. + */ + ArtistMetaData, + /** + * The collection name to which this track belongs. + */ + AlbumMetaData, + /** + * Track/Work name + */ + TitleMetaData, + /** + * Date the track was recorded + */ + DateMetaData, + /** + * A short text indication of music genre + */ + GenreMetaData, + /** + * The track number of this piece if part of a specific larger + * collection or album + */ + TracknumberMetaData, + /** + * A short text description of the contents + */ + DescriptionMetaData, + MusicBrainzDiscIdMetaData + }; + + /** + * The state the media producing object is in at the moment. + * + * \see MediaObject + */ + enum State + { + /** + * After construction it might take a while before the Player is + * ready to play(). Normally this doesn't happen for local + * files, but can happen for remote files where the asynchronous + * mimetype detection and prebuffering can take a while. + */ + LoadingState, + /** + * The Player has a valid media file loaded and is ready for + * playing. + */ + StoppedState, + /** + * The Player is playing a media file. + */ + PlayingState, + /** + * The Player is waiting for data to be able to continue + * playing. + */ + BufferingState, + /** + * The Player is currently paused. + */ + PausedState, + /** + * An unrecoverable error occurred. The Object is unusable in this state. + */ + ErrorState + }; + + /** + * Set's the category your program should be listed in in the mixer. + * + * A Jukebox will set this to Music, a VoIP program to Communication, a + * DVD player to video, and so on. + * + * \note These categories can also become useful for an application that + * controls the volumes automatically, like turning down the music when a call + * comes in, or turning down the notifications when the media player knows + * it's playing classical music. + * + * \see AudioOutput::setCategory + */ + enum Category + { + /** + * Will make use of the default device. + */ + NoCategory = -1, + /** + * If the sounds produced are notifications (bing, beep and such) you + * should use this category. + */ + NotificationCategory = 0, + /** + * If your application is a music player (like a jukebox or media player + * playing an audio file). + */ + MusicCategory = 1, + /** + * If the sound is the audio channel of a video. + */ + VideoCategory = 2, + /** + * If your applications produces sounds from communication with somebody + * else (VoIP, voice chat). + */ + CommunicationCategory = 3, + /** + * Sound produced by a computer game should go into this category. + */ + GameCategory = 4, + /** + * Sounds produced for accessibility (e.g. Text-To-Speech) + */ + AccessibilityCategory = 5, + /** + * \internal + * Holds the largest value of categories. + */ + LastCategory = AccessibilityCategory + }; + + /** + * Tells your program how to recover from an error. + * + * \see MediaObject::errorType() + */ + enum ErrorType { + /** + * No error. MediaObject::errorType() returns this if + * MediaObject::state() != Phonon::ErrorState. + */ + NoError = 0, + /** + * Playback should work, and trying with another URL should work. + */ + NormalError = 1, + /** + * Something important does not work. Your program cannot continue + * playback or capture or whatever it was trying to do + * without help from the user. + */ + FatalError = 2 + }; + + /** + * Returns a (translated) string to show to the user identifying the given + * Category. + */ + PHONON_EXPORT QString categoryToString(Category c); + + // TODO: naming + /*enum MediaStreamType { + Audio = 1, + Video = 2, + StillImage = 4, + Subtitle = 8, + AllMedia = 0xFFFFFFFF + }; + Q_DECLARE_FLAGS(MediaStreamTypes, MediaStreamType)*/ +} // namespace Phonon +//Q_DECLARE_OPERATORS_FOR_FLAGS(Phonon::MediaStreamTypes) + +QT_END_NAMESPACE + +//X class kdbgstream; +//X #include <kdebug.h> +//X /** +//X * Implements needed operator to use Phonon::State with kDebug +//X */ +//X inline PHONON_EXPORT kdbgstream &operator<<(kdbgstream & stream, const Phonon::State state) +//X { +//X switch(state) +//X { +//X case Phonon::ErrorState: +//X stream << "ErrorState"; +//X break; +//X case Phonon::LoadingState: +//X stream << "LoadingState"; +//X break; +//X case Phonon::StoppedState: +//X stream << "StoppedState"; +//X break; +//X case Phonon::PlayingState: +//X stream << "PlayingState"; +//X break; +//X case Phonon::BufferingState: +//X stream << "BufferingState"; +//X break; +//X case Phonon::PausedState: +//X stream << "PausedState"; +//X break; +//X } +//X return stream; +//X } + +#include <QtCore/QMetaType> + +Q_DECLARE_METATYPE(Phonon::State) +Q_DECLARE_METATYPE(Phonon::ErrorType) +Q_DECLARE_METATYPE(Phonon::Category) + +QT_END_HEADER + +// vim: sw=4 ts=4 tw=80 +#endif // PHONONNAMESPACE_H diff --git a/src/3rdparty/phonon/phonon/phononnamespace.h.in b/src/3rdparty/phonon/phonon/phononnamespace.h.in new file mode 100644 index 0000000..54c5578 --- /dev/null +++ b/src/3rdparty/phonon/phonon/phononnamespace.h.in @@ -0,0 +1,306 @@ +/* This file is part of the KDE project + Copyright (C) 2005-2008 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONONNAMESPACE_H +#define PHONONNAMESPACE_H + +#include "phonon_export.h" + +/** + * Helper macro that can be used like + * \code + * #if (PHONON_VERSION >= PHONON_VERSION_CHECK(4, 4, 0)) + * \endcode + */ +#define PHONON_VERSION_CHECK(major, minor, patch) ((major<<16)|(minor<<8)|(patch)) + +/** + * PHONON_VERSION is (major << 16) + (minor << 8) + patch. + */ +#define PHONON_VERSION PHONON_VERSION_CHECK(@PHONON_LIB_MAJOR_VERSION@, @PHONON_LIB_MINOR_VERSION@, @PHONON_LIB_PATCH_VERSION@) + +/** + * PHONON_VERSION_STR is "major.minor.patch". E.g. "4.2.1" + */ +#define PHONON_VERSION_STR "@PHONON_LIB_MAJOR_VERSION@.@PHONON_LIB_MINOR_VERSION@.@PHONON_LIB_PATCH_VERSION@" + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +class QString; + +/** + * \brief The %KDE Multimedia classes + * + * In this Namespace you find the classes to access Multimedia functions for + * audio and video playback. Those classes are not dependent + * on any specific framework (like they were in pre KDE4 times) but rather use + * exchangeable backends to do the work. + * + * If you want to write a new backend take a look at \ref phonon_backend_development_page. + * + * \author Matthias Kretz <kretz@kde.org> + */ +namespace Phonon +{ + PHONON_EXPORT const char *phononVersion(); + + /** + * Enum to identify the media discs supported by MediaObject. + * + * \see MediaSource(Phonon::DiscType, const QString &deviceName) + */ + enum DiscType { + /** + * No disc was selected. This is only useful as a return value from + * MediaSource::distType(); + */ + NoDisc = -1, + /** + * Identifies Audio CDs. + */ + Cd = 0, + /** + * Identifies DVDs (not arbitrary data DVDs, only movie DVDs). + */ + Dvd = 1, + /** + * Identifies Video CDs. + */ + Vcd = 2 + }; + + /** + * Provided as keys for \ref MediaObject::metaData for convenience, in addition to the strings defined in + * the Ogg Vorbis specification. + */ + enum MetaData { + /** + * The artist generally considered responsible for the work. In popular + * music this is usually the performing band or singer. For classical + * music it would be the composer. For an audio book it would be the + * author of the original text. + */ + ArtistMetaData, + /** + * The collection name to which this track belongs. + */ + AlbumMetaData, + /** + * Track/Work name + */ + TitleMetaData, + /** + * Date the track was recorded + */ + DateMetaData, + /** + * A short text indication of music genre + */ + GenreMetaData, + /** + * The track number of this piece if part of a specific larger + * collection or album + */ + TracknumberMetaData, + /** + * A short text description of the contents + */ + DescriptionMetaData, + MusicBrainzDiscIdMetaData + }; + + /** + * The state the media producing object is in at the moment. + * + * \see MediaObject + */ + enum State + { + /** + * After construction it might take a while before the Player is + * ready to play(). Normally this doesn't happen for local + * files, but can happen for remote files where the asynchronous + * mimetype detection and prebuffering can take a while. + */ + LoadingState, + /** + * The Player has a valid media file loaded and is ready for + * playing. + */ + StoppedState, + /** + * The Player is playing a media file. + */ + PlayingState, + /** + * The Player is waiting for data to be able to continue + * playing. + */ + BufferingState, + /** + * The Player is currently paused. + */ + PausedState, + /** + * An unrecoverable error occurred. The Object is unusable in this state. + */ + ErrorState + }; + + /** + * Set's the category your program should be listed in in the mixer. + * + * A Jukebox will set this to Music, a VoIP program to Communication, a + * DVD player to video, and so on. + * + * \note These categories can also become useful for an application that + * controls the volumes automatically, like turning down the music when a call + * comes in, or turning down the notifications when the media player knows + * it's playing classical music. + * + * \see AudioOutput::setCategory + */ + enum Category + { + /** + * Will make use of the default device. + */ + NoCategory = -1, + /** + * If the sounds produced are notifications (bing, beep and such) you + * should use this category. + */ + NotificationCategory = 0, + /** + * If your application is a music player (like a jukebox or media player + * playing an audio file). + */ + MusicCategory = 1, + /** + * If the sound is the audio channel of a video. + */ + VideoCategory = 2, + /** + * If your applications produces sounds from communication with somebody + * else (VoIP, voice chat). + */ + CommunicationCategory = 3, + /** + * Sound produced by a computer game should go into this category. + */ + GameCategory = 4, + /** + * Sounds produced for accessibility (e.g. Text-To-Speech) + */ + AccessibilityCategory = 5, + /** + * \internal + * Holds the largest value of categories. + */ + LastCategory = AccessibilityCategory + }; + + /** + * Tells your program how to recover from an error. + * + * \see MediaObject::errorType() + */ + enum ErrorType { + /** + * No error. MediaObject::errorType() returns this if + * MediaObject::state() != Phonon::ErrorState. + */ + NoError = 0, + /** + * Playback should work, and trying with another URL should work. + */ + NormalError = 1, + /** + * Something important does not work. Your program cannot continue + * playback or capture or whatever it was trying to do + * without help from the user. + */ + FatalError = 2 + }; + + /** + * Returns a (translated) string to show to the user identifying the given + * Category. + */ + PHONON_EXPORT QString categoryToString(Category c); + + // TODO: naming + /*enum MediaStreamType { + Audio = 1, + Video = 2, + StillImage = 4, + Subtitle = 8, + AllMedia = 0xFFFFFFFF + }; + Q_DECLARE_FLAGS(MediaStreamTypes, MediaStreamType)*/ +} // namespace Phonon +//Q_DECLARE_OPERATORS_FOR_FLAGS(Phonon::MediaStreamTypes) + +QT_END_NAMESPACE + +//X class kdbgstream; +//X #include <kdebug.h> +//X /** +//X * Implements needed operator to use Phonon::State with kDebug +//X */ +//X inline PHONON_EXPORT kdbgstream &operator<<(kdbgstream & stream, const Phonon::State state) +//X { +//X switch(state) +//X { +//X case Phonon::ErrorState: +//X stream << "ErrorState"; +//X break; +//X case Phonon::LoadingState: +//X stream << "LoadingState"; +//X break; +//X case Phonon::StoppedState: +//X stream << "StoppedState"; +//X break; +//X case Phonon::PlayingState: +//X stream << "PlayingState"; +//X break; +//X case Phonon::BufferingState: +//X stream << "BufferingState"; +//X break; +//X case Phonon::PausedState: +//X stream << "PausedState"; +//X break; +//X } +//X return stream; +//X } + +#include <QtCore/QMetaType> + +Q_DECLARE_METATYPE(Phonon::State) +Q_DECLARE_METATYPE(Phonon::ErrorType) +Q_DECLARE_METATYPE(Phonon::Category) + +QT_END_HEADER + +// vim: sw=4 ts=4 tw=80 +#endif // PHONONNAMESPACE_H diff --git a/src/3rdparty/phonon/phonon/phononnamespace_p.h b/src/3rdparty/phonon/phonon/phononnamespace_p.h new file mode 100644 index 0000000..4dd0ee8 --- /dev/null +++ b/src/3rdparty/phonon/phonon/phononnamespace_p.h @@ -0,0 +1,38 @@ +/* This file is part of the KDE project + Copyright (C) 2005-2006 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONONNAMESPACE_P_H +#define PHONONNAMESPACE_P_H + +#include <QtCore/QDebug> + +#if defined(QT_NO_DEBUG) +#define pDebug if (true) {} else qDebug +#else +#define pDebug if (qgetenv("PHONON_DEBUG").isEmpty()) {} else qDebug +#endif +#define pWarning() qDebug() << "WARNING:" +#define pError() qDebug() << "ERROR:" +#define pFatal(message) qDebug() << "FATAL ERROR:" << message; ::abort() + +// vim: sw=4 ts=4 tw=80 +#endif // PHONONNAMESPACE_P_H diff --git a/src/3rdparty/phonon/phonon/platform.cpp b/src/3rdparty/phonon/phonon/platform.cpp new file mode 100644 index 0000000..ed660dc --- /dev/null +++ b/src/3rdparty/phonon/phonon/platform.cpp @@ -0,0 +1,144 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#include "platform_p.h" +#include "platformplugin.h" +#include "factory_p.h" +#include <QtCore/QCoreApplication> +#include <QtCore/QUrl> +#include <QtGui/QIcon> +#include <QtGui/QStyle> +#include <QtGui/QApplication> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + +void Platform::saveVolume(const QString &outputName, qreal volume) +{ +#ifndef QT_NO_PHONON_PLATFORMPLUGIN + PlatformPlugin *f = Factory::platformPlugin(); + if (f) { + f->saveVolume(outputName, volume); + } +#else + Q_UNUSED(outputName); + Q_UNUSED(volume); +#endif //QT_NO_PHONON_PLATFORMPLUGIN +} + +qreal Platform::loadVolume(const QString &outputName) +{ +#ifndef QT_NO_PHONON_PLATFORMPLUGIN + const PlatformPlugin *f = Factory::platformPlugin(); + if (f) { + return f->loadVolume(outputName); + } +#else + Q_UNUSED(outputName); +#endif //QT_NO_PHONON_PLATFORMPLUGIN + return 1.0; +} + +AbstractMediaStream *Platform::createMediaStream(const QUrl &url, QObject *parent) +{ +#ifndef QT_NO_PHONON_PLATFORMPLUGIN + PlatformPlugin *f = Factory::platformPlugin(); + if (f) { + return f->createMediaStream(url, parent); + } +#else + Q_UNUSED(url); + Q_UNUSED(parent); +#endif //QT_NO_PHONON_PLATFORMPLUGIN + return 0; +} + +QIcon Platform::icon(const QString &name, QStyle *style) +{ + QIcon ret; +#ifndef QT_NO_PHONON_PLATFORMPLUGIN + if (const PlatformPlugin *f = Factory::platformPlugin()) { + ret = f->icon(name); + } +#endif //QT_NO_PHONON_PLATFORMPLUGIN + if (ret.isNull()) { + if (!style) { + style = QApplication::style(); + } + if (name == QLatin1String("player-volume")) { + ret = style->standardPixmap(QStyle::SP_MediaVolume); + } else if (name == QLatin1String("player-volume-muted")) { + ret = style->standardPixmap(QStyle::SP_MediaVolumeMuted); + } + } + + return ret; +} + +void Platform::notification(const char *notificationName, const QString &text, + const QStringList &actions, QObject *receiver, + const char *actionSlot) +{ +#ifndef QT_NO_PHONON_PLATFORMPLUGIN + const PlatformPlugin *f = Factory::platformPlugin(); + if (f) { + f->notification(notificationName, text, actions, receiver, actionSlot); + } +#else + Q_UNUSED(notificationName); + Q_UNUSED(text); + Q_UNUSED(actions); + Q_UNUSED(receiver); + Q_UNUSED(actionSlot); +#endif //QT_NO_PHONON_PLATFORMPLUGIN +} + +QString Platform::applicationName() +{ +#ifndef QT_NO_PHONON_PLATFORMPLUGIN + const PlatformPlugin *f = Factory::platformPlugin(); + if (f) { + return f->applicationName(); + } +#endif //QT_NO_PHONON_PLATFORMPLUGIN + QString ret = QCoreApplication::applicationName(); + if (ret.isEmpty()) + ret = QCoreApplication::applicationFilePath(); + return ret; +} + +QList<QPair<QByteArray, QString> > Platform::deviceAccessListFor(const Phonon::AudioOutputDevice &deviceDesc) +{ +#ifndef QT_NO_PHONON_PLATFORMPLUGIN + const PlatformPlugin *f = Factory::platformPlugin(); + if (f) { + return f->deviceAccessListFor(deviceDesc); + } +#endif //QT_NO_PHONON_PLATFORMPLUGIN + return QList<QPair<QByteArray, QString> >(); +} + +} // namespace Phonon + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/phonon/platform_p.h b/src/3rdparty/phonon/phonon/platform_p.h new file mode 100644 index 0000000..379c54b --- /dev/null +++ b/src/3rdparty/phonon/phonon/platform_p.h @@ -0,0 +1,62 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_PLATFORM_P_H +#define PHONON_PLATFORM_P_H + + +#include <QtCore/QStringList> +#include <QtCore/QtGlobal> +#include <QtCore/QPair> +#include "phonon_export.h" +#include "objectdescription.h" + +QT_BEGIN_NAMESPACE + +class QIcon; +class QObject; +class QUrl; +class QStyle; + +namespace Phonon +{ +class AbstractMediaStream; + +namespace Platform +{ + +void saveVolume(const QString &outputName, qreal volume); +qreal loadVolume(const QString &outputName); +AbstractMediaStream *createMediaStream(const QUrl &url, QObject *parent); +QIcon icon(const QString &name, QStyle *style = 0); +void notification(const char *notificationName, const QString &text, + const QStringList &actions = QStringList(), QObject *receiver = 0, + const char *actionSlot = 0); +QString applicationName(); +QList<QPair<QByteArray, QString> > deviceAccessListFor(const Phonon::AudioOutputDevice &deviceDesc); + +} // namespace Platform +} // namespace Phonon + +QT_END_NAMESPACE + +#endif // PHONON_PLATFORM_P_H diff --git a/src/3rdparty/phonon/phonon/platformplugin.h b/src/3rdparty/phonon/phonon/platformplugin.h new file mode 100644 index 0000000..e1ab1b7 --- /dev/null +++ b/src/3rdparty/phonon/phonon/platformplugin.h @@ -0,0 +1,118 @@ +/* This file is part of the KDE project + Copyright (C) 2007-2008 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_PLATFORMPLUGIN_H +#define PHONON_PLATFORMPLUGIN_H + +#include <QtCore/QObject> +#include <QtCore/QStringList> +#include <QtCore/QPair> +#include "phonon_export.h" +#include "objectdescription.h" + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_PLATFORMPLUGIN + +class QUrl; +class QObject; +class QIcon; + +namespace Phonon +{ +class AbstractMediaStream; + +class PlatformPlugin +{ + public: + virtual ~PlatformPlugin() {} + + /** + * Creates a AbstractMediaStream object that provides the data for the given \p url. On KDE + * this uses KIO. + */ + virtual AbstractMediaStream *createMediaStream(const QUrl &url, QObject *parent) = 0; + + /** + * Returns the icon for the given icon name. + */ + virtual QIcon icon(const QString &name) const = 0; + + /** + * Shows a notification popup + */ + virtual void notification(const char *notificationName, const QString &text, + const QStringList &actions = QStringList(), QObject *receiver = 0, + const char *actionSlot = 0) const = 0; + + /** + * Returns the name of the application. For most Qt application this is + * QCoreApplication::applicationName(), but for KDE this is overridden by KAboutData. + */ + virtual QString applicationName() const = 0; + + /** + * Creates a backend object. This way the platform can decide the backend preference. + */ + virtual QObject *createBackend() = 0; + + /** + * Using the library loader of the platform, loads a given backend. + */ + virtual QObject *createBackend(const QString &library, const QString &version) = 0; + + /** + * Tries to check whether the default backend supports a given MIME type without loading the + * actual backend library. On KDE this reads the MIME type list from the .desktop file of + * the backend. + */ + virtual bool isMimeTypeAvailable(const QString &mimeType) const = 0; + + /** + * Saves the volume for the given output. + */ + virtual void saveVolume(const QString &outputName, qreal volume) = 0; + + /** + * Loads the volume for the given output. + */ + virtual qreal loadVolume(const QString &outputName) const = 0; + + virtual QList<int> objectDescriptionIndexes(ObjectDescriptionType type) const = 0; + virtual QHash<QByteArray, QVariant> objectDescriptionProperties(ObjectDescriptionType type, int index) const = 0; + + /** + * Returns a list of (driver, handle) pairs for the given AudioOutputDevice description. + */ + virtual QList<QPair<QByteArray, QString> > deviceAccessListFor(const Phonon::AudioOutputDevice &) const { return QList<QPair<QByteArray, QString> >(); } +}; +} // namespace Phonon + +Q_DECLARE_INTERFACE(Phonon::PlatformPlugin, "3PlatformPlugin.phonon.kde.org") + +#endif //QT_NO_PHONON_PLATFORMPLUGIN + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // PHONON_PLATFORMPLUGIN_H diff --git a/src/3rdparty/phonon/phonon/preprocessandextract.sh b/src/3rdparty/phonon/phonon/preprocessandextract.sh new file mode 100755 index 0000000..13b1704 --- /dev/null +++ b/src/3rdparty/phonon/phonon/preprocessandextract.sh @@ -0,0 +1,39 @@ +#!/bin/sh +EXTRACT="`dirname $0`/extractmethodcalls.rb" +IGNORE="^\(streameventqueue\|abstractmediastream2\|lockfreequeue\|path\|platform\|iodevicestream\|medianode\|streaminterface\|mediasource\|abstractmediastream\|audioplayer\|globalconfig\|objectdescriptionmodel\|audiooutputadaptor\|effectwidget\|videoplayer\|seekslider\|volumeslider\).cpp$" + +if test -n "$1" -a -f "$1"; then + echo "preprocessing $1" + cpp $1 2>/dev/null > tmp + echo "extracting backend calls from $1" + $EXTRACT tmp > tests/methods/$1 + rm tmp +else + for i in *.cpp; do + if echo $i | grep -q "$IGNORE"; then + printf "%-30s ignored.\n" "$i:" + elif echo $i | grep -q '_p\.cpp$'; then + printf "%-30s postponed.\n" "$i:" + else + printf "%-30s preprocessing" "$i:" + cpp $i 2>/dev/null > tmp + echo -n ", extracting backend calls" + $EXTRACT tmp > tests/methods/$i + rm tmp + echo "." + fi + done + for i in *_p.cpp; do + cpp=`echo $i | sed 's,_p\.cpp$,\.cpp,'` + if echo $cpp | grep -q "$IGNORE"; then + printf "%-30s ignored.\n" "$i:" + elif test "$i" != "*_p.cpp"; then + printf "%-30s preprocessing" "$i:" + cpp $i 2>/dev/null > tmp + echo -n ", extracting backend calls" + $EXTRACT tmp >> tests/methods/$cpp + rm tmp + echo "." + fi + done +fi diff --git a/src/3rdparty/phonon/phonon/qsettingsgroup_p.h b/src/3rdparty/phonon/phonon/qsettingsgroup_p.h new file mode 100644 index 0000000..95f6c9b --- /dev/null +++ b/src/3rdparty/phonon/phonon/qsettingsgroup_p.h @@ -0,0 +1,91 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_QSETTINGSGROUP_P_H +#define PHONON_QSETTINGSGROUP_P_H + +#include <QtCore/QSettings> +#include <QtCore/QString> +#include <QtCore/QVariant> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +class QSettingsGroup +{ + public: + inline QSettingsGroup(QSettings *settings, const QString &name) + : m_mutableSettings(settings), + m_settings(settings), + m_group(name + QLatin1Char('/')) + { + } + + inline QSettingsGroup(const QSettings *settings, const QString &name) + : m_mutableSettings(0), + m_settings(settings), + m_group(name + QLatin1Char('/')) + { + } + + template<typename T> + inline T value(const QString &key, const T &def) const + { + return qvariant_cast<T>(value(key, qVariantFromValue(def))); + } + + inline QVariant value(const QString &key, const QVariant &def) const + { + return m_settings->value(m_group + key, def); + } + + template<typename T> + inline void setValue(const QString &key, const T &value) + { + Q_ASSERT(m_mutableSettings); + m_mutableSettings->setValue(m_group + key, qVariantFromValue(value)); + } + + inline void removeEntry(const QString &key) + { + Q_ASSERT(m_mutableSettings); + m_mutableSettings->remove(m_group + key); + } + + inline bool hasKey(const QString &key) const + { + return m_settings->contains(m_group + key); + } + + private: + QSettings *const m_mutableSettings; + const QSettings *const m_settings; + QString m_group; +}; +} // namespace Phonon + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // PHONON_QSETTINGSGROUP_P_H diff --git a/src/3rdparty/phonon/phonon/seekslider.cpp b/src/3rdparty/phonon/phonon/seekslider.cpp new file mode 100644 index 0000000..e1eea54 --- /dev/null +++ b/src/3rdparty/phonon/phonon/seekslider.cpp @@ -0,0 +1,263 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#include "seekslider.h" +#include "seekslider_p.h" +#include "mediaobject.h" +#include "phonondefs_p.h" + +#include <QtGui/QMouseEvent> +#include <QtGui/QApplication> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_SEEKSLIDER + +namespace Phonon +{ + +SeekSlider::SeekSlider(QWidget *parent) + : QWidget(parent) + , k_ptr(new SeekSliderPrivate(this)) +{ + K_D(SeekSlider); + connect(&d->slider, SIGNAL(valueChanged(int)), SLOT(_k_seek(int))); +} + +SeekSlider::SeekSlider(MediaObject *mo, QWidget *parent) + : QWidget(parent) + , k_ptr(new SeekSliderPrivate(this)) +{ + K_D(SeekSlider); + connect(&d->slider, SIGNAL(valueChanged(int)), SLOT(_k_seek(int))); + setMediaObject(mo); +} + +/*SeekSlider::SeekSlider(SeekSliderPrivate &_d, QWidget *parent) + : QWidget(parent) + , k_ptr(&_d) +{ +} */ + +SeekSlider::~SeekSlider() +{ + delete k_ptr; +} + +void SeekSlider::setMediaObject(MediaObject *media) +{ + K_D(SeekSlider); + if (d->media) { + disconnect(d->media, 0, this, 0); + } + d->media = media; + + if (media) { + connect(media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), + SLOT(_k_stateChanged(Phonon::State))); + connect(media, SIGNAL(totalTimeChanged(qint64)), SLOT(_k_length(qint64))); + connect(media, SIGNAL(tick(qint64)), SLOT(_k_tick(qint64))); + connect(media, SIGNAL(seekableChanged(bool)), SLOT(_k_seekableChanged(bool))); + connect(media, SIGNAL(currentSourceChanged(const Phonon::MediaSource&)), SLOT(_k_currentSourceChanged())); + d->_k_stateChanged(media->state()); + d->_k_seekableChanged(media->isSeekable()); + d->_k_length(media->totalTime()); + } else { + d->_k_stateChanged(Phonon::StoppedState); + d->_k_seekableChanged(false); + } +} + +MediaObject *SeekSlider::mediaObject() const +{ + K_D(const SeekSlider); + return d->media; +} + +void SeekSliderPrivate::_k_seek(int msec) +{ + if (!ticking && media) { + media->seek(msec); + } +} + +void SeekSliderPrivate::_k_tick(qint64 msec) +{ + ticking = true; + slider.setValue(msec); + ticking = false; +} + +void SeekSliderPrivate::_k_length(qint64 msec) +{ + ticking = true; + slider.setRange(0, msec); + ticking = false; +} + +void SeekSliderPrivate::_k_seekableChanged(bool isSeekable) +{ + if (!isSeekable || !media) { + setEnabled(false); + } else { + switch (media->state()) { + case Phonon::PlayingState: + if (media->tickInterval() == 0) { + // if the tick signal is not enabled the slider is useless + // set the tickInterval to some common value + media->setTickInterval(350); + } + case Phonon::BufferingState: + case Phonon::PausedState: + setEnabled(true); + break; + case Phonon::StoppedState: + case Phonon::LoadingState: + case Phonon::ErrorState: + setEnabled(false); + ticking = true; + slider.setValue(0); + ticking = false; + break; + } + } +} + +void SeekSliderPrivate::_k_currentSourceChanged() +{ + //this releases the mouse and makes the seek slider stop seeking if the current source has changed + QMouseEvent event(QEvent::MouseButtonRelease, QPoint(), Qt::LeftButton, 0, 0); + QApplication::sendEvent(&slider, &event); +} + +void SeekSliderPrivate::setEnabled(bool x) +{ + slider.setEnabled(x); + iconLabel.setPixmap(icon.pixmap(iconSize, x ? QIcon::Normal : QIcon::Disabled)); +} + +void SeekSliderPrivate::_k_stateChanged(State newstate) +{ + if (!media || !media->isSeekable()) { + setEnabled(false); + return; + } + switch (newstate) { + case Phonon::PlayingState: + if (media->tickInterval() == 0) { + // if the tick signal is not enabled the slider is useless + // set the tickInterval to some common value + media->setTickInterval(350); + } + case Phonon::BufferingState: + case Phonon::PausedState: + setEnabled(true); + break; + case Phonon::StoppedState: + case Phonon::LoadingState: + case Phonon::ErrorState: + setEnabled(false); + ticking = true; + slider.setValue(0); + ticking = false; + break; + } +} + +bool SeekSlider::hasTracking() const +{ + return k_ptr->slider.hasTracking(); +} + +void SeekSlider::setTracking(bool tracking) +{ + k_ptr->slider.setTracking(tracking); +} + +int SeekSlider::pageStep() const +{ + return k_ptr->slider.pageStep(); +} + +void SeekSlider::setPageStep(int milliseconds) +{ + k_ptr->slider.setPageStep(milliseconds); +} + +int SeekSlider::singleStep() const +{ + return k_ptr->slider.singleStep(); +} + +void SeekSlider::setSingleStep(int milliseconds) +{ + k_ptr->slider.setSingleStep(milliseconds); +} + +bool SeekSlider::isIconVisible() const +{ + K_D(const SeekSlider); + return d->iconLabel.isVisible(); +} + +void SeekSlider::setIconVisible(bool vis) +{ + K_D(SeekSlider); + d->iconLabel.setVisible(vis); +} + +Qt::Orientation SeekSlider::orientation() const +{ + return k_ptr->slider.orientation(); +} + +void SeekSlider::setOrientation(Qt::Orientation o) +{ + K_D(SeekSlider); + Qt::Alignment align = (o == Qt::Horizontal ? Qt::AlignVCenter : Qt::AlignHCenter); + d->layout.setAlignment(&d->iconLabel, align); + d->layout.setAlignment(&d->slider, align); + d->layout.setDirection(o == Qt::Horizontal ? QBoxLayout::LeftToRight : QBoxLayout::TopToBottom); + d->slider.setOrientation(o); +} + +QSize SeekSlider::iconSize() const +{ + return k_ptr->iconSize; +} + +void SeekSlider::setIconSize(const QSize &iconSize) +{ + K_D(SeekSlider); + d->iconSize = iconSize; + d->iconLabel.setPixmap(d->icon.pixmap(d->iconSize, d->slider.isEnabled() ? QIcon::Normal : QIcon::Disabled)); +} + +} // namespace Phonon + +#endif //QT_NO_PHONON_SEEKSLIDER + +QT_END_NAMESPACE + +#include "moc_seekslider.cpp" + +// vim: sw=4 ts=4 diff --git a/src/3rdparty/phonon/phonon/seekslider.h b/src/3rdparty/phonon/phonon/seekslider.h new file mode 100644 index 0000000..540079e --- /dev/null +++ b/src/3rdparty/phonon/phonon/seekslider.h @@ -0,0 +1,157 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_UI_SEEKSLIDER_H +#define PHONON_UI_SEEKSLIDER_H + +#include "phonon_export.h" +#include "phonondefs.h" +#include "phononnamespace.h" +#include <QtGui/QWidget> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_SEEKSLIDER + +namespace Phonon +{ +class MediaObject; + +class SeekSliderPrivate; + +/** \class SeekSlider seekslider.h Phonon/SeekSlider + * \short Widget providing a slider for seeking in MediaObject objects. + * + * \ingroup PhononWidgets + * \author Matthias Kretz <kretz@kde.org> + */ +class PHONON_EXPORT SeekSlider : public QWidget +{ + Q_OBJECT + K_DECLARE_PRIVATE(SeekSlider) + /** + * This property holds whether the icon next to the slider is visible. + * + * By default the icon is visible if the platform provides an icon; else + * it's hidden. + */ + Q_PROPERTY(bool iconVisible READ isIconVisible WRITE setIconVisible) + + /** + * This property holds whether slider tracking is enabled. + * + * If tracking is enabled (the default), the media seeks + * while the slider is being dragged. If tracking is + * disabled, the media seeks only when the user + * releases the slider. + */ + Q_PROPERTY(bool tracking READ hasTracking WRITE setTracking) + + /** + * This property holds the page step. + * + * The larger of two natural steps that a slider provides and + * typically corresponds to the user pressing PageUp or PageDown. + * + * Defaults to 5 seconds. + */ + Q_PROPERTY(int pageStep READ pageStep WRITE setPageStep) + + /** + * This property holds the single step. + * + * The smaller of two natural steps that a slider provides and + * typically corresponds to the user pressing an arrow key. + * + * Defaults to 0.5 seconds. + */ + Q_PROPERTY(int singleStep READ singleStep WRITE setSingleStep) + + /** + * This property holds the orientation of the slider. + * + * The orientation must be Qt::Vertical or Qt::Horizontal (the default). + */ + Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation) + + /** + * \brief the icon size used for the mute button/icon. + * + * The default size is defined by the GUI style. + */ + Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize) + + public: + /** + * Constructs a seek slider widget with the given \p parent. + */ + explicit SeekSlider(QWidget *parent = 0); + explicit SeekSlider(MediaObject *media, QWidget *parent = 0); + + /** + * Destroys the seek slider. + */ + ~SeekSlider(); + + bool hasTracking() const; + void setTracking(bool tracking); + int pageStep() const; + void setPageStep(int milliseconds); + int singleStep() const; + void setSingleStep(int milliseconds); + Qt::Orientation orientation() const; + bool isIconVisible() const; + QSize iconSize() const; + MediaObject *mediaObject() const; + + public Q_SLOTS: + void setOrientation(Qt::Orientation); + void setIconVisible(bool); + void setIconSize(const QSize &size); + + /** + * Sets the media object to be controlled by this slider. + */ + void setMediaObject(MediaObject *); + + protected: + SeekSliderPrivate *const k_ptr; + + private: + Q_PRIVATE_SLOT(k_func(), void _k_stateChanged(Phonon::State)) + Q_PRIVATE_SLOT(k_func(), void _k_seek(int)) + Q_PRIVATE_SLOT(k_func(), void _k_tick(qint64)) + Q_PRIVATE_SLOT(k_func(), void _k_length(qint64)) + Q_PRIVATE_SLOT(k_func(), void _k_seekableChanged(bool)) + Q_PRIVATE_SLOT(k_func(), void _k_currentSourceChanged()) +}; + +} // namespace Phonon + +#endif //QT_NO_PHONON_SEEKSLIDER + +QT_END_NAMESPACE +QT_END_HEADER + +// vim: sw=4 ts=4 tw=80 +#endif // PHONON_UI_SEEKSLIDER_H diff --git a/src/3rdparty/phonon/phonon/seekslider_p.h b/src/3rdparty/phonon/phonon/seekslider_p.h new file mode 100644 index 0000000..f4ed616 --- /dev/null +++ b/src/3rdparty/phonon/phonon/seekslider_p.h @@ -0,0 +1,101 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef SEEKSLIDER_P_H +#define SEEKSLIDER_P_H + +#include "seekslider.h" +#include <QtGui/QBoxLayout> +#include <QtGui/QSlider> +#include <QtGui/QLabel> +#include <QtGui/QPixmap> +#include <QtGui/QIcon> +#include <QtGui/QStyle> +#include "factory_p.h" +#include <QtCore/QPointer> +#include "platform_p.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_SEEKSLIDER + +namespace Phonon +{ +class MediaObject; +class SeekSliderPrivate +{ + Q_DECLARE_PUBLIC(SeekSlider) + protected: + SeekSliderPrivate(SeekSlider *parent) + : layout(QBoxLayout::LeftToRight, parent), + slider(Qt::Horizontal, parent), + iconLabel(parent), + ticking(false) +#ifndef QT_NO_PHONON_PLATFORMPLUGIN + ,icon(Platform::icon(QLatin1String("player-time"), parent->style())) +#endif //QT_NO_PHONON_PLATFORMPLUGIN + { + const int e = parent->style()->pixelMetric(QStyle::PM_ButtonIconSize); + iconSize = QSize(e, e); + + slider.setPageStep(5000); // 5 sec + slider.setSingleStep(500); // 0.5 sec + + layout.setMargin(0); + layout.setSpacing(2); + layout.addWidget(&iconLabel, 0, Qt::AlignVCenter); + layout.addWidget(&slider, 0, Qt::AlignVCenter); + + setEnabled(false); + + if (icon.isNull()) { + iconLabel.setVisible(false); + } + } + + SeekSlider *q_ptr; + + private: + void setEnabled(bool); + void _k_stateChanged(Phonon::State); + void _k_seek(int); + void _k_tick(qint64); + void _k_length(qint64); + void _k_seekableChanged(bool); + void _k_currentSourceChanged(); + + QBoxLayout layout; + QSlider slider; + QLabel iconLabel; + QPointer<MediaObject> media; + bool ticking; + QIcon icon; + QSize iconSize; +}; +} // namespace Phonon + +#endif //QT_NO_PHONON_SEEKSLIDER + +QT_END_NAMESPACE + +#endif // SEEKSLIDER_P_H +// vim: sw=4 ts=4 tw=80 diff --git a/src/3rdparty/phonon/phonon/stream-thoughts b/src/3rdparty/phonon/phonon/stream-thoughts new file mode 100644 index 0000000..5fb6711 --- /dev/null +++ b/src/3rdparty/phonon/phonon/stream-thoughts @@ -0,0 +1,72 @@ +there are two different kind of streams: 1. media files 2. live radio/television + +The difference cannot reliably be determined by the backend so the application has to tell the +frontend. + +This is the expected behaviour: +1. media files +============== +function | prev. state | action | new state +---------+-------------+----------------------------------------------------------------+---------- +ctor | | | Loading +---------+-------------+----------------------------------------------------------------+---------- +setUrl | Loading | refill buffers | Loading + | Stopped | refill buffers | Loading +---------+-------------+----------------------------------------------------------------+---------- +play | Loading | continue buffering | Buffering + | Stopped | start playing | Playing + | Buffering | continue buffering | Buffering + | Playing | | Playing + | Paused | continue playback | Playing +---------+-------------+----------------------------------------------------------------+---------- +pause | Loading | | Loading + | Stopped | | Stopped + | Buffering | continue buffering | Paused + | Playing | pause output and fill buffers to the max | Paused + | Paused | | Paused +---------+-------------+----------------------------------------------------------------+---------- +stop | Loading | | Loading + | Stopped | | Stopped + | Buffering | restart buffering from the beginning of the file | Loading + | Playing | stop output and refill buffers from the beginning of the file | Loading + | Paused | restart buffering from the beginning of the file | Loading + +events +function | prev. state | event | new state +---------+-------------+----------------------------------------------------------------+---------- + | Buffering | when the buffers are filled start playing | Playing + | Loading | when buffers are filled | Stopped + + + +2. live stream +============== +function | prev. state | action | new state +---------+-------------+----------------------------------------------------------------+---------- +ctor | | | Loading +---------+-------------+----------------------------------------------------------------+---------- +setUrl | Loading | | Stopped + | Stopped | | Stopped +---------+-------------+----------------------------------------------------------------+---------- +play | Loading | | Error + | Stopped | start buffering | Buffering + | Buffering | continue buffering | Buffering + | Playing | | Playing + | Paused | continue playback | Playing +---------+-------------+----------------------------------------------------------------+---------- +pause | Loading | | Error + | Stopped | | Stopped + | Buffering | continue buffering | Paused + | Playing | pause output and fill (ring-)buffers to the max | Paused + | Paused | | Paused +---------+-------------+----------------------------------------------------------------+---------- +stop | Loading | | Error + | Stopped | | Stopped + | Buffering | clear buffers | Stopped + | Playing | stop output and clear buffers | Stopped + | Paused | clear buffers | Stopped + +events +function | prev. state | event | new state +---------+-------------+----------------------------------------------------------------+---------- + | Buffering | when the buffers are filled start playing | Playing diff --git a/src/3rdparty/phonon/phonon/streaminterface.cpp b/src/3rdparty/phonon/phonon/streaminterface.cpp new file mode 100644 index 0000000..3646fc1 --- /dev/null +++ b/src/3rdparty/phonon/phonon/streaminterface.cpp @@ -0,0 +1,114 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#include "streaminterface.h" +#include "streaminterface_p.h" +#include "abstractmediastream.h" +#include "abstractmediastream_p.h" +#include "mediasource_p.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + +namespace Phonon +{ + +StreamInterface::StreamInterface() + : d(new StreamInterfacePrivate) +{ + d->q = this; +} + +StreamInterface::~StreamInterface() +{ + if (d->connected) { + AbstractMediaStreamPrivate *dd = d->mediaSource.stream()->d_func(); + dd->setStreamInterface(0); + } + delete d; +} + +void StreamInterface::connectToSource(const MediaSource &mediaSource) +{ + Q_ASSERT(!d->connected); + d->connected = true; + d->mediaSource = mediaSource; + Q_ASSERT(d->mediaSource.type() == MediaSource::Stream); + Q_ASSERT(d->mediaSource.stream()); + AbstractMediaStreamPrivate *dd = d->mediaSource.stream()->d_func(); + dd->setStreamInterface(this); + d->mediaSource.stream()->reset(); +} + +void StreamInterfacePrivate::disconnectMediaStream() +{ + Q_ASSERT(connected); + connected = false; + + // if mediaSource has autoDelete set then it will delete the AbstractMediaStream again who's + // destructor is calling us right now + mediaSource.setAutoDelete(false); + + mediaSource = MediaSource(); + q->endOfData(); + q->setStreamSeekable(false); +} + +void StreamInterface::needData() +{ + if (d->mediaSource.type() == MediaSource::Stream) { + d->mediaSource.stream()->needData(); + } +} + +void StreamInterface::enoughData() +{ + Q_ASSERT(d->connected); + if (d->mediaSource.type() == MediaSource::Stream) { + d->mediaSource.stream()->enoughData(); + } +} + +void StreamInterface::seekStream(qint64 offset) +{ + Q_ASSERT(d->connected); + if (d->mediaSource.type() == MediaSource::Stream) { + d->mediaSource.stream()->seekStream(offset); + } +} + +void StreamInterface::reset() +{ + Q_ASSERT(d->connected); + if (d->mediaSource.type() == MediaSource::Stream) { + d->mediaSource.stream()->reset(); + } +} + +} // namespace Phonon + +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + +QT_END_NAMESPACE + + diff --git a/src/3rdparty/phonon/phonon/streaminterface.h b/src/3rdparty/phonon/phonon/streaminterface.h new file mode 100644 index 0000000..10cc061 --- /dev/null +++ b/src/3rdparty/phonon/phonon/streaminterface.h @@ -0,0 +1,123 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_STREAMINTERFACE_H +#define PHONON_STREAMINTERFACE_H + +#include "phonon_export.h" +#include <QtCore/QObject> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + +namespace Phonon +{ +class StreamInterfacePrivate; +class MediaSource; + +/** \class StreamInterface streaminterface.h Phonon/StreamInterface + * \brief Backend interface to handle media streams (AbstractMediaStream). + * + * \author Matthias Kretz <kretz@kde.org> + */ +class PHONON_EXPORT StreamInterface +{ + friend class StreamInterfacePrivate; + friend class AbstractMediaStreamPrivate; + public: + virtual ~StreamInterface(); + /** + * Called by the application to send a chunk of (encoded) media data. + * + * It is recommended to keep the QByteArray object until the data is consumed so that no + * memcopy is needed. + */ + virtual void writeData(const QByteArray &data) = 0; + /** + * Called when no more media data is available and writeData will not be called anymore. + */ + virtual void endOfData() = 0; + /** + * Called at the start of the stream to tell how many bytes will be sent through writeData + * (if no seeks happen, of course). If this value is negative the stream size cannot be + * determined (might be a "theoretically infinite" stream - like webradio). + */ + virtual void setStreamSize(qint64 newSize) = 0; + /** + * Tells whether the stream is seekable. + */ + virtual void setStreamSeekable(bool s) = 0; + + /** + * Call this function from the constructor of your StreamInterface implementation (or as + * soon as you get the MediaSource object). This will connect your object to the + * AbstractMediaStream object. Only after the connection is done will the following + * functions have an effect. + */ + void connectToSource(const MediaSource &mediaSource); + + /** + * Call this function to tell the AbstractMediaStream that you need more data. The data will + * arrive through writeData. Don't rely on writeData getting called from needData, though + * some AbstractMediaStream implementations might do so. + * + * Depending on the buffering you need you either treat needData as a replacement for a + * read call like QIODevice::read, or you start calling needData whenever your buffer + * reaches a certain lower threshold. + */ + void needData(); + + /** + * Call this function to tell the AbstractMediaStream that you have enough data in your + * buffer and that it should pause calling writeData if possible. + */ + void enoughData(); + + /** + * If the stream is seekable, calling this function will make the next call to writeData + * pass data that starts at the byte offset \p seekTo. + */ + void seekStream(qint64 seekTo); + + /** + * Resets the AbstractMediaStream. E.g. this can be useful for non-seekable streams to start + * over again. + */ + void reset(); + + protected: + StreamInterface(); + + StreamInterfacePrivate *const d; +}; +} // namespace Phonon + +Q_DECLARE_INTERFACE(Phonon::StreamInterface, "StreamInterface1.phonon.kde.org") + +#endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // PHONON_STREAMINTERFACE_H diff --git a/src/3rdparty/phonon/phonon/streaminterface_p.h b/src/3rdparty/phonon/phonon/streaminterface_p.h new file mode 100644 index 0000000..cc41156 --- /dev/null +++ b/src/3rdparty/phonon/phonon/streaminterface_p.h @@ -0,0 +1,59 @@ +/* This file is part of the KDE project + Copyright (C) 2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef STREAMINTERFACE_P_H +#define STREAMINTERFACE_P_H + +#include "streaminterface.h" +#include "mediasource.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM + +namespace Phonon +{ +class StreamInterfacePrivate +{ + friend class StreamInterface; + public: + void disconnectMediaStream(); + + protected: + inline StreamInterfacePrivate() + : connected(false) + { + } + + StreamInterface *q; + MediaSource mediaSource; + bool connected; +}; + +} // namespace Phonon + +#endif // QT_NO_PHONON_ABSTRACTMEDIASTREAM + +QT_END_NAMESPACE + +#endif // STREAMINTERFACE_P_H +// vim: sw=4 sts=4 et tw=100 diff --git a/src/3rdparty/phonon/phonon/videoplayer.cpp b/src/3rdparty/phonon/phonon/videoplayer.cpp new file mode 100644 index 0000000..8f76d4c --- /dev/null +++ b/src/3rdparty/phonon/phonon/videoplayer.cpp @@ -0,0 +1,184 @@ +/* This file is part of the KDE project + Copyright (C) 2004-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#include "videoplayer.h" +#include "mediaobject.h" +#include "audiooutput.h" +#include "videowidget.h" +#include "path.h" +#include <QtGui/QBoxLayout> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_VIDEOPLAYER + +namespace Phonon +{ + +class VideoPlayerPrivate +{ + public: + VideoPlayerPrivate() + : player(0) + , aoutput(0) + , voutput(0) {} + + void init(VideoPlayer *q, Phonon::Category category); + + MediaObject *player; + AudioOutput *aoutput; + VideoWidget *voutput; + + MediaSource src; +}; + +void VideoPlayerPrivate::init(VideoPlayer *q, Phonon::Category category) +{ + QVBoxLayout *layout = new QVBoxLayout(q); + layout->setMargin(0); + + aoutput = new AudioOutput(category, q); + + voutput = new VideoWidget(q); + layout->addWidget(voutput); + + player = new MediaObject(q); + Phonon::createPath(player, aoutput); + Phonon::createPath(player, voutput); + + q->connect(player, SIGNAL(finished()), SIGNAL(finished())); +} + +VideoPlayer::VideoPlayer(Phonon::Category category, QWidget *parent) + : QWidget(parent) + , d(new VideoPlayerPrivate) +{ + d->init(this, category); +} + +VideoPlayer::VideoPlayer(QWidget *parent) + : QWidget(parent) + , d(new VideoPlayerPrivate) +{ + d->init(this, Phonon::VideoCategory); +} + +VideoPlayer::~VideoPlayer() +{ + delete d; +} + +MediaObject *VideoPlayer::mediaObject() const +{ + return d->player; +} + +AudioOutput *VideoPlayer::audioOutput() const +{ + return d->aoutput; +} + +VideoWidget *VideoPlayer::videoWidget() const +{ + return d->voutput; +} + +void VideoPlayer::load(const MediaSource &source) +{ + d->player->setCurrentSource(source); +} + +void VideoPlayer::play(const MediaSource &source) +{ + if (source == d->player->currentSource()) { + if (!isPlaying()) + d->player->play(); + return; + } + // new URL + d->player->setCurrentSource(source); + + if (ErrorState == d->player->state()) + return; + + d->player->play(); +} + +void VideoPlayer::play() +{ + d->player->play(); +} + +void VideoPlayer::pause() +{ + d->player->pause(); +} + +void VideoPlayer::stop() +{ + d->player->stop(); +} + +qint64 VideoPlayer::totalTime() const +{ + return d->player->totalTime(); +} + +qint64 VideoPlayer::currentTime() const +{ + return d->player->currentTime(); +} + +void VideoPlayer::seek(qint64 ms) +{ + d->player->seek(ms); +} + +float VideoPlayer::volume() const +{ + return d->aoutput->volume(); +} + +void VideoPlayer::setVolume(float v) +{ + d->aoutput->setVolume(v); +} + +bool VideoPlayer::isPlaying() const +{ + return (d->player->state() == PlayingState); +} + +bool VideoPlayer::isPaused() const +{ + return (d->player->state() == PausedState); +} + +} // namespaces + +#endif //QT_NO_PHONON_VIDEOPLAYER + +QT_END_NAMESPACE + +#include "moc_videoplayer.cpp" + +// vim: sw=4 ts=4 diff --git a/src/3rdparty/phonon/phonon/videoplayer.h b/src/3rdparty/phonon/phonon/videoplayer.h new file mode 100644 index 0000000..6da1d98 --- /dev/null +++ b/src/3rdparty/phonon/phonon/videoplayer.h @@ -0,0 +1,207 @@ +/* This file is part of the KDE project + Copyright (C) 2004-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef Phonon_VIDEOPLAYER_H +#define Phonon_VIDEOPLAYER_H + +#include "phonon_export.h" +#include "phononnamespace.h" +#include "mediasource.h" +#include <QtGui/QWidget> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_VIDEOPLAYER + +namespace Phonon +{ +class VideoPlayerPrivate; +class MediaObject; +class AudioOutput; +class VideoWidget; + +/** \class VideoPlayer videoplayer.h Phonon/VideoPlayer + * \short Playback class for simple tasks. + * + * With %VideoPlayer you can get results quickly and easily. You can do the standard + * playback tasks like play, pause and stop, but also set a playback volume and + * seek (there's no guarantee that the seek will work, though). + * + * Keep in mind that when the %VideoPlayer instance is deleted the playback will + * stop. + * + * A play and forget code example: + * \code + * VideoPlayer *player = new VideoPlayer(parentWidget); + * connect(player, SIGNAL(finished()), player, SLOT(deleteLater())); + * player->play(url); + * \endcode + * + * \ingroup Playback + * \ingroup PhononVideo + * \author Matthias Kretz <kretz@kde.org> + */ +class PHONON_EXPORT VideoPlayer : public QWidget +{ + Q_OBJECT + public: + /** + * Constructs a new %VideoPlayer instance. + * + * \param category The category used for the audio output device. + * \param parent The QObject parent. + */ + explicit VideoPlayer(Phonon::Category category, QWidget *parent = 0); + + /** + * Constructs a new video widget with a \p parent + * using Phonon::VideoCategory as its category. + * + * \param parent The QObject parent. + */ + VideoPlayer(QWidget *parent = 0); + + /** + * On destruction the playback is stopped, also the audio output is + * removed so that the desktop mixer will not show the application + * anymore. If you need a persistent audio output don't use + * %VideoPlayer but MediaObject, VideoPath and VideoOutput. + */ + ~VideoPlayer(); + + /** + * Get the total time (in milliseconds) of the file currently being played. + */ + qint64 totalTime() const; + /** + * Get the current time (in milliseconds) of the file currently being played. + */ + qint64 currentTime() const; + /** + * This is the current volume of the output as voltage factor. + * + * 1.0 means 100%, 0.5 means 50% voltage/25% power, 0.0 means 0% + */ + float volume() const; + + /** + * \returns \c true if it is currently playing + * \returns \c false if it is currently stopped or paused + */ + bool isPlaying() const; + /** + * \returns \c true if it is currently paused + * \returns \c false if it is currently playing or stopped + */ + bool isPaused() const; + + /** + * getter for the MediaObject. + */ + MediaObject *mediaObject() const; + + /** + * getter for the AudioOutput. + */ + AudioOutput *audioOutput() const; + + /** + * getter for the VideoWidget. + */ + VideoWidget *videoWidget() const; + + public Q_SLOTS: + /** + * Starts preloading the media data and fill audiobuffers in the + * backend. + * + * When there's already a media playing (or paused) it will be stopped + * (the finished signal will not be emitted). + */ + void load(const Phonon::MediaSource &source); + + /** + * Play the media at the given URL. Starts playback as fast as possible. + * This can take a considerable time depending on the URL and the + * backend. + * + * If you need low latency between calling play() and the sound actually + * starting to play on your output device you need to use MediaObject + * and be able to set the URL before calling play(). Note that + * \code + * audioPlayer->load(url); + * audioPlayer->play(); + * \endcode + * doesn't make a difference: the application should be idle between the + * load and play calls so that the backend can start preloading the + * media and fill audio buffers. + */ + void play(const Phonon::MediaSource &source); + + /** + * Continues playback of a paused media. Restarts playback of a stopped + * media. + */ + void play(); + /** + * Pauses the playback. + */ + void pause(); + /** + * Stops the playback. + */ + void stop(); + + /** + * Seeks to the requested time. Note that the backend is free to ignore + * the seek request if the media source isn't seekable. + * + * \param ms Time in milliseconds from the start of the media. + */ + void seek(qint64 ms); + /** + * Sets the volume of the output as voltage factor. + * + * 1.0 means 100%, 0.5 means 50% voltage/25% power, 0.0 means 0% + */ + void setVolume(float volume); + + Q_SIGNALS: + /** + * This signal is emitted when the playback finished. + */ + void finished(); + + protected: + VideoPlayerPrivate *const d; +}; + +} //namespace Phonon + +#endif //QT_NO_PHONON_VIDEOPLAYER + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // Phonon_VIDEOPLAYER_H +// vim: sw=4 ts=4 tw=80 diff --git a/src/3rdparty/phonon/phonon/videowidget.cpp b/src/3rdparty/phonon/phonon/videowidget.cpp new file mode 100644 index 0000000..63f6899 --- /dev/null +++ b/src/3rdparty/phonon/phonon/videowidget.cpp @@ -0,0 +1,183 @@ +/* This file is part of the KDE project + Copyright (C) 2005-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#include "videowidget.h" +#include "videowidget_p.h" +#include "videowidgetinterface.h" +#include "factory_p.h" +#include "phonondefs_p.h" +#include "phononnamespace_p.h" + +#include <QtGui/QAction> + +#define PHONON_INTERFACENAME VideoWidgetInterface + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_VIDEO + +namespace Phonon +{ + +VideoWidget::VideoWidget(QWidget *parent) + : QWidget(parent) + , Phonon::AbstractVideoOutput(*new VideoWidgetPrivate(this)) +{ + K_D(VideoWidget); + d->init(); + d->createBackendObject(); + setMouseTracking(true); +} + +VideoWidget::VideoWidget(VideoWidgetPrivate &dd, QWidget *parent) + : QWidget(parent), + Phonon::AbstractVideoOutput(dd) +{ + K_D(VideoWidget); + d->init(); +} + +void VideoWidgetPrivate::init() +{ + Q_Q(VideoWidget); + changeFlags = q->windowFlags() & (Qt::SubWindow | Qt::Window); +} + +void VideoWidget::mouseMoveEvent(QMouseEvent *e) +{ + QWidget::mouseMoveEvent(e); +} + +void VideoWidgetPrivate::createBackendObject() +{ + if (m_backendObject) + return; + Q_Q(VideoWidget); + m_backendObject = Factory::createVideoWidget(q); + if (m_backendObject) { + setupBackendObject(); + } +} + +#define PHONON_CLASSNAME VideoWidget + +PHONON_INTERFACE_GETTER(Phonon::VideoWidget::AspectRatio, aspectRatio, d->aspectRatio) +PHONON_INTERFACE_SETTER(setAspectRatio, aspectRatio, Phonon::VideoWidget::AspectRatio) + +PHONON_INTERFACE_GETTER(Phonon::VideoWidget::ScaleMode, scaleMode, d->scaleMode) +PHONON_INTERFACE_SETTER(setScaleMode, scaleMode, Phonon::VideoWidget::ScaleMode) + +PHONON_INTERFACE_GETTER(qreal, brightness, d->brightness) +PHONON_INTERFACE_SETTER(setBrightness, brightness, qreal) + +PHONON_INTERFACE_GETTER(qreal, contrast, d->contrast) +PHONON_INTERFACE_SETTER(setContrast, contrast, qreal) + +PHONON_INTERFACE_GETTER(qreal, hue, d->hue) +PHONON_INTERFACE_SETTER(setHue, hue, qreal) + +PHONON_INTERFACE_GETTER(qreal, saturation, d->saturation) +PHONON_INTERFACE_SETTER(setSaturation, saturation, qreal) + +void VideoWidget::setFullScreen(bool newFullScreen) +{ + pDebug() << Q_FUNC_INFO << newFullScreen; + K_D(VideoWidget); + // TODO: disable screensaver? or should we leave that responsibility to the + // application? + Qt::WindowFlags flags = windowFlags(); + if (newFullScreen) { + if (!isFullScreen()) { + //we only update that value if it is not already fullscreen + d->changeFlags = flags & (Qt::Window | Qt::SubWindow); + flags |= Qt::Window; + flags ^= Qt::SubWindow; + setWindowFlags(flags); +#ifdef Q_WS_X11 + // This works around a bug with Compiz + // as the window must be visible before we can set the state + show(); + raise(); + setWindowState( windowState() | Qt::WindowFullScreen ); // set +#else + setWindowState( windowState() | Qt::WindowFullScreen ); // set + show(); +#endif + } + } else if (isFullScreen()) { + flags ^= (Qt::Window | Qt::SubWindow); //clear the flags... + flags |= d->changeFlags; //then we reset the flags (window and subwindow) + setWindowFlags(flags); + setWindowState( windowState() ^ Qt::WindowFullScreen ); // reset + show(); + } +} + +void VideoWidget::exitFullScreen() +{ + setFullScreen(false); +} + +void VideoWidget::enterFullScreen() +{ + setFullScreen(true); +} + +bool VideoWidgetPrivate::aboutToDeleteBackendObject() +{ + aspectRatio = pINTERFACE_CALL(aspectRatio()); + scaleMode = pINTERFACE_CALL(scaleMode()); + return AbstractVideoOutputPrivate::aboutToDeleteBackendObject(); +} + +void VideoWidgetPrivate::setupBackendObject() +{ + Q_Q(VideoWidget); + Q_ASSERT(m_backendObject); + //AbstractVideoOutputPrivate::setupBackendObject(); + pDebug() << "calling setAspectRatio on the backend " << aspectRatio; + pINTERFACE_CALL(setAspectRatio(aspectRatio)); + pINTERFACE_CALL(setScaleMode(scaleMode)); + + QWidget *w = pINTERFACE_CALL(widget()); + if (w) { + layout.addWidget(w); + q->setSizePolicy(w->sizePolicy()); + w->setMouseTracking(true); + } +} + +bool VideoWidget::event(QEvent *e) +{ + return QWidget::event(e); +} + +} //namespace Phonon + +#endif //QT_NO_PHONON_VIDEO + +QT_END_NAMESPACE + +#include "moc_videowidget.cpp" + +#undef PHONON_CLASSNAME +// vim: sw=4 ts=4 tw=80 diff --git a/src/3rdparty/phonon/phonon/videowidget.h b/src/3rdparty/phonon/phonon/videowidget.h new file mode 100644 index 0000000..bde7333 --- /dev/null +++ b/src/3rdparty/phonon/phonon/videowidget.h @@ -0,0 +1,219 @@ +/* This file is part of the KDE project + Copyright (C) 2005-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ +#ifndef Phonon_VIDEOWIDGET_H +#define Phonon_VIDEOWIDGET_H + +#include "phonon_export.h" +#include "phonondefs.h" +#include "abstractvideooutput.h" +#include <QtGui/QWidget> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +class QString; + +#ifndef QT_NO_PHONON_VIDEO + +namespace Phonon +{ +class AbstractVideoOutput; + class VideoWidgetPrivate; + /** \class VideoWidget videowidget.h Phonon/VideoWidget + * \short Widget to display video. + * + * This widget shows the video signal. + * + * \code + * MediaObject *media = new MediaObject(parent); + * VideoWidget *vwidget = new VideoWidget(parent); + * Phonon::createPath(media, vwidget); + * \endcode + * + * \ingroup PhononVideo + * \ingroup PhononWidgets + * \author Matthias Kretz <kretz@kde.org> + */ + class PHONON_EXPORT VideoWidget : public QWidget, public Phonon::AbstractVideoOutput + { + K_DECLARE_PRIVATE(VideoWidget) + Q_OBJECT + Q_ENUMS(AspectRatio ScaleMode) + /** + * This property holds whether the video is shown using the complete + * screen. + * + * The property differs from QWidget::fullScreen in that it is + * writeable. + * + * By default the widget is not shown in fullScreen. + * + * \warning When switching the video to fullscreen using setFullScreen + * your application loses control over the widget that actually shows + * the video (which is then shown as a toplevel window while your + * application still uses this widget). If you only need to capture key + * events the event forwarding done internally should suffice for your + * needs. If you need to map mouse coordinates or add widgets (that are + * not overlays) you should probably handle fullscreen yourself. + */ + Q_PROPERTY(bool fullScreen READ isFullScreen WRITE setFullScreen) + /** + * + * Defaults to AspectRatioAuto. + * + * \see AspectRatio + */ + Q_PROPERTY(AspectRatio aspectRatio READ aspectRatio WRITE setAspectRatio) + + /** + * If the size of the widget and the size of the video are not equal. + * The video will be zoomed to fit the widget. The smaller zoom + * (AddBarsScaleMode) adds black bars at the left/right or top/bottom to + * make all of the image visible (default). The bigger zoom (ExpandMode) + * fills the widget completely, keeping all information in one direction + * and leaving parts of the image outside of the widget in the other + * direction. + */ + Q_PROPERTY(ScaleMode scaleMode READ scaleMode WRITE setScaleMode) + + /** + * This property holds brightness of the video. + * + * Default is 0. Acceptable values are in range of -1, 1. + */ + Q_PROPERTY(qreal brightness READ brightness WRITE setBrightness) + /** + * This property holds the contrast of the video. + * + * Default is 0. Acceptable values are in range of -1, 1. + */ + Q_PROPERTY(qreal contrast READ contrast WRITE setContrast) + /** + * This property holds the hue of the video. + * + * Default is 0. Acceptable values are in range of -1, 1. + */ + Q_PROPERTY(qreal hue READ hue WRITE setHue) + /** + * This property holds saturation of the video. + * + * Default is 0. Acceptable values are in range of -1, 1. + */ + Q_PROPERTY(qreal saturation READ saturation WRITE setSaturation) + + public: + /** + * Defines the width:height to be used for the video. + */ + enum AspectRatio + { + /** + * Let the decoder find the aspect ratio automatically from the + * media file (this is the default). + */ + AspectRatioAuto = 0, + /** + * Fits the video into the widget making the aspect ratio depend + * solely on the size of the widget. This way the aspect ratio + * is freely resizeable by the user. + */ + AspectRatioWidget = 1, + /** + * Make width/height == 4/3, which is the old TV size and + * monitor size (1024/768 == 4/3). (4:3) + */ + AspectRatio4_3 = 2, + /** + * Make width/height == 16/9, which is the size of most current + * media. (16:9) + */ + AspectRatio16_9 = 3 +//X /** +//X * Assume that every pixel of the video image needs to be displayed with the same +//X * physical width and height. (1:1 image pixels, not imagewidth +//X * = imageheight) +//X */ +//X AspectRatioSquare = 4, + }; + + enum ScaleMode { + FitInView = 0, + ScaleAndCrop = 1 + }; + + /** + * Constructs a new video widget with a \p parent. + */ + VideoWidget(QWidget *parent = 0); + + AspectRatio aspectRatio() const; + ScaleMode scaleMode() const; + + qreal brightness() const; + qreal contrast() const; + qreal hue() const; + qreal saturation() const; + + //TODO: bar colors property + public Q_SLOTS: + void setFullScreen(bool fullscreen); + + /** + * Convenience slot, calling setFullScreen(false) + */ + void exitFullScreen(); + + /** + * Convenience slot, calling setFullScreen(true) + */ + void enterFullScreen(); + + void setAspectRatio(AspectRatio); + void setScaleMode(ScaleMode); + + void setBrightness(qreal value); + void setContrast(qreal value); + void setHue(qreal value); + void setSaturation(qreal value); + + protected: + /** + * \internal + * + * Constructs a new video widget with private data pointer \p d and + * a \p parent. + */ + VideoWidget(VideoWidgetPrivate &d, QWidget *parent); + + void mouseMoveEvent(QMouseEvent *); + bool event(QEvent *); + }; + +} //namespace Phonon + +#endif //QT_NO_PHONON_VIDEO + +QT_END_NAMESPACE +QT_END_HEADER + +// vim: sw=4 ts=4 tw=80 +#endif // Phonon_VIDEOWIDGET_H diff --git a/src/3rdparty/phonon/phonon/videowidget_p.h b/src/3rdparty/phonon/phonon/videowidget_p.h new file mode 100644 index 0000000..c2434f2 --- /dev/null +++ b/src/3rdparty/phonon/phonon/videowidget_p.h @@ -0,0 +1,84 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef VIDEOWIDGET_P_H +#define VIDEOWIDGET_P_H + +#include "videowidget.h" +#include "abstractvideooutput_p.h" +#include <QtGui/QBoxLayout> +#include <QtCore/QEvent> +#include <QtCore/QCoreApplication> +#include <QtGui/QPalette> +#include <QtGui/QKeyEvent> +#include <QtCore/QTimer> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_VIDEO + +namespace Phonon +{ + +class VideoWidgetPrivate : public Phonon::AbstractVideoOutputPrivate +{ + Q_DECLARE_PUBLIC(VideoWidget) + public: + virtual QObject *qObject() { return q_func(); } + protected: + virtual bool aboutToDeleteBackendObject(); + virtual void createBackendObject(); + void setupBackendObject(); + + VideoWidgetPrivate(VideoWidget *parent) + : layout(parent), + aspectRatio(VideoWidget::AspectRatioAuto), + scaleMode(VideoWidget::FitInView), + brightness(0), + contrast(0), + hue(0), + saturation(0) + { + layout.setMargin(0); + } + + QHBoxLayout layout; + VideoWidget::AspectRatio aspectRatio; + VideoWidget::ScaleMode scaleMode; + Qt::WindowFlags changeFlags; + + qreal brightness; + qreal contrast; + qreal hue; + qreal saturation; + + private: + void init(); +}; + +} // namespace Phonon + +#endif //QT_NO_PHONON_VIDEO + +QT_END_NAMESPACE +#endif // VIDEOWIDGET_P_H +// vim: sw=4 ts=4 tw=80 diff --git a/src/3rdparty/phonon/phonon/videowidgetinterface.h b/src/3rdparty/phonon/phonon/videowidgetinterface.h new file mode 100644 index 0000000..7ed8a8a --- /dev/null +++ b/src/3rdparty/phonon/phonon/videowidgetinterface.h @@ -0,0 +1,65 @@ +/* This file is part of the KDE project + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). <thierry.bastian@trolltech.com> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_VIDEOWIDGETINTERFACE_H +#define PHONON_VIDEOWIDGETINTERFACE_H + +#include "videowidget.h" + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_VIDEO + +namespace Phonon +{ +class VideoWidgetInterface +{ + public: + virtual ~VideoWidgetInterface() {} + + virtual Phonon::VideoWidget::AspectRatio aspectRatio() const = 0; + virtual void setAspectRatio(Phonon::VideoWidget::AspectRatio) = 0; + virtual qreal brightness() const = 0; + virtual void setBrightness(qreal) = 0; + virtual Phonon::VideoWidget::ScaleMode scaleMode() const = 0; + virtual void setScaleMode(Phonon::VideoWidget::ScaleMode) = 0; + virtual qreal contrast() const = 0; + virtual void setContrast(qreal) = 0; + virtual qreal hue() const = 0; + virtual void setHue(qreal) = 0; + virtual qreal saturation() const = 0; + virtual void setSaturation(qreal) = 0; + virtual QWidget *widget() = 0; +//X virtual int overlayCapabilities() const = 0; +//X virtual bool createOverlay(QWidget *widget, int type) = 0; +}; +} + +Q_DECLARE_INTERFACE(Phonon::VideoWidgetInterface, "VideoWidgetInterface3.phonon.kde.org") + +#endif //QT_NO_PHONON_VIDEO + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // PHONON_VIDEOWIDGETINTERFACE_H diff --git a/src/3rdparty/phonon/phonon/volumefadereffect.cpp b/src/3rdparty/phonon/phonon/volumefadereffect.cpp new file mode 100644 index 0000000..066199e --- /dev/null +++ b/src/3rdparty/phonon/phonon/volumefadereffect.cpp @@ -0,0 +1,108 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#include "volumefadereffect.h" +#include "volumefadereffect_p.h" +#include "volumefaderinterface.h" +#include "factory_p.h" + +#include <QtCore/qmath.h> + +#define PHONON_CLASSNAME VolumeFaderEffect +#define PHONON_INTERFACENAME VolumeFaderInterface + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_VOLUMEFADEREFFECT + +namespace Phonon +{ +PHONON_HEIR_IMPL(Effect) + +PHONON_INTERFACE_GETTER(float, volume, d->currentVolume) +PHONON_INTERFACE_SETTER(setVolume, currentVolume, float) +PHONON_INTERFACE_GETTER(Phonon::VolumeFaderEffect::FadeCurve, fadeCurve, d->fadeCurve) +PHONON_INTERFACE_SETTER(setFadeCurve, fadeCurve, Phonon::VolumeFaderEffect::FadeCurve) + +#ifndef PHONON_LOG10OVER20 +#define PHONON_LOG10OVER20 +static const double log10over20 = 0.1151292546497022842; // ln(10) / 20 +#endif // PHONON_LOG10OVER20 + +double VolumeFaderEffect::volumeDecibel() const +{ + return log(volume()) / log10over20; +} + +void VolumeFaderEffect::setVolumeDecibel(double newVolumeDecibel) +{ + setVolume(exp(newVolumeDecibel * log10over20)); +} + + +void VolumeFaderEffect::fadeIn(int fadeTime) +{ + fadeTo(1.0, fadeTime); +} + +void VolumeFaderEffect::fadeOut(int fadeTime) +{ + fadeTo(0.0, fadeTime); +} + +void VolumeFaderEffect::fadeTo(float volume, int fadeTime) +{ + K_D(VolumeFaderEffect); + if (k_ptr->backendObject()) + INTERFACE_CALL(fadeTo(volume, fadeTime)); + else + d->currentVolume = volume; +} + +bool VolumeFaderEffectPrivate::aboutToDeleteBackendObject() +{ + if (m_backendObject) { + currentVolume = pINTERFACE_CALL(volume()); + fadeCurve = pINTERFACE_CALL(fadeCurve()); + } + return true; +} + +void VolumeFaderEffectPrivate::setupBackendObject() +{ + Q_ASSERT(m_backendObject); + + // set up attributes + pINTERFACE_CALL(setVolume(currentVolume)); + pINTERFACE_CALL(setFadeCurve(fadeCurve)); +} +} + + +#endif //QT_NO_PHONON_VOLUMEFADEREFFECT + +QT_END_NAMESPACE + +#include "moc_volumefadereffect.cpp" + +#undef PHONON_CLASSNAME +// vim: sw=4 ts=4 diff --git a/src/3rdparty/phonon/phonon/volumefadereffect.h b/src/3rdparty/phonon/phonon/volumefadereffect.h new file mode 100644 index 0000000..22f2137 --- /dev/null +++ b/src/3rdparty/phonon/phonon/volumefadereffect.h @@ -0,0 +1,178 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_VOLUMEFADEREFFECT_H +#define PHONON_VOLUMEFADEREFFECT_H + +#include "phonon_export.h" +#include "effect.h" + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_VOLUMEFADEREFFECT + +namespace Phonon +{ + class VolumeFaderEffectPrivate; + + /** \class VolumeFaderEffect volumefadereffect.h Phonon/VolumeFaderEffect + * Audio effect to gradually fade the audio volume. + * + * This effect differs from gradually changing the output volume in that + * a dedicated effect can change the volume in the smallest possible + * steps while every other volume control will make more or less + * noticeable steps. + * + * \ingroup PhononEffects + * \author Matthias Kretz <kretz@kde.org> + * \see AudioOutput::volume + */ + class PHONON_EXPORT VolumeFaderEffect : public Effect + { + Q_OBJECT + K_DECLARE_PRIVATE(VolumeFaderEffect) + PHONON_HEIR(VolumeFaderEffect) + Q_ENUMS(FadeCurve) + /** + * This is the current volume of the output as voltage factor. + * Setting this property changes the volume immediately. + * + * 1.0 means 100%, 0.5 means 50% voltage/25% power, 0.0 means 0% + * + * \see volumeDecibel + */ + Q_PROPERTY(float volume READ volume WRITE setVolume) + /** + * This is the current volume of the output in decibel. + * Setting this property changes the volume immediately. + * + * 0 dB means no change in volume, -6dB means an attenuation of the + * voltage to 50% and an attenuation of the power to 25%, -inf dB means + * silence. + * + * \see volume + */ + Q_PROPERTY(double volumeDecibel READ volumeDecibel WRITE setVolumeDecibel) + /** + * This property holds the fade curve to be used for the fadeIn(), fadeOut() + * and fadeTo() slots. + * + * Defaults to Fade3Decibel. + * + * \see FadeCurve + */ + Q_PROPERTY(FadeCurve fadeCurve READ fadeCurve WRITE setFadeCurve) + public: + /** + * Determines the curve of the volume change. + */ + enum FadeCurve { + /** + * "Crossfade curve" / "fast" fade out + * + * Often the best fade for a crossfade, as after half of the + * time the volume reached -3dB. This means that half the + * possible power (which is proportional to the square of the + * voltage) is reached. Summed, the maximum power of two audio + * signals fading with a -3dB curve will always be equal. + * + * For fading in or out the -3dB curve is too abrupt in the end. + * + * This is the default fade curve. + */ + Fade3Decibel, + /** + * "Linear" fade out + * + * With a -6dB fade curve after half of the fading time -6dB has + * been reached. -6dB is equal to half of the voltage meaning + * that the voltage multiplier changes linearly from the start + * of the fade to the end. + */ + Fade6Decibel, + /** + * "slow" fade out + * + * After half of the fade time -9dB are reached. So the fade is + * fast in the beginning and slow in the end. This is a good + * fade for ending music. + */ + Fade9Decibel, + /** + * more extreme version of the -9dB fade + */ + Fade12Decibel + }; + + float volume() const; + double volumeDecibel() const; + + FadeCurve fadeCurve() const; + + public Q_SLOTS: + /** + * Tells the Fader to change the volume from the current volume to 100% + * in \p fadeTime milliseconds. + * Short for \c fadeTo(1.0, fadeTime). + * + * \param fadeTime the fade duration in milliseconds + * + * \see fadeTo + * \see volume + */ + void fadeIn(int fadeTime); + + /** + * Tells the Fader to change the volume from the current volume to 0% + * in \p fadeTime milliseconds. + * Short for \c fadeTo(0.0, fadeTime). + * + * \param fadeTime the fade duration in milliseconds + * + * \see fadeTo + */ + void fadeOut(int fadeTime); + + void setVolume(float volume); + void setVolumeDecibel(double volumeDecibel); + + void setFadeCurve(FadeCurve curve); + + /** + * Tells the Fader to change the volume from the current value to + * \p volume in \p fadeTime milliseconds + * + * \see fadeIn + * \see fadeOut + */ + void fadeTo(float volume, int fadeTime); + }; +} //namespace Phonon + +#endif //QT_NO_PHONON_VOLUMEFADEREFFECT + +QT_END_NAMESPACE +QT_END_HEADER + +// vim: sw=4 ts=4 tw=80 +#endif // PHONON_VOLUMEFADEREFFECT_H diff --git a/src/3rdparty/phonon/phonon/volumefadereffect_p.h b/src/3rdparty/phonon/phonon/volumefadereffect_p.h new file mode 100644 index 0000000..cdd4e00 --- /dev/null +++ b/src/3rdparty/phonon/phonon/volumefadereffect_p.h @@ -0,0 +1,58 @@ +/* This file is part of the KDE project + Copyright (C) 2006 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_VOLUMEFADEREFFECT_P_H +#define PHONON_VOLUMEFADEREFFECT_P_H + +#include "volumefadereffect.h" +#include "effect_p.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_VOLUMEFADEREFFECT + +namespace Phonon +{ +class VolumeFaderEffectPrivate : public EffectPrivate +{ + Q_DECLARE_PUBLIC(VolumeFaderEffect) + PHONON_PRIVATECLASS + protected: + VolumeFaderEffectPrivate() + : currentVolume(1.0) + , fadeCurve(VolumeFaderEffect::Fade3Decibel) + { + // invalid EffectDescription + // ############# parameter functions are incorrect + } + + float currentVolume; + VolumeFaderEffect::FadeCurve fadeCurve; +}; +} + +#endif //QT_NO_PHONON_VOLUMEFADEREFFECT + +QT_END_NAMESPACE + +#endif // PHONON_VOLUMEFADEREFFECT_P_H +// vim: sw=4 ts=4 tw=80 diff --git a/src/3rdparty/phonon/phonon/volumefaderinterface.h b/src/3rdparty/phonon/phonon/volumefaderinterface.h new file mode 100644 index 0000000..da4262b --- /dev/null +++ b/src/3rdparty/phonon/phonon/volumefaderinterface.h @@ -0,0 +1,58 @@ +/* This file is part of the KDE project + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). <thierry.bastian@trolltech.com> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_VOLUMEFADERINTERFACE_H +#define PHONON_VOLUMEFADERINTERFACE_H + +#include "volumefadereffect.h" +#include <QtCore/QObject> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_VOLUMEFADEREFFECT + +namespace Phonon +{ +class VolumeFaderInterface +{ + public: + virtual ~VolumeFaderInterface() {} + + virtual float volume() const { return 1.0; } + virtual void setVolume(float) {} + virtual Phonon::VolumeFaderEffect::FadeCurve fadeCurve() const { + return VolumeFaderEffect::Fade3Decibel; + } + virtual void setFadeCurve(Phonon::VolumeFaderEffect::FadeCurve) {} + virtual void fadeTo(float, int) {} +}; +} + +Q_DECLARE_INTERFACE(Phonon::VolumeFaderInterface, "VolumeFaderInterface4.phonon.kde.org") + +#endif //QT_NO_PHONON_VOLUMEFADEREFFECT + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // PHONON_VOLUMEFADERINTERFACE_H diff --git a/src/3rdparty/phonon/phonon/volumeslider.cpp b/src/3rdparty/phonon/phonon/volumeslider.cpp new file mode 100644 index 0000000..b59f689 --- /dev/null +++ b/src/3rdparty/phonon/phonon/volumeslider.cpp @@ -0,0 +1,262 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#include "volumeslider.h" +#include "volumeslider_p.h" +#include "audiooutput.h" +#include "phonondefs_p.h" +#include "phononnamespace_p.h" +#include "factory_p.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_VOLUMESLIDER + +namespace Phonon +{ +VolumeSlider::VolumeSlider(QWidget *parent) + : QWidget(parent), + k_ptr(new VolumeSliderPrivate(this)) +{ + K_D(VolumeSlider); +#ifndef QT_NO_TOOLTIP + setToolTip(tr("Volume: %1%").arg(100)); +#endif +#ifndef QT_NO_WHATSTHIS + setWhatsThis(tr("Use this slider to adjust the volume. The leftmost position is 0%, the rightmost is %1%").arg(100)); +#endif + + connect(&d->slider, SIGNAL(valueChanged(int)), SLOT(_k_sliderChanged(int))); + connect(&d->muteButton, SIGNAL(clicked()), SLOT(_k_buttonClicked())); + + setFocusProxy(&d->slider); +} + +VolumeSlider::VolumeSlider(AudioOutput *output, QWidget *parent) + : QWidget(parent), + k_ptr(new VolumeSliderPrivate(this)) +{ + K_D(VolumeSlider); +#ifndef QT_NO_TOOLTIP + setToolTip(tr("Volume: %1%").arg(100)); +#endif +#ifndef QT_NO_WHATSTHIS + setWhatsThis(tr("Use this slider to adjust the volume. The leftmost position is 0%, the rightmost is %1%").arg(100)); +#endif + + connect(&d->slider, SIGNAL(valueChanged(int)), SLOT(_k_sliderChanged(int))); + connect(&d->muteButton, SIGNAL(clicked()), SLOT(_k_buttonClicked())); + + if (output) { + d->output = output; + d->slider.setValue(qRound(100 * output->volume())); + d->slider.setEnabled(true); + d->muteButton.setEnabled(true); + connect(output, SIGNAL(volumeChanged(qreal)), SLOT(_k_volumeChanged(qreal))); + connect(output, SIGNAL(mutedChanged(bool)), SLOT(_k_mutedChanged(bool))); + } + + setFocusProxy(&d->slider); +} + +VolumeSlider::~VolumeSlider() +{ + delete k_ptr; +} + +bool VolumeSlider::isMuteVisible() const +{ + return k_ptr->muteButton.isVisible(); +} + +void VolumeSlider::setMuteVisible(bool visible) +{ + k_ptr->muteButton.setVisible(visible); +} + +QSize VolumeSlider::iconSize() const +{ + return k_ptr->muteButton.iconSize(); +} + +void VolumeSlider::setIconSize(const QSize &iconSize) +{ + pDebug() << Q_FUNC_INFO << iconSize; + k_ptr->muteButton.setIconSize(iconSize); +} + +qreal VolumeSlider::maximumVolume() const +{ + return k_ptr->slider.maximum() * 0.01; +} + +void VolumeSlider::setMaximumVolume(qreal volume) +{ + int max = static_cast<int>(volume * 100); + k_ptr->slider.setMaximum(max); +#ifndef QT_NO_WHATSTHIS + setWhatsThis(tr("Use this slider to adjust the volume. The leftmost position is 0%, the rightmost is %1%") + .arg(max)); +#endif +} + +Qt::Orientation VolumeSlider::orientation() const +{ + return k_ptr->slider.orientation(); +} + +void VolumeSlider::setOrientation(Qt::Orientation o) +{ + K_D(VolumeSlider); + Qt::Alignment align = (o == Qt::Horizontal ? Qt::AlignVCenter : Qt::AlignHCenter); + d->layout.setAlignment(&d->muteButton, align); + d->layout.setAlignment(&d->slider, align); + d->layout.setDirection(o == Qt::Horizontal ? QBoxLayout::LeftToRight : QBoxLayout::TopToBottom); + d->slider.setOrientation(o); +} + +AudioOutput *VolumeSlider::audioOutput() const +{ + K_D(const VolumeSlider); + return d->output; +} + +void VolumeSlider::setAudioOutput(AudioOutput *output) +{ + K_D(VolumeSlider); + if (d->output) { + disconnect(d->output, 0, this, 0); + } + d->output = output; + if (output) { + d->slider.setValue(qRound(100 * output->volume())); + d->slider.setEnabled(true); + d->muteButton.setEnabled(true); + + d->_k_volumeChanged(output->volume()); + d->_k_mutedChanged(output->isMuted()); + + connect(output, SIGNAL(volumeChanged(qreal)), SLOT(_k_volumeChanged(qreal))); + connect(output, SIGNAL(mutedChanged(bool)), SLOT(_k_mutedChanged(bool))); + } else { + d->slider.setValue(100); + d->slider.setEnabled(false); + d->muteButton.setEnabled(false); + } +} + +void VolumeSliderPrivate::_k_buttonClicked() +{ + if (output) { + output->setMuted(!output->isMuted()); + } else { + slider.setEnabled(false); + muteButton.setEnabled(false); + } +} + +void VolumeSliderPrivate::_k_mutedChanged(bool muted) +{ +#ifndef QT_NO_TOOLTIP + Q_Q(VolumeSlider); +#endif + if (muted) { +#ifndef QT_NO_TOOLTIP + q->setToolTip(VolumeSlider::tr("Muted")); +#endif + muteButton.setIcon(mutedIcon); + } else { +#ifndef QT_NO_TOOLTIP + q->setToolTip(VolumeSlider::tr("Volume: %1%").arg(static_cast<int>(output->volume() * 100.0))); +#endif + muteButton.setIcon(volumeIcon); + } +} + +void VolumeSliderPrivate::_k_sliderChanged(int value) +{ +#ifndef QT_NO_TOOLTIP + Q_Q(VolumeSlider); +#endif + + if (output) { +#ifndef QT_NO_TOOLTIP + if (!output->isMuted()) { + q->setToolTip(VolumeSlider::tr("Volume: %1%").arg(value)); + } +#endif + + ignoreVolumeChange = true; + output->setVolume((static_cast<qreal>(value)) * 0.01); + ignoreVolumeChange = false; + } else { + slider.setEnabled(false); + muteButton.setEnabled(false); + } +} + +void VolumeSliderPrivate::_k_volumeChanged(qreal value) +{ + if (!ignoreVolumeChange) { + slider.setValue(qRound(100 * value)); + } +} + +bool VolumeSlider::hasTracking() const +{ + return k_ptr->slider.hasTracking(); +} + +void VolumeSlider::setTracking(bool tracking) +{ + k_ptr->slider.setTracking(tracking); +} + +int VolumeSlider::pageStep() const +{ + return k_ptr->slider.pageStep(); +} + +void VolumeSlider::setPageStep(int milliseconds) +{ + k_ptr->slider.setPageStep(milliseconds); +} + +int VolumeSlider::singleStep() const +{ + return k_ptr->slider.singleStep(); +} + +void VolumeSlider::setSingleStep(int milliseconds) +{ + k_ptr->slider.setSingleStep(milliseconds); +} + +} // namespace Phonon + +#endif //QT_NO_PHONON_VOLUMESLIDER + +QT_END_NAMESPACE + +#include "moc_volumeslider.cpp" + +// vim: sw=4 et diff --git a/src/3rdparty/phonon/phonon/volumeslider.h b/src/3rdparty/phonon/phonon/volumeslider.h new file mode 100644 index 0000000..47863a8 --- /dev/null +++ b/src/3rdparty/phonon/phonon/volumeslider.h @@ -0,0 +1,155 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef PHONON_UI_VOLUMESLIDER_H +#define PHONON_UI_VOLUMESLIDER_H + +#include "phonon_export.h" +#include "phonondefs.h" +#include <QtGui/QWidget> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_VOLUMESLIDER + +namespace Phonon +{ +class AudioOutput; +class VolumeSliderPrivate; + +/** \class VolumeSlider volumeslider.h Phonon/VolumeSlider + * \short Widget providing a slider to control the volume of an AudioOutput. + * + * \ingroup PhononWidgets + * \author Matthias Kretz <kretz@kde.org> + */ +class PHONON_EXPORT VolumeSlider : public QWidget +{ + Q_OBJECT + K_DECLARE_PRIVATE(VolumeSlider) + /** + * This property holds the maximum volume that can be set with this slider. + * + * By default the maximum value is 1.0 (100%). + */ + Q_PROPERTY(qreal maximumVolume READ maximumVolume WRITE setMaximumVolume) + /** + * This property holds the orientation of the slider. + * + * The orientation must be Qt::Vertical (the default) or Qt::Horizontal. + */ + Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation) + + /** + * This property holds whether slider tracking is enabled. + * + * If tracking is enabled (the default), the volume changes + * while the slider is being dragged. If tracking is + * disabled, the volume changes only when the user + * releases the slider. + */ + Q_PROPERTY(bool tracking READ hasTracking WRITE setTracking) + + /** + * This property holds the page step. + * + * The larger of two natural steps that a slider provides and + * typically corresponds to the user pressing PageUp or PageDown. + * + * Defaults to 5 (5% of the voltage). + */ + Q_PROPERTY(int pageStep READ pageStep WRITE setPageStep) + + /** + * This property holds the single step. + * + * The smaller of two natural steps that a slider provides and + * typically corresponds to the user pressing an arrow key. + * + * Defaults to 1 (1% of the voltage). + */ + Q_PROPERTY(int singleStep READ singleStep WRITE setSingleStep) + + /** + * This property holds whether the mute button/icon next to the slider is visible. + * + * By default the mute button/icon is visible. + */ + Q_PROPERTY(bool muteVisible READ isMuteVisible WRITE setMuteVisible) + + /** + * \brief the icon size used for the mute button/icon. + * + * The default size is defined by the GUI style. + */ + Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize) + public: + /** + * Constructs a new volume slider with a \p parent. + */ + explicit VolumeSlider(QWidget *parent = 0); + explicit VolumeSlider(AudioOutput *, QWidget *parent = 0); + ~VolumeSlider(); + + bool hasTracking() const; + void setTracking(bool tracking); + int pageStep() const; + void setPageStep(int milliseconds); + int singleStep() const; + void setSingleStep(int milliseconds); + bool isMuteVisible() const; + QSize iconSize() const; + qreal maximumVolume() const; + Qt::Orientation orientation() const; + AudioOutput *audioOutput() const; + + public Q_SLOTS: + void setMaximumVolume(qreal); + void setOrientation(Qt::Orientation); + void setMuteVisible(bool); + void setIconSize(const QSize &size); + + /** + * Sets the audio output object to be controlled by this slider. + */ + void setAudioOutput(Phonon::AudioOutput *); + + protected: + VolumeSliderPrivate *const k_ptr; + + private: + Q_PRIVATE_SLOT(k_ptr, void _k_sliderChanged(int)) + Q_PRIVATE_SLOT(k_ptr, void _k_volumeChanged(qreal)) + Q_PRIVATE_SLOT(k_ptr, void _k_mutedChanged(bool)) + Q_PRIVATE_SLOT(k_ptr, void _k_buttonClicked()) +}; + +} // namespace Phonon + +#endif //QT_NO_PHONON_VOLUMESLIDER + +QT_END_NAMESPACE +QT_END_HEADER + +// vim: sw=4 ts=4 et +#endif // PHONON_UI_VOLUMESLIDER_H diff --git a/src/3rdparty/phonon/phonon/volumeslider_p.h b/src/3rdparty/phonon/phonon/volumeslider_p.h new file mode 100644 index 0000000..6d8009a --- /dev/null +++ b/src/3rdparty/phonon/phonon/volumeslider_p.h @@ -0,0 +1,101 @@ +/* This file is part of the KDE project + Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org> + + 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.), Trolltech ASA + (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 <http://www.gnu.org/licenses/>. + +*/ + +#ifndef VOLUMESLIDER_P_H +#define VOLUMESLIDER_P_H + +#include "volumeslider.h" +#include <QtGui/QBoxLayout> +#include <QtGui/QSlider> +#include <QtGui/QLabel> +#include <QtGui/QPixmap> +#include <QtGui/QToolButton> +#include "factory_p.h" +#include "audiooutput.h" +#include <QtGui/QIcon> +#include <QtCore/QPointer> +#include "platform_p.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PHONON_VOLUMESLIDER + +namespace Phonon +{ +class VolumeSliderPrivate +{ + Q_DECLARE_PUBLIC(VolumeSlider) + protected: + VolumeSliderPrivate(VolumeSlider *parent) + : q_ptr(parent), + layout(QBoxLayout::LeftToRight, parent), + slider(Qt::Horizontal, parent), + muteButton(parent), + volumeIcon(Platform::icon(QLatin1String("player-volume"), parent->style())), + mutedIcon(Platform::icon(QLatin1String("player-volume-muted"), parent->style())), + output(0), + ignoreVolumeChange(false) + { + slider.setRange(0, 100); + slider.setPageStep(5); + slider.setSingleStep(1); + + muteButton.setIcon(volumeIcon); + muteButton.setAutoRaise(true); + layout.setMargin(0); + layout.setSpacing(2); + layout.addWidget(&muteButton, 0, Qt::AlignVCenter); + layout.addWidget(&slider, 0, Qt::AlignVCenter); + + slider.setEnabled(false); + muteButton.setEnabled(false); + + if (volumeIcon.isNull()) { + muteButton.setVisible(false); + } + } + + VolumeSlider *q_ptr; + + void _k_sliderChanged(int); + void _k_volumeChanged(qreal); + void _k_mutedChanged(bool); + void _k_buttonClicked(); + + private: + QBoxLayout layout; + QSlider slider; + QToolButton muteButton; + QIcon volumeIcon; + QIcon mutedIcon; + + QPointer<AudioOutput> output; + bool ignoreVolumeChange; +}; +} // namespace Phonon + +#endif //QT_NO_PHONON_VOLUMESLIDER + +QT_END_NAMESPACE + +#endif // VOLUMESLIDER_P_H +// vim: sw=4 sts=4 et tw=100 diff --git a/src/3rdparty/phonon/qt7/CMakeLists.txt b/src/3rdparty/phonon/qt7/CMakeLists.txt new file mode 100644 index 0000000..5310be1 --- /dev/null +++ b/src/3rdparty/phonon/qt7/CMakeLists.txt @@ -0,0 +1,58 @@ +# Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +# +# 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 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 <http://www.gnu.org/licenses/>. +project(phonon-qt7) +include(ConfigureChecks.cmake) + +include_directories(${OPENGL_INCLUDE_DIR}) + +if (BUILD_PHONON_QT7) + set(phonon_qt7_SRCS + quicktimevideoplayer.mm + backendheader.mm + medianodevideopart.mm + medianodeevent.mm + audiooutput.mm + backendinfo.mm + audiosplitter.mm + audioeffects.mm + quicktimestreamreader.mm + medianode.mm + backend.mm + mediaobject.mm + mediaobjectaudionode.mm + audiomixer.mm + quicktimeaudioplayer.mm + videoframe.mm + quicktimemetadata.mm + audiodevice.mm + audioconnection.mm + audiograph.mm + audionode.mm + videowidget.mm + ) + + automoc4_add_library(phonon_qt7 MODULE ${phonon_qt7_SRCS}) + target_link_libraries(phonon_qt7 + ${QT_QTGUI_LIBRARY} ${QT_QTOPENGL_LIBRARY} + ${PHONON_LIBS} + "-framework QuickTime" + "-framework AudioUnit" + "-framework AudioToolbox" + "-framework CoreAudio" + "-framework QuartzCore" + "-framework QTKit" + ) + install(TARGETS phonon_qt7 DESTINATION ${PLUGIN_INSTALL_DIR}) + +endif (BUILD_PHONON_QT7) diff --git a/src/3rdparty/phonon/qt7/ConfigureChecks.cmake b/src/3rdparty/phonon/qt7/ConfigureChecks.cmake new file mode 100644 index 0000000..9644867 --- /dev/null +++ b/src/3rdparty/phonon/qt7/ConfigureChecks.cmake @@ -0,0 +1,16 @@ +# Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +# +# 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 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 <http://www.gnu.org/licenses/>. + +# Find the frameworks if necessary +set(BUILD_PHONON_QT7 TRUE) diff --git a/src/3rdparty/phonon/qt7/audioconnection.h b/src/3rdparty/phonon/qt7/audioconnection.h new file mode 100644 index 0000000..2129744 --- /dev/null +++ b/src/3rdparty/phonon/qt7/audioconnection.h @@ -0,0 +1,84 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_AudioConnection_H +#define Phonon_QT7_AudioConnection_H + +#include <QtCore/QObject> +#include "backendheader.h" + +#include <AudioToolbox/AudioToolbox.h> +#include <AudioUnit/AudioUnit.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class MediaNode; + class AudioNode; + class AudioGraph; + + class AudioConnection { + public: + AudioConnection(); + AudioConnection(MediaNode *sink); + AudioConnection(MediaNode *source, int output, MediaNode *sink, int input); + + AudioConnection(AudioNode *sink); + AudioConnection(AudioNode *source, int output, AudioNode *sink, int input); + + ~AudioConnection(); + + bool connect(AudioGraph *graph); + bool disconnect(AudioGraph *graph); + + bool updateStreamSpecification(); + bool isBetween(MediaNode *source, MediaNode *sink); + bool isValid(); + bool isSinkOnly(); + void freeMemoryAllocations(); + void invalidate(); + + MediaNode *m_source; + AudioNode *m_sourceAudioNode; + int m_sourceOutputBus; + + MediaNode *m_sink; + AudioNode *m_sinkAudioNode; + int m_sinkInputBus; + + AudioChannelLayout *m_sourceChannelLayout; + UInt32 m_sourceChannelLayoutSize; + + AudioChannelLayout *m_sinkChannelLayout; + UInt32 m_sinkChannelLayoutSize; + + AudioStreamBasicDescription m_sourceStreamDescription; + AudioStreamBasicDescription m_sinkStreamDescription; + + bool m_hasSourceSpecification; + bool m_hasSinkSpecification; + bool m_connected; + }; + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_AudioConnection_H diff --git a/src/3rdparty/phonon/qt7/audioconnection.mm b/src/3rdparty/phonon/qt7/audioconnection.mm new file mode 100644 index 0000000..39b8b30 --- /dev/null +++ b/src/3rdparty/phonon/qt7/audioconnection.mm @@ -0,0 +1,152 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "audioconnection.h" +#include "medianode.h" +#include "audionode.h" +#include "audiograph.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + + AudioConnection::AudioConnection() + : m_source(0), m_sourceAudioNode(0), m_sourceOutputBus(0), + m_sink(0), m_sinkAudioNode(0), m_sinkInputBus(0), + m_sourceChannelLayout(0), m_sinkChannelLayout(0), + m_hasSourceSpecification(false), m_hasSinkSpecification(false), m_connected(false) + {} + + AudioConnection::AudioConnection(MediaNode *source, int output, MediaNode *sink, int input) + : m_source(source), m_sourceAudioNode(source->m_audioNode), m_sourceOutputBus(output), + m_sink(sink), m_sinkAudioNode(sink->m_audioNode), m_sinkInputBus(input), + m_sourceChannelLayout(0), m_sinkChannelLayout(0), + m_hasSourceSpecification(false), m_hasSinkSpecification(false), m_connected(false) + {} + + AudioConnection::AudioConnection(MediaNode *sink) + : m_source(0), m_sourceAudioNode(0), m_sourceOutputBus(0), + m_sink(sink), m_sinkAudioNode(sink->m_audioNode), m_sinkInputBus(0), + m_sourceChannelLayout(0), m_sinkChannelLayout(0), m_connected(false) + {} + + AudioConnection::AudioConnection(AudioNode *source, int output, AudioNode *sink, int input) + : m_source(0), m_sourceAudioNode(source), m_sourceOutputBus(output), + m_sink(0), m_sinkAudioNode(sink), m_sinkInputBus(input), + m_sourceChannelLayout(0), m_sinkChannelLayout(0), + m_hasSourceSpecification(false), m_hasSinkSpecification(false), m_connected(false) + {} + + AudioConnection::AudioConnection(AudioNode *sink) + : m_source(0), m_sourceAudioNode(0), m_sourceOutputBus(0), + m_sink(0), m_sinkAudioNode(sink), m_sinkInputBus(0), + m_sourceChannelLayout(0), m_sinkChannelLayout(0), m_connected(false) + {} + + AudioConnection::~AudioConnection() + { + freeMemoryAllocations(); + } + + void AudioConnection::freeMemoryAllocations() + { + if (m_sinkChannelLayout && m_sourceChannelLayout != m_sinkChannelLayout) + free(m_sinkChannelLayout); + if (m_sourceChannelLayout) + free(m_sourceChannelLayout); + m_sinkChannelLayout = 0; + m_sourceChannelLayout = 0; + } + + bool AudioConnection::updateStreamSpecification() + { + m_hasSourceSpecification = false; + m_hasSinkSpecification = false; + freeMemoryAllocations(); + + bool updateOk; + if (m_sourceAudioNode){ + updateOk = m_sourceAudioNode->fillInStreamSpecification(this, AudioNode::Source); + if (!updateOk) + return false; + updateOk = m_sourceAudioNode->setStreamSpecification(this, AudioNode::Source); + if (!updateOk) + return false; + } + updateOk = m_sinkAudioNode->fillInStreamSpecification(this, AudioNode::Sink); + if (!updateOk) + return false; + updateOk = m_sinkAudioNode->setStreamSpecification(this, AudioNode::Sink); + if (!updateOk) + return false; + return true; + } + + bool AudioConnection::connect(AudioGraph *graph) + { + if (m_connected || !m_sourceAudioNode) + return true; + + DEBUG_AUDIO_GRAPH("Connection" << int(this) << "connect" + << int(m_sourceAudioNode) << m_sourceOutputBus << "->" + << int(m_sinkAudioNode) << m_sinkInputBus) + + AUNode sourceOut = m_sourceAudioNode->getOutputAUNode(); + AUNode sinkIn = m_sinkAudioNode->getInputAUNode(); + OSStatus err = AUGraphConnectNodeInput(graph->audioGraphRef(), sourceOut, m_sourceOutputBus, sinkIn, m_sinkInputBus); + m_connected = (err == noErr) ? true : false; + return m_connected; + } + + bool AudioConnection::disconnect(AudioGraph *graph) + { + if (!m_connected || !m_sourceAudioNode) + return true; + + DEBUG_AUDIO_GRAPH("Connection" << int(this) << "disconnect" + << int(m_sourceAudioNode) << m_sourceOutputBus << "->" + << int(m_sinkAudioNode) << m_sinkInputBus) + + AUNode sinkIn = m_sinkAudioNode->getInputAUNode(); + AUGraphDisconnectNodeInput(graph->audioGraphRef(), sinkIn, m_sinkInputBus); + m_connected = false; + return true; + } + + void AudioConnection::invalidate() + { + m_connected = false; + } + + bool AudioConnection::isBetween(MediaNode *source, MediaNode *sink){ + return (source == m_source) && (sink == m_sink); + } + + bool AudioConnection::isValid(){ + return (m_sourceAudioNode != 0); + } + + bool AudioConnection::isSinkOnly(){ + return (m_sourceAudioNode == 0) && (m_sinkAudioNode != 0); + } + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/audiodevice.h b/src/3rdparty/phonon/qt7/audiodevice.h new file mode 100644 index 0000000..36e937e --- /dev/null +++ b/src/3rdparty/phonon/qt7/audiodevice.h @@ -0,0 +1,52 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_AUDIODEVICE_H +#define Phonon_QT7_AUDIODEVICE_H + +#include <AudioToolbox/AudioToolbox.h> +#include <AudioUnit/AudioUnit.h> +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class AudioDevice + { + public: + enum Scope {In, Out}; + + static QList<AudioDeviceID> devices(Scope scope); + static AudioDeviceID defaultDevice(Scope scope); + static AudioDeviceID defaultSystemDevice(Scope scope); + static AudioDeviceID currentDevice(AudioUnit unit, Scope scope); + static bool setDevice(AudioUnit unit, AudioDeviceID deviceID, Scope scope); + static QString deviceName(AudioDeviceID deviceId); + static QString deviceSourceName(AudioDeviceID deviceID); + static QString deviceSourceNameElseDeviceName(AudioDeviceID deviceID); + static QString deviceNameElseDeviceSourceName(AudioDeviceID deviceID); + static QString deviceUID(AudioDeviceID deviceID); + }; + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_AUDIODEVICE_H diff --git a/src/3rdparty/phonon/qt7/audiodevice.mm b/src/3rdparty/phonon/qt7/audiodevice.mm new file mode 100644 index 0000000..6bec62d --- /dev/null +++ b/src/3rdparty/phonon/qt7/audiodevice.mm @@ -0,0 +1,177 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "audiodevice.h" +#include "audiograph.h" +#include <QtCore/QVector> +#include "backendheader.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +QList<AudioDeviceID> AudioDevice::devices(Scope scope) +{ + QList<AudioDeviceID> devices; + + // Insert the default device explicit + if (AudioDeviceID defdev = defaultDevice(scope)) + devices << defdev; + + // How many input/output devices are awailable: + UInt32 deviceCount = 0; + OSStatus err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &deviceCount, 0); + BACKEND_ASSERT3(err == noErr, "Could not get number of audio devices awailable.", FATAL_ERROR, devices) + + // Get list of all devices: + AudioDeviceID deviceArray[deviceCount]; + err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &deviceCount, &deviceArray); + BACKEND_ASSERT3(err == noErr, "Could not get audio devices list.", FATAL_ERROR, devices) + + for (uint i=0; i<deviceCount; i++){ + if (!devices.contains(deviceArray[i])){ + // Check if the current device is input or output: + UInt32 size; + err = AudioDeviceGetPropertyInfo(deviceArray[i], 0, scope == In, kAudioDevicePropertyStreams, &size, 0); + if (err == noErr && size > 0) + devices << deviceArray[i]; + } + } + return devices; +} + +AudioDeviceID AudioDevice::defaultSystemDevice(Scope scope) +{ + ARGUMENT_UNSUPPORTED(scope, In, NORMAL_ERROR, 0) + AudioDeviceID deviceID = 0; + UInt32 size = sizeof(deviceID); + OSStatus err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultSystemOutputDevice, &size, &deviceID); + BACKEND_ASSERT3(err == noErr, "Could not get default system audio device.", FATAL_ERROR, 0) + return deviceID; +} + +AudioDeviceID AudioDevice::defaultDevice(Scope scope) +{ + ARGUMENT_UNSUPPORTED(scope, In, NORMAL_ERROR, 0) + AudioDeviceID deviceID = 0; + UInt32 size = sizeof(deviceID); + OSStatus err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &deviceID); + BACKEND_ASSERT3(err == noErr, "Could not get default output audio device.", FATAL_ERROR, 0) + return deviceID; +} + +AudioDeviceID AudioDevice::currentDevice(AudioUnit /*unit*/, Scope /*scope*/) +{ + return 0; +#if 0 + +kAudioDevicePropertyDeviceUID + + if (!m_audioUnit) + return 0; + AudioDeviceID deviceID = 0; + UInt32 size = sizeof(deviceID); + OSStatus err = AudioUnitGetProperty(m_audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &size, &deviceID); + BACKEND_ASSERT3(err == noErr, "Could not get current audio device.", FATAL_ERROR, 0) + return deviceID; +#endif +} + +bool AudioDevice::setDevice(AudioUnit unit, AudioDeviceID deviceID, Scope scope) +{ + ARGUMENT_UNSUPPORTED(scope, In, NORMAL_ERROR, false) + UInt32 size = sizeof(deviceID); + OSStatus err = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceID, size); + if (err != noErr) + return false; + return true; +} + +QString AudioDevice::deviceSourceNameElseDeviceName(AudioDeviceID deviceID) +{ + QString name = deviceSourceName(deviceID); + if (name.isEmpty()) + name = deviceName(deviceID); + return name; +} + +QString AudioDevice::deviceNameElseDeviceSourceName(AudioDeviceID deviceID) +{ + QString name = deviceName(deviceID); + if (name.isEmpty()) + name = deviceSourceName(deviceID); + return name; +} + +QString AudioDevice::deviceName(AudioDeviceID deviceID) +{ + if (!deviceID) + return QString(); + CFStringRef cfString = 0; + UInt32 size = sizeof(cfString); + OSStatus err = AudioDeviceGetProperty(deviceID, 0, 0, kAudioDevicePropertyDeviceNameCFString, &size, &cfString); + if (err != noErr) + return QString(); + QString name = PhononCFString::toQString(cfString); + CFRelease(cfString); + return name; +} + +QString AudioDevice::deviceSourceName(AudioDeviceID deviceID) +{ + if (!deviceID) + return QString(); + UInt32 dataSource = 0; + UInt32 size = sizeof(dataSource); + OSStatus err = AudioDeviceGetProperty(deviceID, 0, 0, kAudioDevicePropertyDataSource, &size, &dataSource); + if (err != noErr) + return QString(); + + CFStringRef cfName = 0; + AudioValueTranslation translation = {&dataSource, sizeof(dataSource), &cfName, sizeof(cfName)}; + size = sizeof(translation); + err = AudioDeviceGetProperty(deviceID, 0, 0, kAudioDevicePropertyDataSourceNameForIDCFString, &size, &translation); + if (err != noErr){ + CFRelease(cfName); + return QString(); + } + QString name = PhononCFString::toQString(cfName); + CFRelease(cfName); + return name; +} + +QString AudioDevice::deviceUID(AudioDeviceID deviceID) +{ + if (!deviceID) + return QString(); + + CFStringRef cfString = 0; + UInt32 size = sizeof(cfString); + OSStatus err = AudioDeviceGetProperty(deviceID, 0, 0, kAudioDevicePropertyDeviceUID, &size, &cfString); + if (err != noErr) + return QString(); + QString uid = PhononCFString::toQString(cfString); + CFRelease(cfString); + return uid; +} + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/audioeffects.h b/src/3rdparty/phonon/qt7/audioeffects.h new file mode 100644 index 0000000..33fff1c --- /dev/null +++ b/src/3rdparty/phonon/qt7/audioeffects.h @@ -0,0 +1,80 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_AUDIOEFFECTS_H +#define Phonon_QT7_AUDIOEFFECTS_H + +#include <QtCore/QVariant> +#include <QtCore/QHash> +#include <phonon/effectinterface.h> +#include <phonon/effectparameter.h> +#include "medianode.h" +#include "audionode.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class AudioEffectAudioNode : public AudioNode + { + public: + AudioEffectAudioNode(int effectType); + int m_effectType; + + ComponentDescription getAudioNodeDescription() const; + void initializeAudioUnit(); + + QVariant parameterValue(const Phonon::EffectParameter &value) const; + void setParameterValue(const Phonon::EffectParameter ¶meter, const QVariant &newValue); + + private: + QHash<int, float> m_alteredParameters; + }; + +/////////////////////////////////////////////////////////////////////// + + class AudioEffect : public MediaNode, Phonon::EffectInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::EffectInterface) + + public: + AudioEffect(int effectType, QObject *parent = 0); + AudioEffectAudioNode *m_audioNode; + + QString name(); + QString description(); + + // EffectInterface: + virtual QList<Phonon::EffectParameter> parameters() const; + virtual QVariant parameterValue(const Phonon::EffectParameter ¶meter) const; + virtual void setParameterValue(const Phonon::EffectParameter ¶meter, const QVariant &newValue); + + static QList<int> effectList(); + + private: + Phonon::EffectParameter createParameter(const AudioUnit &audioUnit, const AudioUnitParameterID &id) const; + }; + + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_AUDIOEFFECTS_H diff --git a/src/3rdparty/phonon/qt7/audioeffects.mm b/src/3rdparty/phonon/qt7/audioeffects.mm new file mode 100644 index 0000000..b0ec3cc --- /dev/null +++ b/src/3rdparty/phonon/qt7/audioeffects.mm @@ -0,0 +1,254 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "audioeffects.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +AudioEffectAudioNode::AudioEffectAudioNode(int effectType) + : AudioNode(1, 1), m_effectType(effectType) +{ +} + +ComponentDescription AudioEffectAudioNode::getAudioNodeDescription() const +{ + ComponentDescription d; + d.componentType = kAudioUnitType_Effect; + d.componentSubType = m_effectType; + d.componentManufacturer = kAudioUnitManufacturer_Apple; + d.componentFlags = 0; + d.componentFlagsMask = 0; + return d; +} + +void AudioEffectAudioNode::initializeAudioUnit() +{ + if (!m_audioUnit) + return; + foreach(int id, m_alteredParameters.keys()){ + Float32 value = m_alteredParameters.value(id); + ComponentResult res = AudioUnitSetParameter(m_audioUnit, id, kAudioUnitScope_Global, 0, value, 0); + BACKEND_ASSERT2(res == noErr, "Could not initialize audio effect.", NORMAL_ERROR) + } +} + +QVariant AudioEffectAudioNode::parameterValue(const Phonon::EffectParameter ¶meter) const +{ + if (m_audioUnit){ + Float32 value = 0; + AudioUnitGetParameter(m_audioUnit, parameter.id(), kAudioUnitScope_Global, 0, &value); + return QVariant(value); + } else if (m_alteredParameters.contains(parameter.id())){ + return QVariant(m_alteredParameters.value(parameter.id())); + } else { + // Use default value: + AudioUnit tmpAudioUnit; + ComponentDescription description = getAudioNodeDescription(); + Component component = FindNextComponent(0, &description); + BACKEND_ASSERT3(component, "Could not get parameters of audio effect.", NORMAL_ERROR, QVariant()) + OSErr err = OpenAComponent(component, &tmpAudioUnit); + BACKEND_ASSERT3(err == noErr, "Could not get parameters of audio effect.", NORMAL_ERROR, QVariant()) + AudioUnitParameterInfo info; + UInt32 size = sizeof(info); + ComponentResult res = AudioUnitGetProperty(tmpAudioUnit, + kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, parameter.id(), &info, &size); + BACKEND_ASSERT3(res == noErr, "Could not get parameter info from audio effect.", NORMAL_ERROR, QVariant()) + return QVariant(info.defaultValue); + } +} + +void AudioEffectAudioNode::setParameterValue(const Phonon::EffectParameter ¶meter, const QVariant &newValue) +{ + Float32 value = 0; + if (newValue.isValid()){ + value = newValue.toDouble(); + m_alteredParameters.insert(parameter.id(), value); + } else { + // Use default value: + m_alteredParameters.remove(parameter.id()); + if (m_audioUnit){ + AudioUnit tmpAudioUnit; + ComponentDescription description = getAudioNodeDescription(); + Component component = FindNextComponent(0, &description); + BACKEND_ASSERT2(component, "Could not get parameters of audio effect.", NORMAL_ERROR) + OSErr err = OpenAComponent(component, &tmpAudioUnit); + BACKEND_ASSERT2(err == noErr, "Could not get parameters of audio effect.", NORMAL_ERROR) + AudioUnitParameterInfo info; + UInt32 size = sizeof(info); + ComponentResult res = AudioUnitGetProperty(tmpAudioUnit, + kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, parameter.id(), &info, &size); + BACKEND_ASSERT2(res == noErr, "Could not get parameter info from audio effect.", NORMAL_ERROR) + value = info.defaultValue; + } + } + + if (m_audioUnit){ + ComponentResult res = AudioUnitSetParameter(m_audioUnit, parameter.id(), kAudioUnitScope_Global, 0, value, 0); + BACKEND_ASSERT2(res == noErr, "Could not set effect parameter value.", NORMAL_ERROR) + } +} + +/////////////////////////////////////////////////////////////////////// + +AudioEffect::AudioEffect(int effectType, QObject *parent) + : MediaNode(AudioSink | AudioSource, 0, parent) +{ + m_audioNode = new AudioEffectAudioNode(effectType); + setAudioNode(m_audioNode); +} + +QList<Phonon::EffectParameter> AudioEffect::parameters() const +{ + QList<Phonon::EffectParameter> effectList; + // Create a temporary audio unit: + AudioUnit audioUnit; + ComponentDescription description = m_audioNode->getAudioNodeDescription(); + Component component = FindNextComponent(0, &description); + BACKEND_ASSERT3(component, "Could not get parameters of audio effect.", NORMAL_ERROR, effectList) + OSErr err = OpenAComponent(component, &audioUnit); + BACKEND_ASSERT3(err == noErr, "Could not get parameters of audio effect.", NORMAL_ERROR, effectList) + + UInt32 size = 0; + // Get parameter count: + ComponentResult res = AudioUnitGetProperty(audioUnit, + kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0, 0, &size); + BACKEND_ASSERT3(res == noErr, "Could not get parameter count from audio effect.", NORMAL_ERROR, effectList) + int paramCount = size / sizeof(AudioUnitParameterID); + + // Get parameters: + AudioUnitParameterID parameters[paramCount]; + res = AudioUnitGetProperty(audioUnit, + kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0, ¶meters, &size); + BACKEND_ASSERT3(res == noErr, "Could not get parameter list from audio effect.", NORMAL_ERROR, effectList) + + for (int i=0; i<paramCount; ++i) + effectList << createParameter(audioUnit, parameters[i]); + + CloseComponent(audioUnit); + return effectList; +} + +QString AudioEffect::name() +{ + ComponentDescription description = m_audioNode->getAudioNodeDescription(); + Component component = FindNextComponent(0, &description); + BACKEND_ASSERT3(component, "Could not get audio effect name.", NORMAL_ERROR, QLatin1String("<unknown effect>")) + + ComponentDescription cDesc; + Handle nameH = NewHandle(0); + GetComponentInfo(component, &cDesc, nameH, 0, 0); + HLock(nameH); + char *namePtr = *nameH; + int len = *namePtr++; + namePtr[len] = 0; + QString qsName = QString::fromUtf8(namePtr); + DisposeHandle(nameH); + return qsName; +} + +QString AudioEffect::description() +{ + ComponentDescription description = m_audioNode->getAudioNodeDescription(); + Component component = FindNextComponent(0, &description); + BACKEND_ASSERT3(component, "Could not get audio effect description.", NORMAL_ERROR, QLatin1String("<unknown effect>")) + + ComponentDescription cDesc; + Handle descH = NewHandle(0); + GetComponentInfo(component, &cDesc, 0, descH, 0); + HLock(descH); + char *descPtr = *descH; + int len = *descPtr++; + descPtr[len] = 0; + QString qsDesc = QString::fromUtf8(descPtr); + DisposeHandle(descH); + return qsDesc; +} + +QList<int> AudioEffect::effectList() +{ + QList<int> effects; + + ComponentDescription d; + d.componentType = kAudioUnitType_Effect; + d.componentSubType = 0; + d.componentManufacturer = 0; + d.componentFlags = 0; + d.componentFlagsMask = 0; + Component component = FindNextComponent(0, &d); + + while (component) { + ComponentDescription cDesc; + GetComponentInfo(component, &cDesc, 0, 0, 0); + effects << cDesc.componentSubType; + component = FindNextComponent(component, &d); + } + return effects; +} + +Phonon::EffectParameter AudioEffect::createParameter(const AudioUnit &audioUnit, const AudioUnitParameterID &id) const +{ + AudioUnitParameterInfo info; + UInt32 size = sizeof(info); + ComponentResult res = AudioUnitGetProperty(audioUnit, + kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, id, &info, &size); + BACKEND_ASSERT3(res == noErr, "Could not get parameter info from audio effect.", NORMAL_ERROR, Phonon::EffectParameter()) + + QString name = info.flags & kAudioUnitParameterFlag_HasCFNameString + ? PhononCFString::toQString(info.cfNameString) : QLatin1String("<unknown parameter>"); + + Phonon::EffectParameter::Hint hint; + switch(info.unit){ + case (kAudioUnitParameterUnit_Indexed): + case (kAudioUnitParameterUnit_Seconds): + case (kAudioUnitParameterUnit_SampleFrames): + case (kAudioUnitParameterUnit_Milliseconds): + hint = Phonon::EffectParameter::IntegerHint; + break; + case (kAudioUnitParameterUnit_Boolean): + hint = Phonon::EffectParameter::ToggledHint; + break; + default: + hint = Phonon::EffectParameter::LogarithmicHint; + break; + } + + QVariant def(info.defaultValue); + QVariant min(info.minValue); + QVariant max(info.maxValue); + return Phonon::EffectParameter(id, name, hint, def, min, max, QVariantList(), name); +} + +QVariant AudioEffect::parameterValue(const Phonon::EffectParameter &value) const +{ + return m_audioNode->parameterValue(value); +} + +void AudioEffect::setParameterValue(const Phonon::EffectParameter ¶meter, const QVariant &newValue) +{ + m_audioNode->setParameterValue(parameter, newValue); +} + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE + +#include "moc_audioeffects.cpp" diff --git a/src/3rdparty/phonon/qt7/audiograph.h b/src/3rdparty/phonon/qt7/audiograph.h new file mode 100644 index 0000000..76bc21a --- /dev/null +++ b/src/3rdparty/phonon/qt7/audiograph.h @@ -0,0 +1,86 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_AUDIOGRAPH_H +#define Phonon_QT7_AUDIOGRAPH_H + +#include <AudioToolbox/AudioToolbox.h> +#include <AudioUnit/AudioUnit.h> + +#include <QtCore/qnamespace.h> +#include "audioconnection.h" +#include "medianode.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class AudioGraph : public MediaNode + { + public: + AudioGraph(MediaNode *root); + virtual ~AudioGraph(); + AUGraph audioGraphRef(); + void startAllOverFromScratch(); + + bool openAndInit(); + void start(); + void tryStartGraph(); + void stop(); + void prepare(); + void rebuildGraph(); + void update(); + bool isRunning(); + void setPaused(bool pause); + bool graphCannotPlay(); + void rebuildGraphIfNeeded(); + void updateStreamSpecifications(); + void setStatusCannotPlay(); + MediaNode *root(); + void notify(const MediaNodeEvent *event, bool propagate = true); + + private: + friend class MediaNode; + friend class AudioNode; + + void deleteGraph(); + bool updateStreamSpecificationRecursive(AudioConnection *connection); + void createAndConnectAuNodesRecursive(AudioConnection *connection); + bool createAudioUnitsRecursive(AudioConnection *connection); + + void connectLate(AudioConnection *connection); + void disconnectLate(AudioConnection *connection); + + int nodeCount(); + + bool m_initialized; + bool m_startedLogically; + bool m_rebuildLater; + bool m_graphCannotPlay; + bool m_paused; + + AUGraph m_audioGraphRef; + MediaNode *m_root; + }; + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_AUDIOGRAPH_H diff --git a/src/3rdparty/phonon/qt7/audiograph.mm b/src/3rdparty/phonon/qt7/audiograph.mm new file mode 100644 index 0000000..0d096e3 --- /dev/null +++ b/src/3rdparty/phonon/qt7/audiograph.mm @@ -0,0 +1,320 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "audiograph.h" +#include "quicktimeaudioplayer.h" +#include "medianode.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +AudioGraph::AudioGraph(MediaNode *root) : MediaNode(AudioGraphNode, 0, root), m_root(root) +{ + m_audioGraphRef = 0; + m_initialized = false; + m_startedLogically = false; + m_graphCannotPlay = false; + m_paused = false; +} + +AudioGraph::~AudioGraph() +{ + deleteGraph(); +} + +void AudioGraph::startAllOverFromScratch() +{ + MediaNodeEvent event(MediaNodeEvent::AudioGraphAboutToBeDeleted, this); + m_root->notify(&event); + deleteGraph(); +} + +void AudioGraph::deleteGraph() +{ + if (m_audioGraphRef){ + AUGraphStop(m_audioGraphRef); + AUGraphUninitialize(m_audioGraphRef); + AUGraphClose(m_audioGraphRef); + DisposeAUGraph(m_audioGraphRef); + m_initialized = false; + m_graphCannotPlay = false; + DEBUG_AUDIO_GRAPH("Graph ref in" << int(this) << "is deleted") + } +} + +MediaNode *AudioGraph::root() +{ + return m_root; +} + +AUGraph AudioGraph::audioGraphRef() +{ + return m_audioGraphRef; +} + +void AudioGraph::setStatusCannotPlay() +{ + DEBUG_AUDIO_GRAPH("Graph" << int(this) << "received 'cannot play' request") + if (!m_graphCannotPlay){ + stop(); + m_graphCannotPlay = true; + MediaNodeEvent e(MediaNodeEvent::AudioGraphCannotPlay, this); + m_root->notify(&e); + } +} + +void AudioGraph::rebuildGraph() +{ + DEBUG_AUDIO_GRAPH("Graph" << int(this) << "is rebuilding") + startAllOverFromScratch(); + if (!openAndInit()){ + setStatusCannotPlay(); + } else { + tryStartGraph(); + m_graphCannotPlay = false; + } +} + +bool AudioGraph::graphCannotPlay() +{ + return m_graphCannotPlay; +} + +void AudioGraph::updateStreamSpecifications() +{ + if (!m_initialized){ + if (m_graphCannotPlay) + rebuildGraph(); + return; + } + + AudioConnection rootConnection(m_root); + bool updateOk = updateStreamSpecificationRecursive(&rootConnection); + if (!updateOk){ + DEBUG_AUDIO_GRAPH("Graph" << int(this) << "could not update stream specification. Rebuild.") + rebuildGraph(); + } +} + +bool AudioGraph::updateStreamSpecificationRecursive(AudioConnection *connection) +{ + bool updateOk = connection->updateStreamSpecification(); + if (!updateOk) + return false; + + for (int i=0; i<connection->m_sink->m_audioSinkList.size(); ++i){ + if (!updateStreamSpecificationRecursive(connection->m_sink->m_audioSinkList[i])) + return false; + } + return true; +} + +bool AudioGraph::openAndInit() +{ + OSStatus err; + err = NewAUGraph(&m_audioGraphRef); + BACKEND_ASSERT3(err == noErr, "Could not create audio graph.", NORMAL_ERROR, false) + + MediaNodeEvent eventNew(MediaNodeEvent::NewAudioGraph, this); + m_root->notify(&eventNew); + + AudioConnection rootConnection(m_root); + createAndConnectAuNodesRecursive(&rootConnection); + err = AUGraphOpen(m_audioGraphRef); + BACKEND_ASSERT3(err == noErr, "Could not create audio graph.", NORMAL_ERROR, false) + + if (!createAudioUnitsRecursive(&rootConnection)) + return false; + + err = AUGraphInitialize(m_audioGraphRef); + BACKEND_ASSERT3(err == noErr, "Could not initialize audio graph.", NORMAL_ERROR, false) + + m_initialized = true; + MediaNodeEvent eventInit(MediaNodeEvent::AudioGraphInitialized, this); + m_root->notify(&eventInit); + return true; +} + +void AudioGraph::createAndConnectAuNodesRecursive(AudioConnection *connection) +{ + connection->m_sink->m_audioNode->createAndConnectAUNodes(); + for (int i=0; i<connection->m_sink->m_audioSinkList.size(); ++i){ + AudioConnection *c = connection->m_sink->m_audioSinkList[i]; + createAndConnectAuNodesRecursive(c); + bool ok = c->connect(this); + BACKEND_ASSERT2(ok, "Could not connect an audio nodes pair in the audio graph.", NORMAL_ERROR) + } +} + +bool AudioGraph::createAudioUnitsRecursive(AudioConnection *connection) +{ + connection->m_sink->m_audioNode->createAudioUnits(); + bool ok = connection->updateStreamSpecification(); + if (!ok) + return false; + for (int i=0; i<connection->m_sink->m_audioSinkList.size(); ++i){ + if (!createAudioUnitsRecursive(connection->m_sink->m_audioSinkList[i])) + return false; + } + return true; +} + +void AudioGraph::tryStartGraph() +{ + // The graph will only start if the background AUGraph + // is valid. Therefore we just try. If it fails, user + // actions like connect etc. migh make the graph valid + // at a later point. + if (m_startedLogically && !isRunning()){ + OSStatus err = AUGraphStart(m_audioGraphRef); + if (err == noErr) + DEBUG_AUDIO_GRAPH("Graph" << int(this) << "started") + else + DEBUG_AUDIO_GRAPH("Graph" << int(this) << "could not start") + } +} + +bool AudioGraph::isRunning() +{ + Boolean running = false; + AUGraphIsRunning(m_audioGraphRef, &running); + return running; +} + +void AudioGraph::setPaused(bool pause) +{ + // This function should only make + // a difference if the graph is + // running before pausing. + if (pause){ + if (isRunning()){ + stop(); + m_paused = true; + } + } else if (m_paused){ + start(); + m_paused = false; + } +} + +void AudioGraph::connectLate(AudioConnection *connection) +{ + MediaNodeEvent event(MediaNodeEvent::NewAudioGraph, this); + connection->m_sink->notify(&event); + + if (!m_initialized) + return; + + DEBUG_AUDIO_GRAPH("Graph:" << int(this) << "create and connect audio sink after init:" << int(connection->m_sink->m_audioNode)) + AudioConnection startConnection(connection->m_source); + createAndConnectAuNodesRecursive(&startConnection); + + if (!createAudioUnitsRecursive(&startConnection)){ + DEBUG_AUDIO_GRAPH("Graph" << int(this) << "could not update stream specification. Rebuild.") + rebuildGraph(); + } +} + +void AudioGraph::disconnectLate(AudioConnection *connection) +{ + if (!m_initialized) + return; + + DEBUG_AUDIO_GRAPH("Graph:" << int(this) << "disconnect audio sink after init:" << int(connection->m_sink->m_audioNode)) + + if (!connection->disconnect(this)){ + DEBUG_AUDIO_GRAPH("Graph" << int(this) << "could not disconnect audio sink. Rebuild.") + rebuildGraph(); + } +} + +void AudioGraph::update() +{ + if (m_startedLogically){ + if (m_initialized){ + // Quick solution: + AUGraphUpdate(m_audioGraphRef, 0); + tryStartGraph(); + } else + rebuildGraph(); + } +} + +int AudioGraph::nodeCount() +{ + if (!m_audioGraphRef) + return 0; + UInt32 count; + AUGraphGetNodeCount(m_audioGraphRef, &count); + return int(count); +} + +void AudioGraph::prepare() +{ + if (!m_initialized) + rebuildGraph(); +} + +void AudioGraph::start() +{ + // Start does not mean 'start to play + // music'. It means 'prepare to receive + // audio from the player units'. + DEBUG_AUDIO_GRAPH("Graph" << int(this) << "asked to start (cannot play:" << m_graphCannotPlay << ")") + m_startedLogically = true; + + if (m_graphCannotPlay) + return; + + if (!m_initialized) + rebuildGraph(); + + if (!m_graphCannotPlay) + tryStartGraph(); +} + +void AudioGraph::stop() +{ + DEBUG_AUDIO_GRAPH("Graph" << int(this) << "asked to stop") + if (m_audioGraphRef) + AUGraphStop(m_audioGraphRef); + m_startedLogically = false; +} + +void AudioGraph::notify(const MediaNodeEvent *event, bool propagate) +{ + switch (event->type()){ + case MediaNodeEvent::StartConnectionChange: + if (m_graphCannotPlay) + startAllOverFromScratch(); + break; + case MediaNodeEvent::EndConnectionChange: + update(); + break; + default: + break; + } + m_root->notify(event, propagate); +} + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/audiomixer.h b/src/3rdparty/phonon/qt7/audiomixer.h new file mode 100644 index 0000000..275d7df --- /dev/null +++ b/src/3rdparty/phonon/qt7/audiomixer.h @@ -0,0 +1,91 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_AUDIOMIXER_H +#define Phonon_QT7_AUDIOMIXER_H + +#include <QtCore/QObject> +#include <QtCore/QTime> +#include <QtCore/QEvent> +#include <phonon/effectinterface.h> +#include <phonon/effectparameter.h> +#include <phonon/volumefaderinterface.h> +#include "medianode.h" +#include "audionode.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class AudioMixerAudioNode : public AudioNode + { + public: + AudioMixerAudioNode(); + void setVolume(float volume, int bus = 0); + float volume(int bus = 0); + + protected: + ComponentDescription getAudioNodeDescription() const; + void initializeAudioUnit(); + + private: + friend class AudioMixer; + int m_numberOfBusses; + float m_volume; + }; + + class AudioMixer : public MediaNode, Phonon::EffectInterface, Phonon::VolumeFaderInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::EffectInterface Phonon::VolumeFaderInterface) + + public: + AudioMixer(QObject *parent = 0); + ~AudioMixer(); + AudioMixerAudioNode *m_audioNode; + Phonon::VolumeFaderEffect::FadeCurve m_fadeCurve; + + int m_fadeTimer; + int m_fadeDuration; + float m_fadeToVolume; + float m_fadeFromVolume; + QTime m_fadeStartTime; + + // EffectInterface: + QList<Phonon::EffectParameter> parameters() const; + QVariant parameterValue(const Phonon::EffectParameter ¶meter) const; + void setParameterValue(const Phonon::EffectParameter ¶meter, const QVariant &newValue); + + // VolumeFaderInterface: + float volume() const; + void setVolume(float volume); + Phonon::VolumeFaderEffect::FadeCurve fadeCurve() const; + void setFadeCurve(Phonon::VolumeFaderEffect::FadeCurve fadeCurve); + void fadeTo(float volume, int fadeTime); + void updateFade(); + + protected: + bool event(QEvent *event); + }; + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_AUDIOMIXER_H diff --git a/src/3rdparty/phonon/qt7/audiomixer.mm b/src/3rdparty/phonon/qt7/audiomixer.mm new file mode 100644 index 0000000..30b1e6f --- /dev/null +++ b/src/3rdparty/phonon/qt7/audiomixer.mm @@ -0,0 +1,181 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "audiomixer.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +AudioMixerAudioNode::AudioMixerAudioNode() : AudioNode(30, 1) +{ + m_numberOfBusses = 2; + m_volume = 1.0f; +} + +ComponentDescription AudioMixerAudioNode::getAudioNodeDescription() const +{ + ComponentDescription description; + description.componentType = kAudioUnitType_Mixer; + description.componentSubType = kAudioUnitSubType_StereoMixer; + description.componentManufacturer = kAudioUnitManufacturer_Apple; + description.componentFlags = 0; + description.componentFlagsMask = 0; + return description; +} + +void AudioMixerAudioNode::initializeAudioUnit() +{ + // Set bus count: + OSStatus err = AudioUnitSetProperty(m_audioUnit, + kAudioUnitProperty_BusCount, kAudioUnitScope_Input, 0, &m_numberOfBusses, sizeof(int)); + BACKEND_ASSERT2(err == noErr, "Could not set number of busses on audio mixer node.", FATAL_ERROR) +} + +void AudioMixerAudioNode::setVolume(float volume, int bus) +{ + if (volume < 0) + m_volume = 0; + else if (volume > 1) + m_volume = 1; + else + m_volume = volume; + + if (m_audioUnit){ +// Float32 db = Float32(volume);//Float32(20.0 * log10(volume)); // convert to db + Float32 db = Float32(volume); + OSStatus err = AudioUnitSetParameter(m_audioUnit, kStereoMixerParam_Volume, kAudioUnitScope_Input, bus, db, 0); + BACKEND_ASSERT2(err == noErr, "Could not set volume on audio mixer node.", NORMAL_ERROR) + } +} + +float AudioMixerAudioNode::volume(int bus) +{ + if (!m_audioUnit) + return 0; + + Float32 db; + OSStatus err = AudioUnitGetParameter(m_audioUnit, kStereoMixerParam_Volume, kAudioUnitScope_Input, bus, &db); + BACKEND_ASSERT3(err == noErr, "Could not get volume on audio mixer node.", NORMAL_ERROR, 0) + return float(db); +} + +/////////////////////////////////////////////////////////////////////// + +AudioMixer::AudioMixer(QObject *parent) : MediaNode(AudioSink | AudioSource, 0, parent) +{ + m_audioNode = new AudioMixerAudioNode(); + setAudioNode(m_audioNode); + m_fadeCurve = Phonon::VolumeFaderEffect::Fade3Decibel; + m_fadeTimer = 0; + m_fadeDuration = 0; + m_fadeFromVolume = 0; + m_fadeToVolume = 0; +} + +AudioMixer::~AudioMixer() +{ + if (m_fadeTimer) + killTimer(m_fadeTimer); +} + +QList<Phonon::EffectParameter> AudioMixer::parameters() const +{ + QList<Phonon::EffectParameter> ret; + return ret; +} + +QVariant AudioMixer::parameterValue(const Phonon::EffectParameter &value) const +{ + NOT_IMPLEMENTED; + Q_UNUSED(value); + return QVariant(); +} + +void AudioMixer::setParameterValue(const Phonon::EffectParameter ¶meter, const QVariant &newValue) +{ + NOT_IMPLEMENTED; + Q_UNUSED(parameter); + Q_UNUSED(newValue); +} + +float AudioMixer::volume() const +{ + return m_audioNode->volume(0); +} + +void AudioMixer::setVolume(float volume) +{ + m_audioNode->setVolume(volume, 0); +} + +Phonon::VolumeFaderEffect::FadeCurve AudioMixer::fadeCurve() const +{ + return m_fadeCurve; +} + +void AudioMixer::setFadeCurve(Phonon::VolumeFaderEffect::FadeCurve fadeCurve) +{ + m_fadeCurve = fadeCurve; +} + +void AudioMixer::fadeTo(float volume, int fadeTime) +{ + m_fadeToVolume = volume; + m_fadeDuration = fadeTime; + m_fadeFromVolume = m_audioNode->volume(0); + + m_fadeStartTime.start(); + if (m_fadeTimer) + killTimer(m_fadeTimer); + m_fadeTimer = startTimer(100); +} + +void AudioMixer::updateFade() +{ + float step = float(m_fadeStartTime.elapsed()) / float(m_fadeDuration); + if (step > 1){ + step = 1; + if (m_fadeTimer) + killTimer(m_fadeTimer); + } + float volume = m_fadeFromVolume + ((m_fadeToVolume - m_fadeFromVolume) * step); + m_audioNode->setVolume(volume, 0); +} + +bool AudioMixer::event(QEvent *event) +{ + switch (event->type()){ + case QEvent::Timer:{ + QTimerEvent *timerEvent = static_cast<QTimerEvent *>(event); + if (timerEvent->timerId() == m_fadeTimer) + updateFade(); + break; } + default: + break; + } + return MediaNode::event(event); +} + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE + +#include "moc_audiomixer.cpp" diff --git a/src/3rdparty/phonon/qt7/audionode.h b/src/3rdparty/phonon/qt7/audionode.h new file mode 100644 index 0000000..2498e49 --- /dev/null +++ b/src/3rdparty/phonon/qt7/audionode.h @@ -0,0 +1,86 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_AudioNode_H +#define Phonon_QT7_AudioNode_H + +#include <QtCore/QObject> +#include "backendheader.h" +#include "audioconnection.h" +#include <AudioToolbox/AudioToolbox.h> +#include <AudioUnit/AudioUnit.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class AudioGraph; + class MediaNodeEvent; + class MediaNode; + + class MediaNodeConnection{ + MediaNode *source; + MediaNode *sink; + int inputPort; + int outputPort; + }; + + class AudioNode + { + public: + enum ConnectionSide {Source, Sink}; + + AudioNode(int maxInput, int maxOutput); + virtual ~AudioNode(); + + virtual void createAndConnectAUNodes(); + virtual void createAudioUnits(); + virtual void setGraph(AudioGraph *audioGraph); + virtual AUNode getInputAUNode(); + virtual AUNode getOutputAUNode(); + virtual bool fillInStreamSpecification(AudioConnection *connection, ConnectionSide side); + virtual bool setStreamSpecification(AudioConnection *connection, ConnectionSide side); + void notify(const MediaNodeEvent *event); + + virtual void mediaNodeEvent(const MediaNodeEvent *event); + Float64 getTimeInSamples(int timeProperty); + + AudioGraph *m_audioGraph; + AudioConnection *m_lastConnectionIn; + + int m_maxInputBusses; + int m_maxOutputBusses; + + protected: + AUNode m_auNode; + AudioUnit m_audioUnit; + + // Only the following methods needs to + // be overidden by only_one-audio-unit nodes: + virtual ComponentDescription getAudioNodeDescription() const; + virtual void initializeAudioUnit(); + + private: + bool setStreamHelp(AudioConnection *c, int bus, OSType scope, bool fromSource); + }; +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_AudioNode_H diff --git a/src/3rdparty/phonon/qt7/audionode.mm b/src/3rdparty/phonon/qt7/audionode.mm new file mode 100644 index 0000000..cb9e82f --- /dev/null +++ b/src/3rdparty/phonon/qt7/audionode.mm @@ -0,0 +1,240 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "audionode.h" +#include "audiograph.h" +#include "audioconnection.h" +#include "medianode.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +AudioNode::AudioNode(int maxInputBusses, int maxOutputBusses) +{ + m_auNode = 0; + m_audioUnit = 0; + m_audioGraph = 0; + m_maxInputBusses = maxInputBusses; + m_maxOutputBusses = maxOutputBusses; + m_lastConnectionIn = 0; +} + +AudioNode::~AudioNode() +{ + setGraph(0); +} + +void AudioNode::setGraph(AudioGraph *audioGraph) +{ + if (m_audioGraph == audioGraph) + return; + + DEBUG_AUDIO_GRAPH("AudioNode" << int(this) << "is setting graph:" << int(audioGraph)) + if (m_auNode){ + AUGraphRemoveNode(m_audioGraph->audioGraphRef(), m_auNode); + m_auNode = 0; + } + + m_audioUnit = 0; + m_lastConnectionIn = 0; + m_audioGraph = audioGraph; +} + +void AudioNode::createAndConnectAUNodes() +{ + if (m_auNode) + return; + + ComponentDescription description = getAudioNodeDescription(); + DEBUG_AUDIO_GRAPH("AudioNode" << int(this) << "creates AUNode" + << QString(!FindNextComponent(0, &description) ? "ERROR: COMPONENT NOT FOUND!" : "OK!")) + + OSStatus err = noErr; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) + err = AUGraphAddNode(m_audioGraph->audioGraphRef(), &description, &m_auNode); + else +#endif + err = AUGraphNewNode(m_audioGraph->audioGraphRef(), &description, 0, 0, &m_auNode); + + BACKEND_ASSERT2(err != kAUGraphErr_OutputNodeErr, "A MediaObject can only be connected to one audio output device.", FATAL_ERROR) + BACKEND_ASSERT2(err == noErr, "Could not create new AUNode.", FATAL_ERROR) +} + +AUNode AudioNode::getInputAUNode() +{ + return m_auNode; +} + +AUNode AudioNode::getOutputAUNode() +{ + return m_auNode; +} + +void AudioNode::createAudioUnits() +{ + if (m_audioUnit) + return; + + DEBUG_AUDIO_GRAPH("AudioNode" << int(this) << "creates AudioUnit") + OSStatus err = AUGraphGetNodeInfo(m_audioGraph->audioGraphRef(), m_auNode, 0, 0, 0, &m_audioUnit); + BACKEND_ASSERT2(err == noErr, "Could not get audio unit from audio node.", FATAL_ERROR) + initializeAudioUnit(); +} + +ComponentDescription AudioNode::getAudioNodeDescription() const +{ + // Override if needed. + ComponentDescription cd; + Q_UNUSED(cd); + return cd; +} + +bool AudioNode::setStreamHelp(AudioConnection *c, int bus, OSType scope, bool fromSource) +{ + if (fromSource){ + OSStatus err = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_StreamFormat, scope, + bus, &c->m_sourceStreamDescription, sizeof(AudioStreamBasicDescription)); + if (err != noErr){ + DEBUG_AUDIO_STREAM("AudioNode" << int(this) << " - failed setting stream format") + return false; + } + AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_AudioChannelLayout, scope, + bus, c->m_sourceChannelLayout, c->m_sourceChannelLayoutSize); + } else { + OSStatus err = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_StreamFormat, scope, + bus, &c->m_sinkStreamDescription, sizeof(AudioStreamBasicDescription)); + if (err != noErr){ + DEBUG_AUDIO_STREAM("AudioNode" << int(this) << " - failed setting stream format") + return false; + } + AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_AudioChannelLayout, scope, + bus, c->m_sinkChannelLayout, c->m_sourceChannelLayoutSize); + } + return true; +} + +bool AudioNode::setStreamSpecification(AudioConnection *connection, ConnectionSide side) +{ + if (side == Source){ + // This object am source of connection: + if (connection->m_hasSourceSpecification){ + DEBUG_AUDIO_STREAM("AudioNode" << int(this) << "sets stream specification out" + << connection->m_sourceOutputBus << "from connection source") + return setStreamHelp(connection, connection->m_sourceOutputBus, kAudioUnitScope_Output, true); + } else { + DEBUG_AUDIO_STREAM("AudioNode" << int(this) << "did not set stream specification out") + } + } else { + if (connection->m_hasSinkSpecification){ + DEBUG_AUDIO_STREAM("AudioNode" << int(this) << "sets stream specification" + << connection->m_sinkInputBus << "from connection sink") + return setStreamHelp(connection, connection->m_sinkInputBus, kAudioUnitScope_Input, false); + } else if (connection->m_hasSourceSpecification){ + DEBUG_AUDIO_STREAM("AudioNode" << int(this) << "sets stream specification" + << connection->m_sinkInputBus << "from connection source") + return setStreamHelp(connection, connection->m_sinkInputBus, kAudioUnitScope_Input, true); + } else { + DEBUG_AUDIO_STREAM("AudioNode" << int(this) << "did not set stream specification in") + } + } + return true; +} + +bool AudioNode::fillInStreamSpecification(AudioConnection *connection, ConnectionSide side) +{ + if (side == Source){ + // As default, use the last description to describe the source: + if (m_lastConnectionIn->m_hasSinkSpecification){ + DEBUG_AUDIO_STREAM("AudioNode" << int(this) << "is source, and fills in stream spec using last connection sink.") + connection->m_sourceStreamDescription = m_lastConnectionIn->m_sinkStreamDescription; + connection->m_sourceChannelLayout = (AudioChannelLayout *) malloc(m_lastConnectionIn->m_sinkChannelLayoutSize); + memcpy(connection->m_sourceChannelLayout, m_lastConnectionIn->m_sinkChannelLayout, m_lastConnectionIn->m_sinkChannelLayoutSize); + connection->m_sourceChannelLayoutSize = m_lastConnectionIn->m_sinkChannelLayoutSize; + connection->m_hasSourceSpecification = true; + } else if (m_lastConnectionIn->m_hasSourceSpecification){ + DEBUG_AUDIO_STREAM("AudioNode" << int(this) << "is source, and fills in stream spec using last connection source.") + connection->m_sourceStreamDescription = m_lastConnectionIn->m_sourceStreamDescription; + connection->m_sourceChannelLayout = (AudioChannelLayout *) malloc(m_lastConnectionIn->m_sourceChannelLayoutSize); + memcpy(connection->m_sourceChannelLayout, m_lastConnectionIn->m_sourceChannelLayout, m_lastConnectionIn->m_sourceChannelLayoutSize); + connection->m_sourceChannelLayoutSize = m_lastConnectionIn->m_sourceChannelLayoutSize; + connection->m_hasSourceSpecification = true; + } else { + DEBUG_AUDIO_STREAM("AudioNode" << int(this) << " __WARNING__: could not get stream specification...") + } + } else { + DEBUG_AUDIO_STREAM("AudioNode" << int(this) << "is sink, skips filling in stream.") + if (!connection->isSinkOnly()) + m_lastConnectionIn = connection; + } + return true; +} + +/** + Let timeProperty be one of e.g + {kAudioUnitProperty_Latency, kAudioUnitProperty_TailTime, + kAudioOutputUnitProperty_StartTime, kAudioUnitProperty_CurrentPlayTime} +*/ +Float64 AudioNode::getTimeInSamples(int timeProperty) +{ + if (!m_audioUnit) + return 0; + + AudioTimeStamp timeStamp; + UInt32 size = sizeof(timeStamp); + memset(&timeStamp, 0, sizeof(timeStamp)); + OSStatus err = AudioUnitGetProperty(m_audioUnit, + timeProperty, kAudioUnitScope_Global, + 0, &timeStamp, &size); + if (err != noErr) + return 0; + return timeStamp.mSampleTime; +} + +void AudioNode::notify(const MediaNodeEvent *event) +{ + switch(event->type()){ + case MediaNodeEvent::AudioGraphAboutToBeDeleted: + setGraph(0); + break; + case MediaNodeEvent::NewAudioGraph: + setGraph(static_cast<AudioGraph *>(event->data())); + break; + default: + break; + } + + mediaNodeEvent(event); +} + +void AudioNode::mediaNodeEvent(const MediaNodeEvent */*event*/) +{ + // Override if needed +} + +void AudioNode::initializeAudioUnit() +{ + // Override if needed. +} + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/audiooutput.h b/src/3rdparty/phonon/qt7/audiooutput.h new file mode 100644 index 0000000..c4a0526 --- /dev/null +++ b/src/3rdparty/phonon/qt7/audiooutput.h @@ -0,0 +1,88 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_AUDIOOUTPUT_H +#define Phonon_QT7_AUDIOOUTPUT_H + +#include <QtCore/QObject> +#include <phonon/audiooutputinterface.h> +#include <phonon/abstractaudiooutput.h> + +#include "medianode.h" +#include "audionode.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class AudioOutputAudioPart : public QObject, AudioNode + { + Q_OBJECT + public: + AudioOutputAudioPart(); + + void setVolume(float volume); + float volume(); + + protected: + ComponentDescription getAudioNodeDescription() const; + void initializeAudioUnit(); + + signals: + void volumeChanged(qreal newVolume); + void audioDeviceFailed(); + + private: + friend class AudioOutput; + qreal m_volume; + AudioDeviceID m_audioDevice; + void setAudioDevice(AudioDeviceID device); + }; + + class AudioOutput : public MediaNode, public AudioOutputInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::AudioOutputInterface) + + public: + AudioOutput(QObject *parent = 0); + ~AudioOutput(); + + qreal volume() const; + void setVolume(qreal); + int outputDevice() const; + bool setOutputDevice(int); + + signals: + void volumeChanged(qreal newVolume); + void audioDeviceFailed(); + + protected: + void mediaNodeEvent(const MediaNodeEvent *event); + + private: + AudioOutputAudioPart *m_audioOutput; + int m_device; + bool m_redirectToMovie; + }; + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE +#endif // Phonon_QT7_AUDIOOUTPUT_H diff --git a/src/3rdparty/phonon/qt7/audiooutput.mm b/src/3rdparty/phonon/qt7/audiooutput.mm new file mode 100644 index 0000000..38066d5 --- /dev/null +++ b/src/3rdparty/phonon/qt7/audiooutput.mm @@ -0,0 +1,168 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "audiooutput.h" +#include "audiograph.h" +#include "audiodevice.h" +#include "mediaobject.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +AudioOutputAudioPart::AudioOutputAudioPart() : AudioNode(1, 0) +{ + m_audioDevice = AudioDevice::defaultDevice(AudioDevice::Out); + m_volume = 1; +} + +ComponentDescription AudioOutputAudioPart::getAudioNodeDescription() const +{ + ComponentDescription description; + description.componentType = kAudioUnitType_Output; + description.componentSubType = kAudioUnitSubType_DefaultOutput; + description.componentManufacturer = kAudioUnitManufacturer_Apple; + description.componentFlags = 0; + description.componentFlagsMask = 0; + return description; +} + +void AudioOutputAudioPart::initializeAudioUnit() +{ + setAudioDevice(m_audioDevice); + setVolume(m_volume); +} + +void AudioOutputAudioPart::setAudioDevice(AudioDeviceID device) +{ + m_audioDevice = device; + if (!m_audioDevice) + return; + if (!m_audioUnit) + return; + bool ok = AudioDevice::setDevice(m_audioUnit, m_audioDevice, AudioDevice::Out); + if (!ok) + emit audioDeviceFailed(); +} + +void AudioOutputAudioPart::setVolume(float volume) +{ + if (volume < 0) + m_volume = 0; + if (volume > 1) + m_volume = 1; + else + m_volume = volume; + + if (m_audioUnit){ + float db = volume;//20.0 * log10(volume); // convert to db + OSStatus err = AudioUnitSetParameter(m_audioUnit, kHALOutputParam_Volume, kAudioUnitScope_Input, 0, db, 0); + BACKEND_ASSERT2(err == noErr, "Could not set volume on output audio unit.", FATAL_ERROR) + emit volumeChanged(qreal(db)); + } +} + +float AudioOutputAudioPart::volume() +{ + return m_volume; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +AudioOutput::AudioOutput(QObject *parent) : MediaNode(AudioSink, parent) +{ + m_audioOutput = new AudioOutputAudioPart(); + setAudioNode(m_audioOutput); + connect(m_audioOutput, SIGNAL(volumeChanged(qreal)), this, SIGNAL(volumeChanged(qreal))); + connect(m_audioOutput, SIGNAL(audioDeviceFailed()), this, SIGNAL(audioDeviceFailed())); + m_redirectToMovie = false; +} + +AudioOutput::~AudioOutput() +{ +} + +void AudioOutput::setVolume(qreal volume) +{ + IMPLEMENTED; + m_audioOutput->setVolume(float(volume)); + if (m_owningMediaObject) + m_owningMediaObject->setVolumeOnMovie(volume); + + emit volumeChanged(m_audioOutput->volume()); +} + +qreal AudioOutput::volume() const +{ + IMPLEMENTED; + return qreal(m_audioOutput->volume()); +} + +bool AudioOutput::setOutputDevice(int device) +{ + IMPLEMENTED; + if (device == -1) + return false; + + if (m_owningMediaObject){ + bool ok = m_owningMediaObject->setAudioDeviceOnMovie(device); + if (!ok) + return false; + } + + if (m_audioGraph){ + MediaNodeEvent event1(MediaNodeEvent::AboutToRestartAudioStream, this); + m_audioGraph->notify(&event1); + } + + m_audioOutput->setAudioDevice(device); + + if (m_audioGraph){ + MediaNodeEvent event2(MediaNodeEvent::RestartAudioStreamRequest, this); + m_audioGraph->notify(&event2); + } + return true; +} + +int AudioOutput::outputDevice() const +{ + IMPLEMENTED; + return m_audioOutput->m_audioDevice; +} + +void AudioOutput::mediaNodeEvent(const MediaNodeEvent *event) +{ + switch (event->type()){ + case MediaNodeEvent::SetMediaObject: + if (static_cast<MediaObject *>(event->data())){ + setVolume(volume()); + setOutputDevice(outputDevice()); + } + break; + default: + break; + } +} + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE + +#include "moc_audiooutput.cpp" diff --git a/src/3rdparty/phonon/qt7/audiopartoutput.h b/src/3rdparty/phonon/qt7/audiopartoutput.h new file mode 100644 index 0000000..0ccdfb6 --- /dev/null +++ b/src/3rdparty/phonon/qt7/audiopartoutput.h @@ -0,0 +1,47 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_AUDIOPARTOUTPUT_H +#define Phonon_QT7_AUDIOPARTOUTPUT_H + +#include <AudioToolbox/AudioToolbox.h> +#include <AudioUnit/AudioUnit.h> +#include "audionode.h" +#include <QtCore/qnamespace.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class AudioPartOutput : public AudioNode + { + public: + AudioPartOutput(); + virtual ~AudioPartOutput(); + + private: + ComponentDescription getAudioNodeDescription() const; + void initializeAudioUnit(AudioNode *source); + }; + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_AUDIOPARTOUTPUT_H diff --git a/src/3rdparty/phonon/qt7/audiopartoutput.mm b/src/3rdparty/phonon/qt7/audiopartoutput.mm new file mode 100644 index 0000000..b985e69 --- /dev/null +++ b/src/3rdparty/phonon/qt7/audiopartoutput.mm @@ -0,0 +1,69 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "audiopartoutput.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +AudioPartOutput::AudioPartOutput() + : AudioNode() +{ +} + +AudioPartOutput::~AudioPartOutput() +{ +} + +ComponentDescription AudioPartOutput::getAudioNodeDescription() const +{ + ComponentDescription description; + description.componentType = kAudioUnitType_Output; + description.componentSubType = kAudioUnitSubType_DefaultOutput; + description.componentManufacturer = kAudioUnitManufacturer_Apple; + description.componentFlags = 0; + description.componentFlagsMask = 0; + return description; +} + +void AudioPartOutput::initializeAudioUnit(AudioNode *source) +{ + m_audioStreamDescription = source->outputStreamDescription(); + m_audioChannelLayout = source->outputChannelLayout(); + m_audioChannelLayoutSize = source->outputChannelLayoutSize(); + + // Specify the stream format: + OSStatus err; + err = AudioUnitSetProperty(m_audioUnit, + kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, + 0, m_audioStreamDescription, sizeof(AudioStreamBasicDescription)); + BACKEND_ASSERT2(err == noErr, "Could not set stream format on audio output unit.", FATAL_ERROR) + + // Set the channel layout: + err = AudioUnitSetProperty(m_audioUnit, + kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Input, + 0, m_audioChannelLayout, m_audioChannelLayoutSize); + BACKEND_ASSERT2(err == noErr, "Could not set channel layout on audio output unit.", FATAL_ERROR) +} + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/audiosplitter.h b/src/3rdparty/phonon/qt7/audiosplitter.h new file mode 100644 index 0000000..ed6101f --- /dev/null +++ b/src/3rdparty/phonon/qt7/audiosplitter.h @@ -0,0 +1,50 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_AUDIOSPLITTER_H +#define Phonon_QT7_AUDIOSPLITTER_H + +#include <QtCore/QFile> + +#include "medianode.h" +#include "audionode.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class AudioNodeSplitter : public AudioNode + { + public: + AudioNodeSplitter(); + ComponentDescription getAudioNodeDescription() const; + }; + + class AudioSplitter : public MediaNode + { + public: + AudioSplitter(QObject *parent = 0); + ~AudioSplitter(); + }; + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_AUDIOSPLITTER_H diff --git a/src/3rdparty/phonon/qt7/audiosplitter.mm b/src/3rdparty/phonon/qt7/audiosplitter.mm new file mode 100644 index 0000000..685ea12 --- /dev/null +++ b/src/3rdparty/phonon/qt7/audiosplitter.mm @@ -0,0 +1,52 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "audiosplitter.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +AudioNodeSplitter::AudioNodeSplitter() : AudioNode(1, 2) +{ +} + +ComponentDescription AudioNodeSplitter::getAudioNodeDescription() const +{ + ComponentDescription description; + description.componentType = kAudioUnitType_FormatConverter; + description.componentSubType = kAudioUnitSubType_Splitter; + description.componentManufacturer = kAudioUnitManufacturer_Apple; + description.componentFlags = 0; + description.componentFlagsMask = 0; + return description; +} + +AudioSplitter::AudioSplitter(QObject *parent) : MediaNode(AudioSink | AudioSource, new AudioNodeSplitter(), parent) +{ +} + +AudioSplitter::~AudioSplitter() +{ +} + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/backend.h b/src/3rdparty/phonon/qt7/backend.h new file mode 100644 index 0000000..287fcec --- /dev/null +++ b/src/3rdparty/phonon/qt7/backend.h @@ -0,0 +1,61 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_BACKEND_H +#define Phonon_QT7_BACKEND_H + +#include <QtCore/QList> +#include <QtCore/QPointer> +#include <QtCore/QStringList> +#include <phonon/backendinterface.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class Backend : public QObject, public BackendInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::BackendInterface) + + public: + Backend(); + Backend(QObject *parent, const QStringList &args); + virtual ~Backend(); + + QObject *createObject(BackendInterface::Class, QObject *parent, const QList<QVariant> &args); + QStringList availableMimeTypes() const; + QList<int> objectDescriptionIndexes(ObjectDescriptionType type) const; + QHash<QByteArray, QVariant> objectDescriptionProperties(ObjectDescriptionType type, int index) const; + + bool startConnectionChange(QSet<QObject *> nodes); + bool connectNodes(QObject *source, QObject *sink); + bool disconnectNodes(QObject *source, QObject *sink); + bool endConnectionChange(QSet<QObject *> nodes); + + Q_SIGNALS: + void objectDescriptionChanged(ObjectDescriptionType); + + private: + bool quickTime7Available(); + }; +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE +#endif // Phonon_QT7_BACKEND_H diff --git a/src/3rdparty/phonon/qt7/backend.mm b/src/3rdparty/phonon/qt7/backend.mm new file mode 100644 index 0000000..327ddd7 --- /dev/null +++ b/src/3rdparty/phonon/qt7/backend.mm @@ -0,0 +1,276 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "backend.h" +#include <QtCore/QDebug> +#include <QtCore/QSet> +#include <QtCore/QVariant> +#include <QtCore/QtPlugin> + +#include "backendheader.h" + +#include "videowidget.h" +#include "audiooutput.h" +#include "mediaobject.h" +#include "videoeffect.h" +#include "medianode.h" +#include "audiodevice.h" +#include "audiomixer.h" +#include "backendinfo.h" +#include "quicktimeaudioplayer.h" + +#include "audiograph.h" +#include "audiomixer.h" +#include "audiooutput.h" +#include "audiosplitter.h" +#include "audioeffects.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +Backend::Backend() +{ + IMPLEMENTED << "Creating backend QT7"; +} + +Backend::Backend(QObject *parent, const QStringList &) : QObject(parent) +{ + IMPLEMENTED << "Creating backend QT7"; + setProperty("identifier", QLatin1String("Mac OS X/QuickTime7")); + setProperty("backendName", QLatin1String("Mac OS X/QuickTime7")); + setProperty("backendComment", QLatin1String("Developed by Trolltech")); + setProperty("backendVersion", QLatin1String("0.1")); + setProperty("backendIcon", QLatin1String("")); + setProperty("backendWebsite", QLatin1String("http://qtsoftware.com/")); +} + +Backend::~Backend() +{ +} + +bool Backend::quickTime7Available() +{ + static bool ok = BackendInfo::isQuickTimeVersionAvailable(0x0700); + if (!ok){ + static bool messageWritten = false; + if (!messageWritten && qgetenv("PHONON_DEBUG") == "1"){ + messageWritten = true; + QString str("WARNING: Phonon backend plugin need QuickTime 7 or newer to work."); + str += " This computer has version " + + BackendInfo::quickTimeVersionString() + + " installed."; + qWarning(str.toAscii().data()); + } + return false; + } + return true; +} + +QObject *Backend::createObject(BackendInterface::Class c, QObject *parent, const QList<QVariant> &args) +{ + if (!quickTime7Available()) + return 0; + + switch (c) { + case MediaObjectClass: + IMPLEMENTED << "Creating new MediaObjectClass."; + return new MediaObject(parent); + break; + case VolumeFaderEffectClass: + IMPLEMENTED << "Creating new VolumeFaderEffectClass."; + return new AudioMixer(parent); + break; + case AudioOutputClass: + IMPLEMENTED << "Creating new AudioOutputClass."; + return new AudioOutput(parent); + break; + case AudioDataOutputClass: + NOT_IMPLEMENTED << "Creating new AudioDataOutputClass."; + break; + case VisualizationClass: + NOT_IMPLEMENTED << "Creating new VisualizationClass."; + break; + case VideoDataOutputClass: + NOT_IMPLEMENTED << "Creating new VideoDataOutputClass."; + break; + case EffectClass: + IMPLEMENTED << "Creating new EffectClass."; + return new AudioEffect(args[0].toInt()); + break; + case VideoWidgetClass: + IMPLEMENTED << "Creating new VideoWidget."; + return new VideoWidget(parent); + break; + default: + return 0; + } + return 0; +} + +bool Backend::startConnectionChange(QSet<QObject *> objects) +{ + IMPLEMENTED; + QList<AudioGraph *> notifiedGraphs; + for (int i=0; i<objects.size(); i++){ + MediaNode *node = qobject_cast<MediaNode*>(objects.values()[i]); + if (node && node->m_audioGraph && !notifiedGraphs.contains(node->m_audioGraph)){ + MediaNodeEvent event(MediaNodeEvent::StartConnectionChange); + node->m_audioGraph->notify(&event); + notifiedGraphs << node->m_audioGraph; + } + } + return true; +} + +bool Backend::endConnectionChange(QSet<QObject *> objects) +{ + IMPLEMENTED; + QList<AudioGraph *> notifiedGraphs; + for (int i=0; i<objects.size(); i++){ + MediaNode *node = qobject_cast<MediaNode*>(objects.values()[i]); + if (node && node->m_audioGraph && !notifiedGraphs.contains(node->m_audioGraph)){ + MediaNodeEvent event(MediaNodeEvent::EndConnectionChange); + node->m_audioGraph->notify(&event); + notifiedGraphs << node->m_audioGraph; + } + } + return true; +} + +bool Backend::connectNodes(QObject *aSource, QObject *aSink) +{ + IMPLEMENTED; + MediaNode *source = qobject_cast<MediaNode*>(aSource); + if (!source) return false; + MediaNode *sink = qobject_cast<MediaNode*>(aSink); + if (!sink) return false; + + return source->connectToSink(sink); +} + + +bool Backend::disconnectNodes(QObject *aSource, QObject *aSink) +{ + IMPLEMENTED; + MediaNode *source = qobject_cast<MediaNode*>(aSource); + if (!source) return false; + MediaNode *sink = qobject_cast<MediaNode*>(aSink); + if (!sink) return false; + + return source->disconnectToSink(sink); +} + + +QStringList Backend::availableMimeTypes() const +{ + IMPLEMENTED; + return BackendInfo::quickTimeMimeTypes(BackendInfo::In); +} + +/** +* Returns a set of indexes that acts as identifiers for the various properties +* this backend supports for the given ObjectDescriptionType. +* More information for a given property/index can be +* looked up in Backend::objectDescriptionProperties(...). +*/ +QList<int> Backend::objectDescriptionIndexes(ObjectDescriptionType type) const +{ + QList<int> ret; + + switch (type){ + case AudioOutputDeviceType:{ + IMPLEMENTED_SILENT << "Creating index set for type: AudioOutputDeviceType"; + QList<AudioDeviceID> devices = AudioDevice::devices(AudioDevice::Out); + for (int i=0; i<devices.size(); i++) + ret << int(devices[i]); + break; } + case EffectType:{ + IMPLEMENTED_SILENT << "Creating index set for type: EffectType"; + if (QuickTimeAudioPlayer::soundPlayerIsAwailable()) + ret = AudioEffect::effectList(); + break; } + +#if 0 // will be awailable in a later version of phonon. + case AudioCaptureDeviceType:{ + IMPLEMENTED_SILENT << "Creating index set for type: AudioCaptureDeviceType"; + QList<AudioDeviceID> devices = AudioDevice::devices(AudioDevice::In).keys(); + for (int i=0; i<devices.size(); i++) + ret <<int(devices[i]); + break; } + case VideoEffectType:{ + // Just count the number of filters awailable (c), and + // add return a set with the numbers 1..c inserted: + IMPLEMENTED_SILENT << "Creating index set for type: VideoEffectType"; + QList<QtCore/QString> filters = objc_getCiFilterInfo()->filterDisplayNames; + for (int i=0; i<filters.size(); i++) + ret << insert(i); + break; } +#endif + default: + NOT_IMPLEMENTED; + break; + } + return ret; +} + +QHash<QByteArray, QVariant> Backend::objectDescriptionProperties(ObjectDescriptionType type, int index) const +{ + QHash<QByteArray, QVariant> ret; + + switch (type){ + case AudioOutputDeviceType:{ + IMPLEMENTED_SILENT << "Creating description hash for type: AudioOutputDeviceType"; + ret.insert("name", AudioDevice::deviceSourceNameElseDeviceName(index)); + ret.insert("description", AudioDevice::deviceNameElseDeviceSourceName(index)); + break; } + case EffectType:{ + AudioEffect e(index); + ret.insert("name", e.name()); + ret.insert("description", e.description()); + break; } + +#if 0 // will be awailable in a later version of phonon. + case VideoEffectType:{ + // Get list of effects, pick out filter at index, and return its name: + IMPLEMENTED_SILENT << "Creating description hash for type: VideoEffectType"; + QList<QtCore/QString> filters = objc_getCiFilterInfo()->filterDisplayNames; + ret.insert("name", filters[index]); + case AudioCaptureDeviceType:{ + IMPLEMENTED_SILENT << "Creating description hash for type: AudioCaptureDeviceType"; + QMap<AudioDeviceID, QString> devices = AudioDevice::devices(AudioDevice::In); + ret.insert("name", devices.value(index)); + break; } +#endif + default: + NOT_IMPLEMENTED; + break; + } + + return ret; +} + +Q_EXPORT_PLUGIN2(phonon_qt7, Backend) +}} + +QT_END_NAMESPACE + +#include "moc_backend.cpp" + diff --git a/src/3rdparty/phonon/qt7/backendheader.h b/src/3rdparty/phonon/qt7/backendheader.h new file mode 100644 index 0000000..fd0d892 --- /dev/null +++ b/src/3rdparty/phonon/qt7/backendheader.h @@ -0,0 +1,184 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_BACKENDHEADER_H +#define Phonon_QT7_BACKENDHEADER_H + +#include <QtCore/QString> +#import <Foundation/NSAutoreleasePool.h> +#include <CoreFoundation/CFBase.h> + +#ifndef Q_WS_MAC64 +#define QUICKTIME_C_API_AVAILABLE +#endif + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +// Implemented in error.cpp: +void gSetErrorString(const QString &errorString); +QString gGetErrorString(); +void gSetErrorLocation(const QString &errorLocation); +void gSetErrorType(int type); +int gGetErrorType(); +void gClearError(); + +#define NO_ERROR 0 +#define NORMAL_ERROR 1 +#define FATAL_ERROR 2 + +#define ERROR_LOCATION \ + QLatin1String("Function: ") + QLatin1String(__FUNCTION__) \ + + QLatin1String(", File: ") + QLatin1String(__FILE__) \ + + QLatin1String(", Line: ") + QString::number(__LINE__) + +#define SET_ERROR(string, type){ \ + Phonon::QT7::gSetErrorString(string); \ + Phonon::QT7::gSetErrorType(type); \ + Phonon::QT7::gSetErrorLocation(ERROR_LOCATION); } + +#define BACKEND_ASSERT(test, string, type) \ + bool fail = !test; \ + if (fail) \ + SET_ERROR(QLatin1String(string), type) \ + if (fail) + +#define BACKEND_ASSERT2(test, string, type) \ + if (!(test)) { \ + SET_ERROR(QLatin1String(string), type) \ + return; \ + } + +#define BACKEND_ASSERT3(test, string, type, ret) \ + if (!(test)) { \ + SET_ERROR(QLatin1String(string), type) \ + return ret; \ + } + +#define ARGUMENT_UNSUPPORTED(a, x, type, ret) \ + if ((a) == (x)) { \ + SET_ERROR("Argument value not supported: "#a" == "#x, type); \ + return ret; \ + } + +#define CASE_UNSUPPORTED(string, type) SET_ERROR(string, type) + +#ifdef SET_DEBUG_IMPLEMENTED +#define IMPLEMENTED qDebug() << "QT7:" << __FUNCTION__ << "(" << __FILE__ << "):" +#else +#define IMPLEMENTED if (1); else qDebug() +#endif + +#ifdef SET_DEBUG_HALF_IMPLEMENTED +#define HALF_IMPLEMENTED qDebug() << "QT7: --- HALF IMPLEMENTED:" << __FUNCTION__ << "(" << __FILE__ << "," << __LINE__ << "):" +#else +#define HALF_IMPLEMENTED if (1); else qDebug() +#endif + +#ifdef SET_DEBUG_NOT_IMPLEMENTED +#define NOT_IMPLEMENTED qDebug() << "QT7: *** NOT IMPLEMENTED:" << __FUNCTION__ << "(" << __FILE__ << "," << __LINE__ << "):" +#else +#define NOT_IMPLEMENTED if (1); else qDebug() +#endif + +#ifdef SET_DEBUG_IMPLEMENTED_SILENT +#define IMPLEMENTED_SILENT qDebug() << "QT7: (silent)" << __FUNCTION__ << "(" << __FILE__ << "," << __LINE__ << "):" +#else +#define IMPLEMENTED_SILENT if (1); else qDebug() +#endif + +#ifdef SET_DEBUG_AUDIO_GRAPH +#define DEBUG_AUDIO_GRAPH(x) qDebug() << "QT7 DEBUG GRAPH:" << x; +#else +#define DEBUG_AUDIO_GRAPH(x) {} +#endif + +#ifdef SET_DEBUG_AUDIO_STREAM +#define DEBUG_AUDIO_STREAM(x) qDebug() << "QT7 DEBUG STREAM:" << x; +#else +#define DEBUG_AUDIO_STREAM(x) {} +#endif + +///////////////////////////////////////////////////////////////////////////////////////// + +class PhononAutoReleasePool +{ +private: + void *pool; +public: + PhononAutoReleasePool(); + ~PhononAutoReleasePool(); +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +template <typename T> +class PhononCFType +{ +public: + inline PhononCFType(const T &t = 0) : type(t) {} + inline PhononCFType(const PhononCFType &helper) : type(helper.type) { if (type) CFRetain(type); } + inline ~PhononCFType() { if (type) CFRelease(type); } + inline operator T() { return type; } + inline PhononCFType operator =(const PhononCFType &helper) + { + if (helper.type) + CFRetain(helper.type); + CFTypeRef type2 = type; + type = helper.type; + if (type2) + CFRelease(type2); + return *this; + } + inline T *operator&() { return &type; } + static PhononCFType constructFromGet(const T &t) + { + CFRetain(t); + return PhononCFType<T>(t); + } +protected: + T type; +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +class PhononCFString : public PhononCFType<CFStringRef> +{ +public: + inline PhononCFString(const QString &str) : PhononCFType<CFStringRef>(0), string(str) {} + inline PhononCFString(const CFStringRef cfstr = 0) : PhononCFType<CFStringRef>(cfstr) {} + inline PhononCFString(const PhononCFType<CFStringRef> &other) : PhononCFType<CFStringRef>(other) {} + operator QString() const; + operator CFStringRef() const; + static QString toQString(CFStringRef cfstr); + static CFStringRef toCFStringRef(const QString &str); +private: + QString string; +}; +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE + +#ifdef Q_CC_INTEL +#pragma warning (disable : 1899) // mute icc warning for the use of 4cc +#endif + +#endif // Phonon_QT7_BACKENDHEADER_H diff --git a/src/3rdparty/phonon/qt7/backendheader.mm b/src/3rdparty/phonon/qt7/backendheader.mm new file mode 100644 index 0000000..3a73389 --- /dev/null +++ b/src/3rdparty/phonon/qt7/backendheader.mm @@ -0,0 +1,127 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "backendheader.h" +#include <QtCore/QString> +#include <QtCore/QDebug> + +#include <CoreFoundation/CoreFoundation.h> +#include <QVarLengthArray> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +Q_GLOBAL_STATIC(QString, gErrorString) +int gErrorType = NO_ERROR; + +void gSetErrorString(const QString &errorString) +{ + if (qgetenv("PHONON_DEBUG") == "1"){ + qDebug() << "Error:" << errorString; + } + + if (!gErrorString()->isEmpty()) + return; // not yet caught. + + *gErrorString() = errorString; +} + +QString gGetErrorString() +{ + return *gErrorString(); +} + +void gSetErrorLocation(const QString &errorLocation) +{ + if (qgetenv("PHONON_DEBUG") == "1"){ + qDebug() << "Location:" << errorLocation; + } +} + +void gSetErrorType(int errorType) +{ + if (gErrorType != NO_ERROR) + return; // not yet caught. + gErrorType = errorType; +} + +int gGetErrorType() +{ + return gErrorType; +} + +void gClearError() +{ + gErrorString()->clear(); + gErrorType = NO_ERROR; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +PhononAutoReleasePool::PhononAutoReleasePool() +{ + pool = (void*)[[NSAutoreleasePool alloc] init]; +} + +PhononAutoReleasePool::~PhononAutoReleasePool() +{ + [(NSAutoreleasePool*)pool release]; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +QString PhononCFString::toQString(CFStringRef str) +{ + if(!str) + return QString(); + CFIndex length = CFStringGetLength(str); + const UniChar *chars = CFStringGetCharactersPtr(str); + if (chars) + return QString(reinterpret_cast<const QChar *>(chars), length); + + QVarLengthArray<UniChar> buffer(length); + CFStringGetCharacters(str, CFRangeMake(0, length), buffer.data()); + return QString(reinterpret_cast<const QChar *>(buffer.constData()), length); +} + +PhononCFString::operator QString() const +{ + if (string.isEmpty() && type) + const_cast<PhononCFString*>(this)->string = toQString(type); + return string; +} + +CFStringRef PhononCFString::toCFStringRef(const QString &string) +{ + return CFStringCreateWithCharacters(0, reinterpret_cast<const UniChar *>(string.unicode()), + string.length()); +} + +PhononCFString::operator CFStringRef() const +{ + if (!type) + const_cast<PhononCFString*>(this)->type = toCFStringRef(string); + return type; +} + +}} + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/backendinfo.h b/src/3rdparty/phonon/qt7/backendinfo.h new file mode 100644 index 0000000..c30cda3 --- /dev/null +++ b/src/3rdparty/phonon/qt7/backendinfo.h @@ -0,0 +1,48 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_BACKENDINFO_H +#define Phonon_QT7_BACKENDINFO_H + +#include <phonon/mediasource.h> +#include <Carbon/Carbon.h> +#include <QtCore/QStringList> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class BackendInfo + { + public: + enum Scope {In, Out}; + + static QString quickTimeVersionString(); + static bool isQuickTimeVersionAvailable(int minHexVersion); + static QStringList quickTimeMimeTypes(Scope scope); + static QStringList quickTimeCompressionFormats(); + static QStringList coreAudioCodecs(Scope scope); + static QStringList coreAudioFileTypes(Scope scope); + }; + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_BACKENDINFO_H diff --git a/src/3rdparty/phonon/qt7/backendinfo.mm b/src/3rdparty/phonon/qt7/backendinfo.mm new file mode 100644 index 0000000..e173f05 --- /dev/null +++ b/src/3rdparty/phonon/qt7/backendinfo.mm @@ -0,0 +1,311 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "backendinfo.h" +#include "backendheader.h" + +#include <AudioToolbox/AudioToolbox.h> +#include <AudioUnit/AudioUnit.h> +#include <CoreServices/CoreServices.h> + +#import <QTKit/QTMovie.h> + +#ifdef QUICKTIME_C_API_AVAILABLE + #include <QuickTime/QuickTime.h> + #undef check // avoid name clash; +#endif + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +QString BackendInfo::quickTimeVersionString() +{ + SInt32 version; + OSStatus err = Gestalt(gestaltQuickTimeVersion, &version); + if (err != noErr) + return QString("00.0.0.0000"); + QString versionString = QString("%1%2.%3.%4.%5%6%7%8") + .arg((version >> (7*4)) & 0xF) + .arg((version >> (6*4)) & 0xF) + .arg((version >> (5*4)) & 0xF) + .arg((version >> (4*4)) & 0xF) + .arg((version >> (3*4)) & 0xF) + .arg((version >> (2*4)) & 0xF) + .arg((version >> (1*4)) & 0xF) + .arg((version >> (0*4)) & 0xF); + return versionString; +} + +bool BackendInfo::isQuickTimeVersionAvailable(int minHexVersion) +{ + // minHexVersion == 0x0741 means version 7.4.1 + SInt32 qtHexVersion; + OSStatus err = Gestalt(gestaltQuickTimeVersion, &qtHexVersion); + return (err == noErr) ? ((qtHexVersion >> 16) >= minHexVersion) : 0; +} + +#ifdef QUICKTIME_C_API_AVAILABLE +static QString getMimeTypeTag(QTAtomContainer mimeList, int index, OSType type) +{ + QTAtom mimeAtom = QTFindChildByIndex(mimeList, kParentAtomIsContainer, type, index, 0); + char mimeCharArray[256]; + long length; + OSStatus err = QTCopyAtomDataToPtr(mimeList, mimeAtom, true, sizeof(mimeCharArray)-1, mimeCharArray, &length); + if (err == noErr) + return QString::fromAscii(mimeCharArray, length); + return QString(); +} +#endif // QUICKTIME_C_API_AVAILABLE + +#ifdef QUICKTIME_C_API_AVAILABLE +QStringList BackendInfo::quickTimeMimeTypes(Scope scope) +{ + QStringList mimeTypes; + ARGUMENT_UNSUPPORTED(scope, Out, NORMAL_ERROR, mimeTypes) + + ComponentDescription description; + description.componentType = MovieImportType; + description.componentSubType = 0; + description.componentManufacturer = 0; + description.componentFlags = hasMovieImportMIMEList | canMovieImportFiles; + description.componentFlagsMask = canMovieImportFiles | movieImportSubTypeIsFileExtension | hasMovieImportMIMEList; + Component component = FindNextComponent(0, &description); + + while (component) { + QTAtomContainer mimeList = 0; + OSStatus err = MovieImportGetMIMETypeList((MovieImportComponent)component, &mimeList); + if (err == noErr){ + int count = QTCountChildrenOfType(mimeList, kParentAtomIsContainer, 0); + for (int i=1; i<=count; ++i){ + QString mimeType = getMimeTypeTag(mimeList, i, kMimeInfoMimeTypeTag); + if (mimeType.startsWith(QLatin1String("audio")) || mimeType.startsWith(QLatin1String("video"))){ + if (err == noErr && !mimeType.isEmpty()) + mimeTypes << mimeType; + } + } + } + QTDisposeAtomContainer(mimeList); + component = FindNextComponent(component, &description); + } + mimeTypes.sort(); + return mimeTypes; +} + +#else // QUICKTIME_C_API_AVAILABLE == false + +QString mimeForExtensionAudio(const QString &ext) +{ + if (ext == "3g2") return QLatin1String("audio/3g2"); + if (ext == "3gp") return QLatin1String("audio/3gp"); + if (ext == "aac") return QLatin1String("audio/aac"); + if (ext == "ac3") return QLatin1String("audio/ac3"); + if (ext == "aif") return QLatin1String("audio/aif"); + if (ext == "aifc") return QLatin1String("audio/aifc"); + if (ext == "aiff") return QLatin1String("audio/aiff"); + if (ext == "amr") return QLatin1String("audio/amr"); + if (ext == "au") return QLatin1String("audio/au"); + if (ext == "bwf") return QLatin1String("audio/bwf"); + if (ext == "caf") return QLatin1String("audio/caf"); + if (ext == "cdda") return QLatin1String("audio/cdda"); + if (ext == "gsm") return QLatin1String("audio/gsm"); + if (ext == "kar") return QLatin1String("audio/kar"); + if (ext == "m1a") return QLatin1String("audio/m1a"); + if (ext == "m1s") return QLatin1String("audio/m1s"); + if (ext == "m3u") return QLatin1String("audio/m3u"); + if (ext == "m3url") return QLatin1String("audio/m3url"); + if (ext == "mid") return QLatin1String("audio/mid"); + if (ext == "midi") return QLatin1String("audio/midi"); + if (ext == "mka") return QLatin1String("audio/mka"); + if (ext == "mp3") return QLatin1String("audio/mp3"); + if (ext == "mp4") return QLatin1String("audio/mp4"); + if (ext == "mpa") return QLatin1String("audio/mpa"); + if (ext == "mpeg") return QLatin1String("audio/mpeg"); + if (ext == "mpg") return QLatin1String("audio/mpg"); + if (ext == "mpg4") return QLatin1String("audio/mpg4"); + if (ext == "mpm") return QLatin1String("audio/mpm"); + if (ext == "qcp") return QLatin1String("audio/qcp"); + if (ext == "sd2") return QLatin1String("audio/sd2"); + if (ext == "smf") return QLatin1String("audio/smf"); + if (ext == "snd") return QLatin1String("audio/snd"); + if (ext == "ulw") return QLatin1String("audio/ulw"); + if (ext == "wav") return QLatin1String("audio/wav"); + if (ext == "wax") return QLatin1String("audio/wax"); + if (ext == "wma") return QLatin1String("audio/wma"); + return QString(); +} + +QString mimeForExtensionVideo(const QString &ext) +{ + if (ext == "3g2") return QLatin1String("video/3g2"); + if (ext == "3gp") return QLatin1String("video/3gp"); + if (ext == "asf") return QLatin1String("video/asf"); + if (ext == "asx") return QLatin1String("video/asx"); + if (ext == "avi") return QLatin1String("video/avi"); + if (ext == "dif") return QLatin1String("video/dif"); + if (ext == "dv") return QLatin1String("video/dv"); + if (ext == "flc") return QLatin1String("video/flc"); + if (ext == "fli") return QLatin1String("video/fli"); + if (ext == "m15") return QLatin1String("video/m15"); + if (ext == "m1a") return QLatin1String("video/m1a"); + if (ext == "m1s") return QLatin1String("video/m1s"); + if (ext == "m1v") return QLatin1String("video/m1v"); + if (ext == "m75") return QLatin1String("video/m75"); + if (ext == "mkv") return QLatin1String("video/mkv"); + if (ext == "mp4") return QLatin1String("video/mp4"); + if (ext == "mpa") return QLatin1String("video/mpa"); + if (ext == "mpeg") return QLatin1String("video/mpeg"); + if (ext == "mpg") return QLatin1String("video/mpg"); + if (ext == "mpg4") return QLatin1String("video/mpg4"); + if (ext == "mpm") return QLatin1String("video/mpm"); + if (ext == "mpv") return QLatin1String("video/mpv"); + if (ext == "vfw") return QLatin1String("video/vfw"); + if (ext == "wm") return QLatin1String("video/wm"); + if (ext == "wmv") return QLatin1String("video/wmv"); + if (ext == "wmx") return QLatin1String("video/wmx"); + if (ext == "wvx") return QLatin1String("video/wvx"); + return QString(); +} + +QStringList BackendInfo::quickTimeMimeTypes(Scope scope) +{ + QStringList mimeTypes; + QStringList fileExtensions; + ARGUMENT_UNSUPPORTED(scope, Out, NORMAL_ERROR, mimeTypes) + + PhononAutoReleasePool pool; + NSArray *fileTypes = [QTMovie movieFileTypes:QTIncludeAllTypes]; + for (NSString *type in fileTypes){ + QString formattedType = QString::fromUtf8([type UTF8String]); + formattedType = formattedType.remove('\'').remove('.').toLower(); + QString audioMime = mimeForExtensionAudio(formattedType); + QString videoMime = mimeForExtensionVideo(formattedType); + if (!audioMime.isEmpty()) + mimeTypes << audioMime; + if (!videoMime.isEmpty()) + mimeTypes << videoMime; + if (audioMime.isEmpty() && videoMime.isEmpty()) + fileExtensions << QLatin1String("application/x-qt-") + formattedType; + } + mimeTypes.sort(); + fileExtensions.sort(); + return mimeTypes + fileExtensions; +} +#endif // QUICKTIME_C_API_AVAILABLE + +QStringList BackendInfo::quickTimeCompressionFormats() +{ + QStringList result; + +#ifdef QUICKTIME_C_API_AVAILABLE + + ComponentInstance component = 0; + OSStatus err = OpenADefaultComponent(StandardCompressionType, StandardCompressionSubTypeAudio, &component); + BACKEND_ASSERT3(err == noErr, "Could not open component for retrieving awailable compression formats", NORMAL_ERROR, result) + + UInt32 size; + err = QTGetComponentPropertyInfo(component, kQTPropertyClass_SCAudio, kQTSCAudioPropertyID_AvailableCompressionFormatNamesList, 0, &size,0); + BACKEND_ASSERT3(err == noErr, "Could not get awailable compression formats", NORMAL_ERROR, result) + + CFArrayRef formats[size]; + err = QTGetComponentProperty(component, kQTPropertyClass_SCAudio, kQTSCAudioPropertyID_AvailableCompressionFormatNamesList, size, &formats, &size); + BACKEND_ASSERT3(err == noErr, "Could not get awailable compression formats", NORMAL_ERROR, result) + + CFIndex count = CFArrayGetCount(*formats); + for (CFIndex i=0; i<count; ++i){ + const CFStringRef name = (const struct __CFString *) CFArrayGetValueAtIndex(*formats, i); + result << PhononCFString::toQString(name); + } + +#endif // QUICKTIME_C_API_AVAILABLE + return result; +} + + +QStringList BackendInfo::coreAudioCodecs(Scope scope) +{ + QStringList result; + UInt32 size; + OSStatus err; + OSType *formatIDs; + + OSType encodersOrDecoders = (scope == In) + ? kAudioFormatProperty_EncodeFormatIDs : kAudioFormatProperty_DecodeFormatIDs; + + err = AudioFormatGetPropertyInfo(encodersOrDecoders, 0, NULL, &size); + BACKEND_ASSERT3(err == noErr, "Could not get awailable decoders/encoders", NORMAL_ERROR, result) + + formatIDs = (OSType*)malloc(size); + UInt32 numFormats = size / sizeof(OSType); + err = AudioFormatGetProperty(encodersOrDecoders, 0, NULL, &size, formatIDs); + BACKEND_ASSERT(err == noErr, "Could not get awailable decoders/encoders", NORMAL_ERROR){ + free(formatIDs); + return result; + } + + for (UInt32 i=0; i<numFormats; ++i){ + AudioStreamBasicDescription absd; + memset(&absd, 0, sizeof(absd)); + absd.mFormatID = formatIDs[i]; + + CFStringRef name; + size = sizeof(CFStringRef); + err = AudioFormatGetProperty(kAudioFormatProperty_FormatName, sizeof(absd), &absd, &size, &name); + BACKEND_ASSERT(err == noErr, "Could not get awailable decoder/encoder names", NORMAL_ERROR){ + free(formatIDs); + return result; + } + result << PhononCFString::toQString(name); + } + free(formatIDs); + return result; +} + +QStringList BackendInfo::coreAudioFileTypes(Scope scope) +{ + QStringList result; + OSStatus err; + UInt32 propertySize; + + OSType readOrwrite = (scope == In) + ? kAudioFileGlobalInfo_ReadableTypes : kAudioFileGlobalInfo_WritableTypes; + + err = AudioFileGetGlobalInfoSize(readOrwrite, 0, NULL, &propertySize); + BACKEND_ASSERT3(err == noErr, "Could not get core audio file types", NORMAL_ERROR, result) + + OSType *types = (OSType*)malloc(propertySize); + err = AudioFileGetGlobalInfo(readOrwrite, 0, NULL, &propertySize, types); + BACKEND_ASSERT3(err == noErr, "Could not get core audio file types", NORMAL_ERROR, result) + + UInt32 numTypes = propertySize / sizeof(OSType); + for (UInt32 i=0; i<numTypes; ++i){ + CFStringRef name; + UInt32 outSize = sizeof(name); + err = AudioFileGetGlobalInfo(kAudioFileGlobalInfo_FileTypeName, sizeof(OSType), types+i, &outSize, &name); + BACKEND_ASSERT3(err == noErr, "Could not get core audio file type names", NORMAL_ERROR, result) + result << PhononCFString::toQString(name); + } + return result; +} + +}} + +QT_END_NAMESPACE + diff --git a/src/3rdparty/phonon/qt7/lgpl-2.1.txt b/src/3rdparty/phonon/qt7/lgpl-2.1.txt new file mode 100644 index 0000000..5ab7695 --- /dev/null +++ b/src/3rdparty/phonon/qt7/lgpl-2.1.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[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 + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/src/3rdparty/phonon/qt7/lgpl-3.txt b/src/3rdparty/phonon/qt7/lgpl-3.txt new file mode 100644 index 0000000..fc8a5de --- /dev/null +++ b/src/3rdparty/phonon/qt7/lgpl-3.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + 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 that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU 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 as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/src/3rdparty/phonon/qt7/medianode.h b/src/3rdparty/phonon/qt7/medianode.h new file mode 100644 index 0000000..595e4da --- /dev/null +++ b/src/3rdparty/phonon/qt7/medianode.h @@ -0,0 +1,85 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_MediaNode_H +#define Phonon_QT7_MediaNode_H + +#include <QtCore/QObject> +#include "backendheader.h" +#include "medianodeevent.h" +#include "audioconnection.h" +#include "videoframe.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class AudioNode; + class AudioGraph; + class MediaObject; + class AudioConnection; + + class MediaNode : public QObject + { + Q_OBJECT + + public: + enum NodeDescriptionEnum { + AudioSource = 1, + AudioSink = 2, + VideoSource = 4, + VideoSink = 8, + AudioGraphNode = 16 + }; + Q_DECLARE_FLAGS(NodeDescription, NodeDescriptionEnum); + + MediaNode(NodeDescription description, QObject *parent); + MediaNode(NodeDescription description, AudioNode *audioPart, QObject *parent); + virtual ~MediaNode(); + + void setAudioNode(AudioNode *audioPart); + bool connectToSink(MediaNode *sink); + bool disconnectToSink(MediaNode *sink); + AudioConnection *getAudioConnectionToSink(MediaNode *sink); + + void notify(const MediaNodeEvent *event, bool propagate = true); + void sendEventToSinks(const MediaNodeEvent *event); + virtual void mediaNodeEvent(const MediaNodeEvent *event); + + virtual void updateVideo(VideoFrame &frame); + AudioGraph *m_audioGraph; + + AudioNode *m_audioNode; + QList<AudioConnection *> m_audioSinkList; + QList<AudioConnection *> m_audioSourceList; + QList<MediaNode *> m_videoSinkList; + + int availableAudioInputBus(); + int availableAudioOutputBus(); + + NodeDescription m_description; + MediaObject *m_owningMediaObject; + }; + + Q_DECLARE_OPERATORS_FOR_FLAGS(MediaNode::NodeDescription); + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE +#endif // Phonon_QT7_MediaNode_H diff --git a/src/3rdparty/phonon/qt7/medianode.mm b/src/3rdparty/phonon/qt7/medianode.mm new file mode 100644 index 0000000..00f8340 --- /dev/null +++ b/src/3rdparty/phonon/qt7/medianode.mm @@ -0,0 +1,261 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "medianode.h" +#include "audiograph.h" +#include "audionode.h" +#include "backendheader.h" + +#include "mediaobject.h" +#include "audiooutput.h" +#include "quicktimevideoplayer.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +MediaNode::MediaNode(NodeDescription description, QObject *parent) + : QObject(parent), m_audioGraph(0), m_audioNode(0), m_description(description), m_owningMediaObject(0) +{ +} + +MediaNode::MediaNode(NodeDescription description, AudioNode *audioPart, QObject *parent) + : QObject(parent), m_audioGraph(0), m_audioNode(audioPart), m_description(description) +{ +} + +void MediaNode::setAudioNode(AudioNode *audioPart) +{ + if (m_audioNode) + delete m_audioNode; + m_audioNode = audioPart; +} + +MediaNode::~MediaNode() +{ + delete m_audioNode; + qDeleteAll(m_audioSinkList); +} + +AudioConnection *MediaNode::getAudioConnectionToSink(MediaNode *sink) +{ + AudioConnection *connection = 0; + for (int i=0; i<m_audioSinkList.size(); ++i){ + if (m_audioSinkList[i]->isBetween(this, sink)){ + connection = m_audioSinkList[i]; + break; + } + } + return connection; +} + +bool MediaNode::connectToSink(MediaNode *sink) +{ + if ((m_description & AudioSource) && (sink->m_description & AudioSink)){ + // Check that they don't belong to different graphs. If they do, but + // sink is not connected to any source, accept it: + if (m_owningMediaObject && sink->m_owningMediaObject + && m_owningMediaObject != sink->m_owningMediaObject + && !sink->m_audioSourceList.isEmpty()){ + return false; + } + + // Check that the connection doesn't already exists: + AudioConnection *connection = getAudioConnectionToSink(sink); + if (connection) + return true; + + // Check that there are awailable input/output busses: + int inputBus = sink->availableAudioInputBus(); + int outputBus = availableAudioOutputBus(); + if (inputBus >= sink->m_audioNode->m_maxInputBusses || outputBus >= m_audioNode->m_maxOutputBusses) + return false; + + // All OK. Create connection: + connection = new AudioConnection(this, outputBus, sink, inputBus); + m_audioSinkList << connection; + sink->m_audioSourceList << connection; + + if (m_audioNode->m_audioGraph) + m_audioNode->m_audioGraph->connectLate(connection); + + MediaNodeEvent event1(MediaNodeEvent::AudioSinkAdded, connection); + notify(&event1, false); + MediaNodeEvent event2(MediaNodeEvent::AudioSourceAdded, connection); + sink->notify(&event2, false); + return true; + } + + if ((m_description & VideoSource) && (sink->m_description & VideoSink)){ + // Check that the connection doesn't already exists: + if (m_videoSinkList.contains(sink)) + return true; + + m_videoSinkList << sink; + MediaNodeEvent event1(MediaNodeEvent::VideoSinkAdded, sink); + notify(&event1, false); + MediaNodeEvent event2(MediaNodeEvent::VideoSourceAdded, this); + sink->notify(&event2, false); + return true; + } + + return false; +} + +bool MediaNode::disconnectToSink(MediaNode *sink) +{ + if ((m_description & AudioSource) && (sink->m_description & AudioSink)){ + AudioConnection *connection = getAudioConnectionToSink(sink); + if (!connection) + return false; + + m_audioSinkList.removeOne(connection); + sink->m_audioSourceList.removeOne(connection); + + if (m_audioNode->m_audioGraph) + m_audioNode->m_audioGraph->disconnectLate(connection); + + MediaNodeEvent event1(MediaNodeEvent::AudioSinkRemoved, connection); + notify(&event1, false); + MediaNodeEvent event2(MediaNodeEvent::AudioSourceRemoved, connection); + sink->notify(&event2, false); + + delete connection; + return true; + } + + if ((m_description & VideoSource) && (sink->m_description & VideoSink)){ + m_videoSinkList.removeOne(sink); + + MediaNodeEvent event1(MediaNodeEvent::VideoSinkRemoved, sink); + notify(&event1, false); + MediaNodeEvent event2(MediaNodeEvent::VideoSourceRemoved, this); + sink->notify(&event2, false); + return true; + } + + return false; +} + +int MediaNode::availableAudioInputBus() +{ + // Scan through all the connection <u>in</u> to this + // node, and find an awailable index: + int index = -1; + bool available = false; + + while (!available){ + ++index; + available = true; + for (int i=0; i<m_audioSourceList.size(); ++i){ + if (m_audioSourceList[i]->m_sinkInputBus == index){ + available = false; + break; + } + } + } + return index; +} + +int MediaNode::availableAudioOutputBus() +{ + // Scan through all the connection <u>out</u> from this + // node, and find an awailable index: + int bus = -1; + bool available = false; + + while (!available){ + ++bus; + available = true; + for (int i=0; i<m_audioSinkList.size(); ++i){ + if (m_audioSinkList[i]->m_sourceOutputBus == bus){ + available = false; + break; + } + } + } + return bus; +} + +void MediaNode::notify(const MediaNodeEvent *event, bool propagate) +{ + // Let subclass handle the event first: + mediaNodeEvent(event); + + switch(event->type()){ + case MediaNodeEvent::AudioGraphAboutToBeDeleted: + if (m_audioNode){ + foreach(AudioConnection *connection, m_audioSinkList) + connection->invalidate(); + } + break; + case MediaNodeEvent::NewAudioGraph: + m_audioGraph = static_cast<AudioGraph *>(event->data()); + break; + case MediaNodeEvent::AudioSinkAdded: + case MediaNodeEvent::VideoSinkAdded: + if (m_owningMediaObject){ + MediaNodeEvent e1(MediaNodeEvent::SetMediaObject, m_owningMediaObject); + sendEventToSinks(&e1); + QRect videoRect = m_owningMediaObject->videoPlayer()->videoRect(); + MediaNodeEvent e2(MediaNodeEvent::VideoFrameSizeChanged, &videoRect); + sendEventToSinks(&e2); + } + break; + case MediaNodeEvent::SetMediaObject: + m_owningMediaObject = static_cast<MediaObject *>(event->data()); + break; + default: + break; + } + + // Inform the audio node as well: + if (m_audioNode) + m_audioNode->notify(event); + + // And perhaps the sinks: + if (propagate) + sendEventToSinks(event); +} + +void MediaNode::sendEventToSinks(const MediaNodeEvent *event) +{ + for (int i=0; i<m_audioSinkList.size(); ++i) + m_audioSinkList[i]->m_sink->notify(event); + for (int i=0; i<m_videoSinkList.size(); ++i) + m_videoSinkList[i]->notify(event); +} + +void MediaNode::updateVideo(VideoFrame &frame){ + for (int i=0; i<m_videoSinkList.size(); ++i) + m_videoSinkList[i]->updateVideo(frame); +} + +void MediaNode::mediaNodeEvent(const MediaNodeEvent */*event*/) +{ + // Override if needed. +} + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE + +#include "moc_medianode.cpp" + diff --git a/src/3rdparty/phonon/qt7/medianodeevent.h b/src/3rdparty/phonon/qt7/medianodeevent.h new file mode 100644 index 0000000..ad6e212 --- /dev/null +++ b/src/3rdparty/phonon/qt7/medianodeevent.h @@ -0,0 +1,71 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_MEDIANODEEVENT_H +#define Phonon_QT7_MEDIANODEEVENT_H + +#include <QtCore/qnamespace.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class QuickTimeVideoPlayer; + + class MediaNodeEvent + { + public: + enum Type { + AudioGraphAboutToBeDeleted, + NewAudioGraph, + AudioGraphInitialized, + AudioGraphCannotPlay, + AboutToRestartAudioStream, + RestartAudioStreamRequest, + VideoSinkAdded, + VideoSinkRemoved, + AudioSinkAdded, + AudioSinkRemoved, + VideoSourceAdded, + VideoSourceRemoved, + AudioSourceAdded, + AudioSourceRemoved, + VideoOutputCountChanged, + VideoFrameSizeChanged, + SetMediaObject, + StartConnectionChange, + EndConnectionChange, + MediaPlaying + }; + + MediaNodeEvent(Type type, void *data = 0); + virtual ~MediaNodeEvent(); + inline Type type() const{ return eventType; }; + inline void* data() const { return eventData; }; + + private: + Type eventType; + void *eventData; + }; + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_MEDIANODEEVENT_H diff --git a/src/3rdparty/phonon/qt7/medianodeevent.mm b/src/3rdparty/phonon/qt7/medianodeevent.mm new file mode 100644 index 0000000..664fbbc --- /dev/null +++ b/src/3rdparty/phonon/qt7/medianodeevent.mm @@ -0,0 +1,37 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "medianodeevent.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +MediaNodeEvent::MediaNodeEvent(Type type, void *data) : eventType(type), eventData(data) +{ +} + +MediaNodeEvent::~MediaNodeEvent() +{ +} + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/medianodevideopart.h b/src/3rdparty/phonon/qt7/medianodevideopart.h new file mode 100644 index 0000000..fb1f89c --- /dev/null +++ b/src/3rdparty/phonon/qt7/medianodevideopart.h @@ -0,0 +1,42 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_MEDIANODEVIDEOPART_H +#define Phonon_QT7_MEDIANODEVIDEOPART_H + +#include <QtCore/qnamespace.h> +#include "backendheader.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class MediaNodeVideoPart + { + public: + MediaNodeVideoPart(); + virtual ~MediaNodeVideoPart(); + virtual void updateVideo(void *ciImage) = 0; + }; + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_MEDIANODEVIDEOPART_H diff --git a/src/3rdparty/phonon/qt7/medianodevideopart.mm b/src/3rdparty/phonon/qt7/medianodevideopart.mm new file mode 100644 index 0000000..da060da --- /dev/null +++ b/src/3rdparty/phonon/qt7/medianodevideopart.mm @@ -0,0 +1,37 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "medianodevideopart.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +MediaNodeVideoPart::MediaNodeVideoPart() +{ +} + +MediaNodeVideoPart::~MediaNodeVideoPart() +{ +} + +}} + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/mediaobject.h b/src/3rdparty/phonon/qt7/mediaobject.h new file mode 100644 index 0000000..ae623a9 --- /dev/null +++ b/src/3rdparty/phonon/qt7/mediaobject.h @@ -0,0 +1,187 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_MEDIAOBJECT_H +#define Phonon_QT7_MEDIAOBJECT_H + +#include <QtCore/QStringList> +#include <QtCore/QTime> +#include <phonon/mediaobjectinterface.h> +#include <phonon/addoninterface.h> + +#include "medianode.h" + +#if QT_ALLOW_QUICKTIME + #include <QuickTime/QuickTime.h> +#endif + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class QuickTimeVideoPlayer; + class QuickTimeAudioPlayer; + class QuickTimeMetaData; + class AudioGraph; + class MediaObjectAudioNode; + + class MediaObject : public MediaNode, + public Phonon::MediaObjectInterface, public Phonon::AddonInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::MediaObjectInterface Phonon::AddonInterface) + + public: + MediaObject(QObject *parent); + ~MediaObject(); + + QStringList availableAudioStreams() const; + QStringList availableVideoStreams() const; + QStringList availableSubtitleStreams() const; + QString currentAudioStream(const QObject *audioPath) const; + QString currentVideoStream(const QObject *videoPath) const; + QString currentSubtitleStream(const QObject *videoPath) const; + + void setCurrentAudioStream(const QString &streamName,const QObject *audioPath); + void setCurrentVideoStream(const QString &streamName,const QObject *videoPath); + void setCurrentSubtitleStream(const QString &streamName,const QObject *videoPath); + + void play(); + void pause(); + void stop(); + void seek(qint64 milliseconds); + + qint32 tickInterval() const; + void setTickInterval(qint32 interval); + bool hasVideo() const; + bool isSeekable() const; + qint64 currentTime() const; + Phonon::State state() const; + + QString errorString() const; + Phonon::ErrorType errorType() const; + + qint64 totalTime() const; + MediaSource source() const; + void setSource(const MediaSource &); + void setNextSource(const MediaSource &source); + qint32 prefinishMark() const; + void setPrefinishMark(qint32); + qint32 transitionTime() const; + void setTransitionTime(qint32); + bool hasInterface(Interface interface) const; + QVariant interfaceCall(Interface interface, int command, const QList<QVariant> &arguments = QList<QVariant>()); + + QuickTimeVideoPlayer* videoPlayer() const; + QuickTimeAudioPlayer* audioPlayer() const; + + void setVolumeOnMovie(float volume); + bool setAudioDeviceOnMovie(int id); + + int videoOutputCount(); + +#if QT_ALLOW_QUICKTIME + void displayLinkEvent(); +#endif + + signals: + void stateChanged(Phonon::State,Phonon::State); + void tick(qint64); + void seekableChanged(bool); + void hasVideoChanged(bool); + void bufferStatus(int); + void finished(); + void aboutToFinish(); + void prefinishMarkReached(qint32); + void totalTimeChanged(qint64); + void metaDataChanged(QMultiMap<QString,QString>); + void currentSourceChanged(const MediaSource &newSource); + + protected: + void mediaNodeEvent(const MediaNodeEvent *event); + bool event(QEvent *event); + + private: + enum AudioSystem {AS_Unset, AS_Video, AS_Graph, AS_Silent} m_audioSystem; + Phonon::State m_state; + + QuickTimeVideoPlayer *m_videoPlayer; + QuickTimeAudioPlayer *m_audioPlayer; + QuickTimeVideoPlayer *m_nextVideoPlayer; + QuickTimeAudioPlayer *m_nextAudioPlayer; + MediaObjectAudioNode *m_mediaObjectAudioNode; + QuickTimeMetaData *m_metaData; + +#if QT_ALLOW_QUICKTIME + CVDisplayLinkRef m_displayLink; + QMutex m_displayLinkMutex; + bool m_pendingDisplayLinkEvent; + void startDisplayLink(); + void stopDisplayLink(); +#endif + + qint32 m_tickInterval; + qint32 m_transitionTime; + quint32 m_prefinishMark; + quint32 m_currentTime; + float m_percentageLoaded; + + int m_tickTimer; + int m_videoTimer; + int m_audioTimer; + int m_rapidTimer; + + bool m_waitNextSwap; + int m_swapTimeLeft; + QTime m_swapTime; + + void synchAudioVideo(); + void updateCurrentTime(); + void swapCurrentWithNext(qint32 transitionTime); + bool setState(Phonon::State state); + void pause_internal(); + void play_internal(); + void setupAudioSystem(); + void restartAudioVideoTimers(); + void updateRapidly(); + void updateCrossFade(); + void updateAudioBuffers(); + void updateLipSynch(int allowedOffset); + void updateVideoFrames(); + void updateBufferStatus(); + void setMute(bool mute); + void inspectAudioGraphRecursive(AudioConnection *connection, int &effectCount, int &outputCount); + void inspectVideoGraphRecursive(MediaNode *node, int &effectCount, int &outputCount); + void inspectGraph(); + bool isCrossFading(); + + QString m_errorString; + Phonon::ErrorType m_errorType; + bool checkForError(); + + int m_audioEffectCount; + int m_audioOutputCount; + int m_videoEffectCount; + int m_videoOutputCount; + }; + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE +#endif // Phonon_QT7_MEDIAOBJECT_H diff --git a/src/3rdparty/phonon/qt7/mediaobject.mm b/src/3rdparty/phonon/qt7/mediaobject.mm new file mode 100644 index 0000000..6886a3c --- /dev/null +++ b/src/3rdparty/phonon/qt7/mediaobject.mm @@ -0,0 +1,945 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include <QtCore/QEvent> +#include "mediaobject.h" +#include "backendheader.h" +#include "videowidget.h" +#include "videoframe.h" +#include "audiooutput.h" +#include "quicktimevideoplayer.h" +#include "quicktimemetadata.h" +#include "audiograph.h" +#include "mediaobjectaudionode.h" +#include "quicktimeaudioplayer.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +MediaObject::MediaObject(QObject *parent) : MediaNode(AudioSource | VideoSource, parent) +{ + m_owningMediaObject = this; + m_state = Phonon::LoadingState; + + m_videoPlayer = new QuickTimeVideoPlayer(); + m_audioPlayer = new QuickTimeAudioPlayer(); + m_nextVideoPlayer = new QuickTimeVideoPlayer(); + m_nextAudioPlayer = new QuickTimeAudioPlayer(); + m_mediaObjectAudioNode = new MediaObjectAudioNode(m_audioPlayer, m_nextAudioPlayer); + setAudioNode(m_mediaObjectAudioNode); + + m_metaData = new QuickTimeMetaData(); + m_audioGraph = new AudioGraph(this); + + m_tickInterval = 0; + m_prefinishMark = 0; + m_currentTime = 0; + m_transitionTime = 0; + m_percentageLoaded = 0; + m_waitNextSwap = false; + m_audioEffectCount = 0; + m_audioOutputCount = 0; + m_videoEffectCount = 0; + m_videoOutputCount = 0; + m_audioSystem = AS_Unset; + m_errorType = Phonon::NoError; + + m_tickTimer = 0; + m_videoTimer = 0; + m_audioTimer = 0; + m_rapidTimer = 0; + +#if QT_ALLOW_QUICKTIME + m_displayLink = 0; + m_pendingDisplayLinkEvent = false; +#endif + + checkForError(); +} + +MediaObject::~MediaObject() +{ + // m_mediaObjectAudioNode is owned by super class. +#if QT_ALLOW_QUICKTIME + stopDisplayLink(); +#endif + m_audioPlayer->unsetVideoPlayer(); + m_nextAudioPlayer->unsetVideoPlayer(); + delete m_videoPlayer; + delete m_nextVideoPlayer; + delete m_metaData; + checkForError(); +} + +bool MediaObject::setState(Phonon::State state) +{ + Phonon::State prevState = m_state; + m_state = state; + if (prevState != m_state){ + emit stateChanged(m_state, prevState); + if (m_state != state){ + // End-application did something + // upon receiving the signal. + return false; + } + } + return true; +} + +void MediaObject::inspectAudioGraphRecursive(AudioConnection *connection, int &effectCount, int &outputCount) +{ + if ((connection->m_sink->m_description & (AudioSource | AudioSink)) == (AudioSource | AudioSink)) + ++effectCount; + else if (connection->m_sink->m_description & AudioSink) + ++outputCount; + + for (int i=0; i<connection->m_sink->m_audioSinkList.size(); ++i) + inspectAudioGraphRecursive(connection->m_sink->m_audioSinkList[i], effectCount, outputCount); +} + +void MediaObject::inspectVideoGraphRecursive(MediaNode *node, int &effectCount, int &outputCount) +{ + if ((node->m_description & (VideoSource | VideoSink)) == (VideoSource | VideoSink)) + ++effectCount; + else if (node->m_description & VideoSink) + ++outputCount; + + for (int i=0; i<node->m_videoSinkList.size(); ++i) + inspectVideoGraphRecursive(node->m_videoSinkList[i], effectCount, outputCount); +} + +void MediaObject::inspectGraph() +{ + // Inspect the graph to check wether there are any + // effects or outputs connected. This will have + // influence on the audio system and video system that ends up beeing used: + int prevVideoOutputCount = m_videoOutputCount; + m_audioEffectCount = 0; + m_audioOutputCount = 0; + m_videoEffectCount = 0; + m_videoOutputCount = 0; + AudioConnection rootConnection(this); + inspectAudioGraphRecursive(&rootConnection, m_audioEffectCount, m_audioOutputCount); + inspectVideoGraphRecursive(this, m_videoEffectCount, m_videoOutputCount); + + if (m_videoOutputCount != prevVideoOutputCount){ + MediaNodeEvent e1(MediaNodeEvent::VideoOutputCountChanged, &m_videoOutputCount); + notify(&e1); + } +} + +void MediaObject::setupAudioSystem() +{ + // Select which audio system to use: + AudioSystem newAudioSystem = AS_Unset; + if (!m_audioOutputCount || !m_videoPlayer->canPlayMedia()){ + newAudioSystem = AS_Silent; + } else if (m_audioEffectCount == 0){ + newAudioSystem = AS_Video; + } else if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_4){ + newAudioSystem = AS_Video; + SET_ERROR("Audio effects are not supported for Mac OS 10.3 and below", NORMAL_ERROR); + } else if (m_videoPlayer->isDrmProtected()){ + newAudioSystem = AS_Video; + SET_ERROR("Audio effects are not supported for DRM protected media", NORMAL_ERROR); + } else if (m_audioGraph->graphCannotPlay()){ + newAudioSystem = AS_Video; + SET_ERROR("Audio effects are not supported for the current codec", NORMAL_ERROR); +#ifdef QUICKTIME_C_API_AVAILABLE + } else { + newAudioSystem = AS_Graph; + } +#else + } else { + newAudioSystem = AS_Video; + SET_ERROR("Audio effects are not supported for the 64-bit version of the Phonon QT7 backend", NORMAL_ERROR); + } +#endif + + if (newAudioSystem == m_audioSystem) + return; + + // Enable selected audio system: + m_audioSystem = newAudioSystem; + switch (newAudioSystem){ + case AS_Silent: + m_audioGraph->stop(); + m_videoPlayer->enableAudio(false); + m_nextVideoPlayer->enableAudio(false); + m_audioPlayer->enableAudio(false); + m_nextAudioPlayer->enableAudio(false); + break; + case AS_Graph: + if (m_state == Phonon::PausedState) + m_audioGraph->prepare(); + else + m_audioGraph->start(); + // Starting the graph can lead to a recursive call + // telling us that we must direct audio through + // video. If that has happened, we must not proceed: + if (m_audioSystem != AS_Graph) + return; + m_videoPlayer->enableAudio(false); + m_nextVideoPlayer->enableAudio(false); + m_audioPlayer->enableAudio(true); + m_audioPlayer->seek(m_videoPlayer->currentTime()); + m_nextAudioPlayer->enableAudio(true); + m_audioPlayer->seek(m_videoPlayer->currentTime()); + m_nextAudioPlayer->seek(m_nextVideoPlayer->currentTime()); + break; + case AS_Video: + case AS_Unset: + m_audioGraph->stop(); + m_videoPlayer->enableAudio(true); + m_nextVideoPlayer->enableAudio(true); + m_audioPlayer->enableAudio(false); + m_nextAudioPlayer->enableAudio(false); + m_videoPlayer->seek(m_audioPlayer->currentTime()); + m_nextVideoPlayer->seek(m_nextAudioPlayer->currentTime()); + break; + } +} + +void MediaObject::setSource(const MediaSource &source) +{ + IMPLEMENTED; + PhononAutoReleasePool pool; + setState(Phonon::LoadingState); + + // Save current state for event/signal handling below: + bool prevHasVideo = m_videoPlayer->hasVideo(); + qint64 prevTotalTime = totalTime(); + m_waitNextSwap = false; + + // Cancel cross-fade if any: + m_nextVideoPlayer->pause(); + m_nextAudioPlayer->pause(); + m_mediaObjectAudioNode->cancelCrossFade(); + + // Set new source: + m_audioPlayer->unsetVideoPlayer(); + m_videoPlayer->setMediaSource(source); + m_audioPlayer->setVideoPlayer(m_videoPlayer); + m_metaData->setVideo(m_videoPlayer); + + m_audioGraph->updateStreamSpecifications(); + m_nextAudioPlayer->unsetVideoPlayer(); + m_nextVideoPlayer->unsetVideo(); + m_currentTime = 0; + + // Emit/notify information about the new source: + QRect videoRect = m_videoPlayer->videoRect(); + MediaNodeEvent e1(MediaNodeEvent::VideoFrameSizeChanged, &videoRect); + notify(&e1); + + // Clear video widgets: + VideoFrame emptyFrame; + updateVideo(emptyFrame); + + emit currentSourceChanged(source); + emit metaDataChanged(m_metaData->metaData()); + + if (prevHasVideo != m_videoPlayer->hasVideo()) + emit hasVideoChanged(m_videoPlayer->hasVideo()); + if (prevTotalTime != totalTime()) + emit totalTimeChanged(totalTime()); + if (checkForError()) + return; + if (!m_videoPlayer->isDrmAuthorized()) + SET_ERROR("This computer is not authorized to play current media (DRM protected).", FATAL_ERROR) + if (checkForError()) + return; + if (!m_videoPlayer->canPlayMedia()) + SET_ERROR("Cannot play media.", FATAL_ERROR) + + // The state might have changed from LoadingState + // as a response to an error state change. So we + // need to check it before stopping: + if (m_state == Phonon::LoadingState) + stop(); + + setupAudioSystem(); + checkForError(); +} + +void MediaObject::setNextSource(const MediaSource &source) +{ + IMPLEMENTED; + m_nextAudioPlayer->unsetVideoPlayer(); + m_nextVideoPlayer->setMediaSource(source); + m_nextAudioPlayer->setVideoPlayer(m_nextVideoPlayer); + checkForError(); +} + +void MediaObject::swapCurrentWithNext(qint32 transitionTime) +{ + PhononAutoReleasePool pool; + setState(Phonon::LoadingState); + // Save current state for event/signal handling below: + bool prevHasVideo = m_videoPlayer->hasVideo(); + qint64 prevTotalTime = totalTime(); + + qSwap(m_audioPlayer, m_nextAudioPlayer); + qSwap(m_videoPlayer, m_nextVideoPlayer); + m_mediaObjectAudioNode->startCrossFade(transitionTime); + m_audioGraph->updateStreamSpecifications(); + m_metaData->setVideo(m_videoPlayer); + + m_waitNextSwap = false; + m_currentTime = 0; + + // Emit/notify information about the new source: + QRect videoRect = m_videoPlayer->videoRect(); + MediaNodeEvent e1(MediaNodeEvent::VideoFrameSizeChanged, &videoRect); + notify(&e1); + + emit currentSourceChanged(m_videoPlayer->mediaSource()); + emit metaDataChanged(m_metaData->metaData()); + + if (prevHasVideo != m_videoPlayer->hasVideo()) + emit hasVideoChanged(m_videoPlayer->hasVideo()); + if (prevTotalTime != totalTime()) + emit totalTimeChanged(totalTime()); + if (checkForError()) + return; + if (!m_videoPlayer->isDrmAuthorized()) + SET_ERROR("This computer is not authorized to play current media (DRM protected).", FATAL_ERROR) + if (checkForError()) + return; + if (!m_videoPlayer->canPlayMedia()) + SET_ERROR("Cannot play next media.", FATAL_ERROR) + + setupAudioSystem(); + checkForError(); + if (m_state == Phonon::LoadingState){ + if (setState(Phonon::PlayingState)) + play_internal(); + checkForError(); + } +} + +#if QT_ALLOW_QUICKTIME +static CVReturn displayLinkCallback(CVDisplayLinkRef /*displayLink*/, + const CVTimeStamp */*inNow*/, + const CVTimeStamp */*inOutputTime*/, + CVOptionFlags /*flagsIn*/, + CVOptionFlags */*flagsOut*/, + void *userData) +{ + MediaObject *mediaObject = static_cast<MediaObject *>(userData); + mediaObject->displayLinkEvent(); + return kCVReturnSuccess; +} + +void MediaObject::displayLinkEvent() +{ + // This function is called from a + // thread != gui thread. So we post the event. + // But we need to make sure that we don't post faster + // than the event loop can eat: + m_displayLinkMutex.lock(); + bool pending = m_pendingDisplayLinkEvent; + m_pendingDisplayLinkEvent = true; + m_displayLinkMutex.unlock(); + + if (!pending) + qApp->postEvent(this, new QEvent(QEvent::User), Qt::HighEventPriority); +} + +void MediaObject::startDisplayLink() +{ + if (m_displayLink) + return; + OSStatus err = CVDisplayLinkCreateWithCGDisplay(kCGDirectMainDisplay, &m_displayLink); + if (err != noErr) + goto fail; + err = CVDisplayLinkSetCurrentCGDisplay(m_displayLink, kCGDirectMainDisplay); + if (err != noErr) + goto fail; + err = CVDisplayLinkSetOutputCallback(m_displayLink, displayLinkCallback, this); + if (err != noErr) + goto fail; + err = CVDisplayLinkStart(m_displayLink); + if (err != noErr) + goto fail; + return; +fail: + stopDisplayLink(); +} + +void MediaObject::stopDisplayLink() +{ + if (!m_displayLink) + return; + CVDisplayLinkStop(m_displayLink); + CFRelease(m_displayLink); + m_displayLink = 0; +} +#endif + +void MediaObject::restartAudioVideoTimers() +{ + if (m_videoTimer) + killTimer(m_videoTimer); + if (m_audioTimer) + killTimer(m_audioTimer); + +#if QT_ALLOW_QUICKTIME + // We prefer to use a display link as timer if available, since + // it is more steady, and results in better and smoother frame drawing: + startDisplayLink(); + if (!m_displayLink){ + float fps = m_videoPlayer->staticFps(); + long videoUpdateFrequency = fps ? long(1000.0f / fps) : 0.001; + m_videoTimer = startTimer(videoUpdateFrequency); + } +#else + float fps = m_videoPlayer->staticFps(); + long videoUpdateFrequency = fps ? long(1000.0f / fps) : 0.001; + m_videoTimer = startTimer(videoUpdateFrequency); +#endif + + long audioUpdateFrequency = m_audioPlayer->regularTaskFrequency(); + m_audioTimer = startTimer(audioUpdateFrequency); + updateVideoFrames(); + updateAudioBuffers(); +} + +void MediaObject::play_internal() +{ + // Play main audio/video: + m_videoPlayer->play(); + m_audioPlayer->play(); + updateLipSynch(0); + // Play old audio/video to finish cross-fade: + if (m_nextVideoPlayer->currentTime() > 0){ + m_nextVideoPlayer->play(); + m_nextAudioPlayer->play(); + } + restartAudioVideoTimers(); + if (!m_rapidTimer) + m_rapidTimer = startTimer(100); +} + +void MediaObject::pause_internal() +{ + m_audioGraph->stop(); + m_audioPlayer->pause(); + m_nextAudioPlayer->pause(); + m_videoPlayer->pause(); + m_nextVideoPlayer->pause(); + killTimer(m_rapidTimer); + killTimer(m_videoTimer); + killTimer(m_audioTimer); + m_rapidTimer = 0; + m_videoTimer = 0; + m_audioTimer = 0; +#if QT_ALLOW_QUICKTIME + stopDisplayLink(); +#endif + if (m_waitNextSwap) + m_swapTimeLeft = m_swapTime.msecsTo(QTime::currentTime()); +} + +void MediaObject::play() +{ + IMPLEMENTED; + if (m_state == Phonon::PlayingState) + return; + if (m_waitNextSwap){ + // update swap time after pause: + m_swapTime = QTime::currentTime(); + m_swapTime.addMSecs(m_swapTimeLeft); + setState(Phonon::PlayingState); + return; + } + if (m_currentTime == m_videoPlayer->duration()) + return; + if (!m_videoPlayer->canPlayMedia()) + return; + if (!setState(Phonon::PlayingState)) + return; + if (m_audioSystem == AS_Graph){ + m_audioGraph->start(); + m_mediaObjectAudioNode->setMute(true); + } + // Inform the graph that we are about to play: + bool playing = true; + MediaNodeEvent e1(MediaNodeEvent::MediaPlaying, &playing); + notify(&e1); + // Start to play: + play_internal(); + m_mediaObjectAudioNode->setMute(false); + checkForError(); +} + +void MediaObject::pause() +{ + IMPLEMENTED; + if (m_state == Phonon::PausedState) + return; + if (!setState(Phonon::PausedState)) + return; + pause_internal(); + // Inform the graph that we are no longer playing: + bool playing = false; + MediaNodeEvent e1(MediaNodeEvent::MediaPlaying, &playing); + notify(&e1); + // But be prepared: + if (m_audioSystem == AS_Graph) + m_audioGraph->prepare(); + checkForError(); +} + +void MediaObject::stop() +{ + IMPLEMENTED; + if (m_state == Phonon::StoppedState) + return; + if (!setState(Phonon::StoppedState)) + return; + m_waitNextSwap = false; + m_nextVideoPlayer->unsetVideo(); + m_nextAudioPlayer->unsetVideoPlayer(); + pause_internal(); + seek(0); + checkForError(); +} + +void MediaObject::seek(qint64 milliseconds) +{ + IMPLEMENTED; + if (m_state == Phonon::ErrorState) + return; + + // Stop cross-fade if any: + m_nextVideoPlayer->unsetVideo(); + m_nextAudioPlayer->unsetVideoPlayer(); + m_mediaObjectAudioNode->cancelCrossFade(); + + // Seek to new position: + m_mediaObjectAudioNode->setMute(true); + m_videoPlayer->seek(milliseconds); + m_audioPlayer->seek(m_videoPlayer->currentTime()); + m_mediaObjectAudioNode->setMute(false); + + // Update time and cancel pending swap: + if (m_currentTime < m_videoPlayer->duration()) + m_waitNextSwap = false; + + updateCurrentTime(); + if (m_state != Phonon::PlayingState) + updateVideoFrames(); + checkForError(); +} + +QStringList MediaObject::availableAudioStreams() const +{ + NOT_IMPLEMENTED; + return QStringList(); +} + +QStringList MediaObject::availableVideoStreams() const +{ + NOT_IMPLEMENTED; + return QStringList(); +} + +QStringList MediaObject::availableSubtitleStreams() const +{ + NOT_IMPLEMENTED; + return QStringList(); +} + +QString MediaObject::currentAudioStream(const QObject */*audioPath*/) const +{ + NOT_IMPLEMENTED; + return QString(); +} + +QString MediaObject::currentVideoStream(const QObject */*videoPath*/) const +{ + NOT_IMPLEMENTED; + return QString(); +} + +QString MediaObject::currentSubtitleStream(const QObject */*videoPath*/) const +{ + NOT_IMPLEMENTED; + return QString(); +} + +void MediaObject::setCurrentAudioStream(const QString &/*streamName*/,const QObject */*audioPath*/) +{ + NOT_IMPLEMENTED; +} + +void MediaObject::setCurrentVideoStream(const QString &/*streamName*/,const QObject */*videoPath*/) +{ + NOT_IMPLEMENTED; +} + +void MediaObject::setCurrentSubtitleStream(const QString &/*streamName*/,const QObject */*videoPath*/) +{ + NOT_IMPLEMENTED; +} + +int MediaObject::videoOutputCount() +{ + return m_videoOutputCount; +} + +void MediaObject::synchAudioVideo() +{ + if (m_state != Phonon::PlayingState) + return; + if (m_videoSinkList.isEmpty() || m_audioSinkList.isEmpty()) + return; + + seek(m_currentTime); + checkForError(); +} + +qint32 MediaObject::tickInterval() const +{ + IMPLEMENTED; + return m_tickInterval; +} + +void MediaObject::setTickInterval(qint32 interval) +{ + IMPLEMENTED; + m_tickInterval = interval; + if (m_tickInterval > 0) + m_tickTimer = startTimer(m_tickInterval); + else{ + killTimer(m_tickTimer); + m_tickTimer = 0; + } +} + +bool MediaObject::hasVideo() const +{ + IMPLEMENTED; + return m_videoPlayer ? m_videoPlayer->hasVideo() : false; +} + +bool MediaObject::isSeekable() const +{ + IMPLEMENTED; + return m_videoPlayer ? m_videoPlayer->isSeekable() : false; +} + +qint64 MediaObject::currentTime() const +{ + IMPLEMENTED_SILENT; + const_cast<MediaObject *>(this)->updateCurrentTime(); + return m_currentTime; +} + +void MediaObject::updateCurrentTime() +{ + quint64 lastUpdateTime = m_currentTime; + m_currentTime = (m_audioSystem == AS_Graph) ? m_audioPlayer->currentTime() : m_videoPlayer->currentTime(); + quint64 total = m_videoPlayer->duration(); + + // Check if it's time to emit aboutToFinish: + quint32 mark = qMax(quint64(0), qMin(total, total + m_transitionTime - 2000)); + if (lastUpdateTime < mark && mark <= m_currentTime) + emit aboutToFinish(); + + // Check if it's time to emit prefinishMarkReached: + mark = qMax(quint64(0), total - m_prefinishMark); + if (lastUpdateTime < mark && mark <= m_currentTime) + emit prefinishMarkReached(total - m_currentTime); + + if (m_nextVideoPlayer->state() == QuickTimeVideoPlayer::NoMedia){ + // There is no next source in que. + // Check if it's time to emit finished: + if (lastUpdateTime < m_currentTime && m_currentTime == total){ + emit finished(); + m_currentTime = (m_audioSystem == AS_Graph) ? m_audioPlayer->currentTime() : m_videoPlayer->currentTime(); + if (m_state == Phonon::PlayingState && m_currentTime == total) + pause(); + } + } else { + // We have a next source. + // Check if it's time to swap to next source: + mark = qMax(quint64(0), total + m_transitionTime); + if (m_waitNextSwap && m_state == Phonon::PlayingState && + m_transitionTime < m_swapTime.msecsTo(QTime::currentTime())){ + swapCurrentWithNext(0); + } else if (mark >= total){ + if (lastUpdateTime < total && total == m_currentTime){ + m_swapTime = QTime::currentTime(); + m_swapTime.addMSecs(mark - total); + m_waitNextSwap = true; + } + } else if (lastUpdateTime < mark && mark <= m_currentTime){ + swapCurrentWithNext(total - m_currentTime); + } + } +} + +qint64 MediaObject::totalTime() const +{ + IMPLEMENTED_SILENT; + return m_videoPlayer->duration(); +} + +Phonon::State MediaObject::state() const +{ + IMPLEMENTED; + return m_state; +} + +QString MediaObject::errorString() const +{ + IMPLEMENTED; + return m_errorString; +} + +Phonon::ErrorType MediaObject::errorType() const +{ + IMPLEMENTED; + return m_errorType; +} + +bool MediaObject::checkForError() +{ + int type = gGetErrorType(); + if (type == NO_ERROR) + return false; + + m_errorType = (type == NORMAL_ERROR) ? Phonon::NormalError : Phonon::FatalError; + m_errorString = gGetErrorString(); + pause_internal(); + gClearError(); + setState(Phonon::ErrorState); + return true; +} + +QuickTimeVideoPlayer* MediaObject::videoPlayer() const +{ + return m_videoPlayer; +} + +MediaSource MediaObject::source() const +{ + IMPLEMENTED; + return m_videoPlayer->mediaSource(); +} + +qint32 MediaObject::prefinishMark() const +{ + IMPLEMENTED; + return m_prefinishMark; +} + +void MediaObject::setPrefinishMark(qint32 mark) +{ + IMPLEMENTED; + m_prefinishMark = mark; +} + +qint32 MediaObject::transitionTime() const +{ + IMPLEMENTED; + return m_transitionTime; +} + +void MediaObject::setTransitionTime(qint32 transitionTime) +{ + IMPLEMENTED; + m_transitionTime = transitionTime; +} + +void MediaObject::setVolumeOnMovie(float volume) +{ + m_videoPlayer->setMasterVolume(volume); + m_nextVideoPlayer->setMasterVolume(volume); +} + +bool MediaObject::setAudioDeviceOnMovie(int id) +{ + m_nextVideoPlayer->setAudioDevice(id); + return m_videoPlayer->setAudioDevice(id); +} + +void MediaObject::updateCrossFade() +{ + m_mediaObjectAudioNode->updateCrossFade(m_currentTime); + // Clean-up previous movie if done fading: + if (m_mediaObjectAudioNode->m_fadeDuration == 0){ + if (m_nextVideoPlayer->isPlaying() || m_nextAudioPlayer->isPlaying()){ + m_nextVideoPlayer->unsetVideo(); + m_nextAudioPlayer->unsetVideoPlayer(); + } + } +} + +void MediaObject::updateBufferStatus() +{ + float percent = m_videoPlayer->percentageLoaded(); + if (percent != m_percentageLoaded){ + m_percentageLoaded = percent; + emit bufferStatus(m_percentageLoaded * 100); + } +} + +void MediaObject::updateAudioBuffers() +{ + // Schedule audio slices: + m_audioPlayer->scheduleAudioToGraph(); + m_nextAudioPlayer->scheduleAudioToGraph(); +} + +bool MediaObject::isCrossFading() +{ + return m_mediaObjectAudioNode->isCrossFading(); +} + +void MediaObject::updateVideoFrames() +{ + // Draw next frame if awailable: + if (m_videoPlayer->videoFrameChanged()){ + updateLipSynch(50); + VideoFrame frame(m_videoPlayer); + if (m_nextVideoPlayer->isPlaying() + && m_nextVideoPlayer->hasVideo() + && isCrossFading()){ + VideoFrame bgFrame(m_nextVideoPlayer); + frame.setBackgroundFrame(bgFrame); + frame.setBaseOpacity(m_mediaObjectAudioNode->m_volume1); + } + + // Send the frame through the graph: + updateVideo(frame); + checkForError(); + } +} + +void MediaObject::updateLipSynch(int allowedOffset) +{ + if (m_audioSystem != AS_Graph || !m_audioGraph->isRunning()) + return; + if (m_videoSinkList.isEmpty() || m_audioSinkList.isEmpty()) + return; + + if (m_videoPlayer->hasVideo()){ + qint64 diff = m_audioPlayer->currentTime() - m_videoPlayer->currentTime(); + if (-allowedOffset > diff || diff > allowedOffset) + m_audioPlayer->seek(m_videoPlayer->currentTime()); + } + + if (isCrossFading() && m_nextVideoPlayer->hasVideo()){ + qint64 diff = m_nextAudioPlayer->currentTime() - m_nextVideoPlayer->currentTime(); + if (-(allowedOffset*2) > diff || diff > (allowedOffset*2)) + m_nextAudioPlayer->seek(m_nextVideoPlayer->currentTime()); + } +} + +void MediaObject::updateRapidly() +{ + updateCurrentTime(); + updateCrossFade(); + updateBufferStatus(); +} + +void MediaObject::setMute(bool mute) +{ + m_mediaObjectAudioNode->setMute(mute); + m_videoPlayer->setMute(mute); + m_nextVideoPlayer->setMute(mute); +} + +void MediaObject::mediaNodeEvent(const MediaNodeEvent *event) +{ + switch (event->type()){ + case MediaNodeEvent::EndConnectionChange: + m_mediaObjectAudioNode->setMute(true); + inspectGraph(); + setupAudioSystem(); + synchAudioVideo(); + checkForError(); + m_mediaObjectAudioNode->setMute(false); + if (m_state == Phonon::PlayingState) + restartAudioVideoTimers(); + break; + case MediaNodeEvent::AudioGraphCannotPlay: + case MediaNodeEvent::AudioGraphInitialized: + if (m_state != Phonon::LoadingState){ + m_mediaObjectAudioNode->setMute(true); + setupAudioSystem(); + updateLipSynch(0); + checkForError(); + m_mediaObjectAudioNode->setMute(false); + } + break; + default: + break; + } +} + +bool MediaObject::event(QEvent *event) +{ + switch (event->type()){ +#if QT_ALLOW_QUICKTIME + case QEvent::User:{ + m_displayLinkMutex.lock(); + m_pendingDisplayLinkEvent = false; + m_displayLinkMutex.unlock(); + updateVideoFrames(); + break; } +#endif + case QEvent::Timer:{ + int timerId = static_cast<QTimerEvent *>(event)->timerId(); + if (timerId == m_rapidTimer) + updateRapidly(); + else if (timerId == m_tickTimer) + emit tick(currentTime()); + else if (timerId == m_videoTimer) + updateVideoFrames(); + else if (timerId == m_audioTimer) + updateAudioBuffers(); + break; } + default: + break; + } + return QObject::event(event); +} + +bool MediaObject::hasInterface(Interface /*interface*/) const +{ + return false; +} + +QVariant MediaObject::interfaceCall(Interface /*interface*/, int /*command*/, const QList<QVariant> &/*arguments*/) +{ + return QVariant(); +} + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#include "moc_mediaobject.cpp" + diff --git a/src/3rdparty/phonon/qt7/mediaobjectaudionode.h b/src/3rdparty/phonon/qt7/mediaobjectaudionode.h new file mode 100644 index 0000000..7939aaa --- /dev/null +++ b/src/3rdparty/phonon/qt7/mediaobjectaudionode.h @@ -0,0 +1,75 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_MEDIAOBJECTAUDIONODE_H +#define Phonon_QT7_MEDIAOBJECTAUDIONODE_H + +#include <QtCore/qnamespace.h> +#include "audionode.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class QuickTimeAudioPlayer; + class AudioMixerAudioNode; + class AudioConnection; + + class MediaObjectAudioNode : public AudioNode + { + public: + MediaObjectAudioNode(QuickTimeAudioPlayer *player1, QuickTimeAudioPlayer *player2); + ~MediaObjectAudioNode(); + + // Overridden section from AudioNode: + void createAndConnectAUNodes(); + void createAudioUnits(); + void setGraph(AudioGraph *audioGraph); + AUNode getOutputAUNode(); + bool fillInStreamSpecification(AudioConnection *connection, ConnectionSide side); + bool setStreamSpecification(AudioConnection *connection, ConnectionSide side); + + void startCrossFade(qint64 duration); + void updateCrossFade(qint64 currentTime); + void cancelCrossFade(); + void setMute(bool mute); + bool isCrossFading(); + + QuickTimeAudioPlayer *m_player1; + QuickTimeAudioPlayer *m_player2; + AudioMixerAudioNode *m_mixer; + + AudioConnection *m_connection1; + AudioConnection *m_connection2; + + float m_fadeDuration; + float m_volume1; + float m_volume2; + float m_mute; + + float applyCurve(float volume); + void updateVolume(); + + void mediaNodeEvent(const MediaNodeEvent *event); + }; + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE +#endif // Phonon_QT7_MEDIAOBJECTAUDIONODE_H diff --git a/src/3rdparty/phonon/qt7/mediaobjectaudionode.mm b/src/3rdparty/phonon/qt7/mediaobjectaudionode.mm new file mode 100644 index 0000000..66d6041 --- /dev/null +++ b/src/3rdparty/phonon/qt7/mediaobjectaudionode.mm @@ -0,0 +1,207 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "mediaobjectaudionode.h" +#include "quicktimeaudioplayer.h" +#include "quicktimevideoplayer.h" +#include "audiomixer.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +MediaObjectAudioNode::MediaObjectAudioNode(QuickTimeAudioPlayer *player1, QuickTimeAudioPlayer *player2) : AudioNode(0, 1) +{ + m_mute = false; + m_player1 = player1; + m_player2 = player2; + m_mixer = new AudioMixerAudioNode(); + + m_connection1 = new AudioConnection(m_player1, 0, m_mixer, 0); + m_connection2 = new AudioConnection(m_player2, 0, m_mixer, 1); + + m_fadeDuration = 0; +} + +MediaObjectAudioNode::~MediaObjectAudioNode() +{ + setGraph(0); + delete m_player1; + delete m_player2; + delete m_mixer; + delete m_connection1; + delete m_connection2; +} + +void MediaObjectAudioNode::createAndConnectAUNodes() +{ + DEBUG_AUDIO_GRAPH("(MediaObjectAudioNode" << int(this) << "createAndConnectAUNodes called)" ) + m_player1->createAndConnectAUNodes(); + m_player2->createAndConnectAUNodes(); + m_mixer->createAndConnectAUNodes(); + + m_connection1->connect(m_audioGraph); + m_connection2->connect(m_audioGraph); +} + +void MediaObjectAudioNode::createAudioUnits() +{ + DEBUG_AUDIO_GRAPH("(MediaObjectAudioNode" << int(this) << "createAudioUnits called)" ) + m_player1->createAudioUnits(); + m_player2->createAudioUnits(); + m_mixer->createAudioUnits(); +} + +void MediaObjectAudioNode::setGraph(AudioGraph *audioGraph) +{ + DEBUG_AUDIO_GRAPH("MediaObjectAudioNode" << int(this) << "is setting graph:" << int(audioGraph)) + m_audioGraph = audioGraph; + m_player1->setGraph(audioGraph); + m_player2->setGraph(audioGraph); + m_mixer->setGraph(audioGraph); +} + +AUNode MediaObjectAudioNode::getOutputAUNode() +{ + return m_mixer->getOutputAUNode(); +} + +bool MediaObjectAudioNode::fillInStreamSpecification(AudioConnection *connection, ConnectionSide side) +{ + if (side == Source){ + DEBUG_AUDIO_STREAM("(MediaObjectAudioNode" << int(this) << "fillInStreamSpecification called, role = source)") + return m_mixer->fillInStreamSpecification(connection, side); + } else { + DEBUG_AUDIO_STREAM("(MediaObjectAudioNode" << int(this) << "fillInStreamSpecification called, role = sink)") + return (m_connection2->updateStreamSpecification() && m_connection1->updateStreamSpecification()); + } +} + +bool MediaObjectAudioNode::setStreamSpecification(AudioConnection *connection, ConnectionSide side) +{ + if (side == Source){ + DEBUG_AUDIO_STREAM("(MediaObjectAudioNode" << int(this) << "setStreamSpecification called, role = source)") + return m_mixer->setStreamSpecification(connection, side); + } + return true; +} + +void MediaObjectAudioNode::setMute(bool mute) +{ + m_mute = mute; + m_mixer->setVolume(m_mute ? 0 : m_volume1, m_connection1->m_sinkInputBus); + m_mixer->setVolume(m_mute ? 0 : m_volume2, m_connection2->m_sinkInputBus); +} + +void MediaObjectAudioNode::updateVolume() +{ + if (m_mute) + return; + + QuickTimeVideoPlayer *player1 = static_cast<QuickTimeAudioPlayer *>(m_connection1->m_sourceAudioNode)->videoPlayer(); + QuickTimeVideoPlayer *player2 = static_cast<QuickTimeAudioPlayer *>(m_connection2->m_sourceAudioNode)->videoPlayer(); + if (player1) + player1->setRelativeVolume(m_volume1); + if (player2) + player2->setRelativeVolume(m_volume2); + + m_mixer->setVolume(m_volume1, m_connection1->m_sinkInputBus); + m_mixer->setVolume(m_volume2, m_connection2->m_sinkInputBus); +} + +void MediaObjectAudioNode::startCrossFade(qint64 duration) +{ + m_fadeDuration = duration; + + // Swap: + AudioConnection *tmp = m_connection1; + m_connection1 = m_connection2; + m_connection2 = tmp; + + // Init volume: + if (m_fadeDuration > 0){ + m_volume1 = 0; + m_volume2 = 1; + } else { + m_volume1 = 1; + m_volume2 = 0; + } + updateVolume(); +} + +float MediaObjectAudioNode::applyCurve(float volume) +{ + float newValue = 0; + if (volume > 0) + newValue = float(0.5f * (2 + log10(volume))); + return newValue; +} + +void MediaObjectAudioNode::updateCrossFade(qint64 currentTime) +{ + // Assume that currentTime starts at 0 and progress. + if (m_fadeDuration > 0){ + float volume = float(currentTime) / float(m_fadeDuration); + if (volume >= 1){ + volume = 1; + m_fadeDuration = 0; + } + m_volume1 = applyCurve(volume); + m_volume2 = 1 - volume; + updateVolume(); + } +} + +bool MediaObjectAudioNode::isCrossFading() +{ + return (m_fadeDuration > 0); +} + +void MediaObjectAudioNode::cancelCrossFade() +{ + m_fadeDuration = 0; + m_volume1 = 1; + m_volume2 = 0; + updateVolume(); +} + +void MediaObjectAudioNode::mediaNodeEvent(const MediaNodeEvent *event) +{ + switch (event->type()){ + case MediaNodeEvent::AudioGraphAboutToBeDeleted: + m_connection1->invalidate(); + m_connection2->invalidate(); + break; + case MediaNodeEvent::AudioGraphCannotPlay: + case MediaNodeEvent::AudioGraphInitialized: + updateVolume(); + break; + default: + break; + } + + m_player1->mediaNodeEvent(event); + m_player2->mediaNodeEvent(event); + m_mixer->mediaNodeEvent(event); +} + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/quicktimeaudioplayer.h b/src/3rdparty/phonon/qt7/quicktimeaudioplayer.h new file mode 100644 index 0000000..25ddb5e --- /dev/null +++ b/src/3rdparty/phonon/qt7/quicktimeaudioplayer.h @@ -0,0 +1,112 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_QUICKTIMEAUDIOPLAYER_H +#define Phonon_QT7_QUICKTIMEAUDIOPLAYER_H + +#include "backendheader.h" + +#ifdef QUICKTIME_C_API_AVAILABLE + #include <QuickTime/QuickTime.h> + #undef check // avoid name clash; +#endif + +#include <phonon/mediasource.h> +#include <Carbon/Carbon.h> +#include <QtCore/QString> +#include "audionode.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class AudioGraph; + class MediaNodeEvent; + class QuickTimeVideoPlayer; + + class QuickTimeAudioPlayer : public AudioNode + { + public: + enum State {Playing, Paused, NoMedia, NoState}; + + QuickTimeAudioPlayer(); + virtual ~QuickTimeAudioPlayer(); + + void play(); + void pause(); + void seek(quint64 milliseconds); + void enableAudio(bool enable); + bool audioEnabled(); + void flush(); + + void setVideoPlayer(QuickTimeVideoPlayer *videoPlayer); + void unsetVideoPlayer(); + + bool hasAudio(); + bool isPlaying(); + void scheduleAudioToGraph(); + long regularTaskFrequency(); + quint64 currentTime(); + QString currentTimeString(); + QuickTimeVideoPlayer *videoPlayer(); + + ComponentDescription getAudioNodeDescription() const; + void initializeAudioUnit(); + bool fillInStreamSpecification(AudioConnection *connection, ConnectionSide side); + void mediaNodeEvent(const MediaNodeEvent *event); + + static bool soundPlayerIsAwailable(); + + private: + void initSoundExtraction(); + void newGraphNotification(); + void allocateSoundSlices(); + void scheduleSoundSlices(); + + State m_state; + QuickTimeVideoPlayer *m_videoPlayer; + +#ifdef QUICKTIME_C_API_AVAILABLE + MovieAudioExtractionRef m_audioExtractionRef; +#endif + + ScheduledAudioSlice *m_sliceList; + AudioChannelLayout *m_audioChannelLayout; + UInt32 m_audioChannelLayoutSize; + AudioStreamBasicDescription m_audioStreamDescription; + + bool m_discrete; + bool m_playerUnitStarted; + bool m_audioExtractionComplete; + bool m_audioEnabled; + bool m_audioUnitIsReset; + + long m_samplesRemaining; + int m_sliceCount; + int m_maxExtractionPacketCount; + + Float64 m_sampleTimeStamp; + quint64 m_startTime; + }; + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_QUICKTIMEAUDIOPLAYER_H diff --git a/src/3rdparty/phonon/qt7/quicktimeaudioplayer.mm b/src/3rdparty/phonon/qt7/quicktimeaudioplayer.mm new file mode 100644 index 0000000..61c97cc --- /dev/null +++ b/src/3rdparty/phonon/qt7/quicktimeaudioplayer.mm @@ -0,0 +1,491 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "quicktimeaudioplayer.h" +#include "quicktimevideoplayer.h" +#include "audiograph.h" +#include "medianodeevent.h" +#include "medianode.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +QuickTimeAudioPlayer::QuickTimeAudioPlayer() : AudioNode(0, 1) +{ + m_state = NoMedia; + m_videoPlayer = 0; + m_audioChannelLayout = 0; + m_sliceList = 0; + m_sliceCount = 30; + m_maxExtractionPacketCount = 4096; + m_audioExtractionComplete = false; + m_audioEnabled = true; + m_samplesRemaining = -1; + m_startTime = 0; + m_sampleTimeStamp = 0; + m_audioUnitIsReset = true; + +#ifdef QUICKTIME_C_API_AVAILABLE + m_audioExtractionRef = 0; +#endif +} + +QuickTimeAudioPlayer::~QuickTimeAudioPlayer() +{ + unsetVideoPlayer(); +} + +void QuickTimeAudioPlayer::unsetVideoPlayer() +{ + if (m_audioUnit){ + OSStatus err = AudioUnitReset(m_audioUnit, kAudioUnitScope_Global, 0); + BACKEND_ASSERT2(err == noErr, "Could not reset audio player unit when unsetting movie", FATAL_ERROR) + } + +#ifdef QUICKTIME_C_API_AVAILABLE + if (m_audioExtractionRef && m_videoPlayer && m_videoPlayer->hasMovie()) + MovieAudioExtractionEnd(m_audioExtractionRef); + m_audioExtractionRef = 0; +#endif + + if (m_audioChannelLayout){ + free(m_audioChannelLayout); + m_audioChannelLayout = 0; + } + + if (m_sliceList){ + for (int i=0; i<m_sliceCount; i++) + free(m_sliceList[i].mBufferList); + free(m_sliceList); + m_sliceList = 0; + } + + m_videoPlayer = 0; + m_audioExtractionComplete = false; + m_samplesRemaining = -1; + m_sampleTimeStamp = 0; + m_state = NoMedia; +} + +void QuickTimeAudioPlayer::enableAudio(bool enable) +{ + // Remember to seek after enabling audio. + if (enable == m_audioEnabled) + return; + + m_audioEnabled = enable; + if (!enable) + flush(); +} + +bool QuickTimeAudioPlayer::audioEnabled() +{ + return m_audioEnabled; +} + +void QuickTimeAudioPlayer::setVideoPlayer(QuickTimeVideoPlayer *videoPlayer) +{ + unsetVideoPlayer(); + if (videoPlayer && videoPlayer->hasMovie()){ + m_videoPlayer = videoPlayer; + initSoundExtraction(); + allocateSoundSlices(); + m_state = Paused; + seek(0); + } +} + +QuickTimeVideoPlayer *QuickTimeAudioPlayer::videoPlayer() +{ + return m_videoPlayer; +} + +void QuickTimeAudioPlayer::scheduleAudioToGraph() +{ + if (!m_videoPlayer || !m_audioEnabled || m_audioExtractionComplete || m_state != Playing) + return; + + // Schedule audio slices, and detect if everything went OK. + // If not, flag the need for another audio system, but let + // the end app know about it: + gClearError(); + scheduleSoundSlices(); + if (gGetErrorType() != NO_ERROR){ + gClearError(); + if (m_audioGraph) + m_audioGraph->setStatusCannotPlay(); + } +} + +void QuickTimeAudioPlayer::flush() +{ + // Empty scheduled audio data, so playback + // will stop. Call seek to refill data again. + if (m_audioUnit){ + m_startTime = currentTime(); + OSStatus err = AudioUnitReset(m_audioUnit, kAudioUnitScope_Global, 0); + BACKEND_ASSERT2(err == noErr, "Could not reset audio player unit on pause", FATAL_ERROR) + m_audioUnitIsReset = true; + } +} + +void QuickTimeAudioPlayer::pause() +{ + m_state = Paused; + flush(); +} + +void QuickTimeAudioPlayer::play() +{ + m_state = Playing; + if (!m_audioEnabled) + return; + if (m_audioUnitIsReset) + seek(m_startTime); + else + scheduleAudioToGraph(); +} + +bool QuickTimeAudioPlayer::isPlaying() +{ + return m_videoPlayer && m_state == Playing; +} + +void QuickTimeAudioPlayer::seek(quint64 milliseconds) +{ + if (!m_videoPlayer || !m_videoPlayer->hasMovie()) + return; + if (milliseconds > m_videoPlayer->duration()) + milliseconds = m_videoPlayer->duration(); + if (!m_audioUnitIsReset && milliseconds == currentTime()) + return; + + m_startTime = milliseconds; + + // Since the graph may be running (advancing time), there is + // no point in seeking if were not going to play immidiatly: + if (m_state != Playing) + return; + if (!m_audioUnit) + return; + if (!m_audioEnabled || !m_videoPlayer->isSeekable()) + return; + + // Reset (and stop playing): + OSStatus err; + if (!m_audioUnitIsReset){ + err = AudioUnitReset(m_audioUnit, kAudioUnitScope_Global, 0); + BACKEND_ASSERT2(err == noErr, "Could not reset audio player unit before seek", FATAL_ERROR) + } + m_sampleTimeStamp = 0; + for (int i = 0; i < m_sliceCount; i++) + m_sliceList[i].mFlags = kScheduledAudioSliceFlag_Complete; + + // Start to play again immidiatly: + AudioTimeStamp timeStamp; + memset(&timeStamp, 0, sizeof(timeStamp)); + timeStamp.mFlags = kAudioTimeStampSampleTimeValid; + timeStamp.mSampleTime = -1; + err = AudioUnitSetProperty(m_audioUnit, + kAudioUnitProperty_ScheduleStartTimeStamp, kAudioUnitScope_Global, + 0, &timeStamp, sizeof(timeStamp)); + BACKEND_ASSERT2(err == noErr, "Could not set schedule start time stamp on audio player unit", FATAL_ERROR) + + // Seek back to 'now' in the movie: + TimeRecord timeRec; + timeRec.scale = m_videoPlayer->timeScale(); + timeRec.base = 0; + timeRec.value.hi = 0; + timeRec.value.lo = (milliseconds / 1000.0f) * timeRec.scale; + +#ifdef QUICKTIME_C_API_AVAILABLE + err = MovieAudioExtractionSetProperty(m_audioExtractionRef, + kQTPropertyClass_MovieAudioExtraction_Movie, + kQTMovieAudioExtractionMoviePropertyID_CurrentTime, + sizeof(TimeRecord), &timeRec); + BACKEND_ASSERT2(err == noErr, "Could not set current time on audio player unit", FATAL_ERROR) +#endif + + float durationLeftSec = float(m_videoPlayer->duration() - milliseconds) / 1000.0f; + m_samplesRemaining = (durationLeftSec > 0) ? (durationLeftSec * m_audioStreamDescription.mSampleRate) : -1; + m_audioExtractionComplete = false; + m_audioUnitIsReset = false; + scheduleAudioToGraph(); + +} + +quint64 QuickTimeAudioPlayer::currentTime() +{ + if (!m_audioUnit){ + if (m_videoPlayer) + return m_videoPlayer->currentTime(); + else + return m_startTime; + } + + Float64 currentUnitTime = getTimeInSamples(kAudioUnitProperty_CurrentPlayTime); + if (currentUnitTime == -1) + currentUnitTime = 0; + + quint64 cTime = quint64(m_startTime + + float(currentUnitTime / float(m_audioStreamDescription.mSampleRate)) * 1000.0f); + return (m_videoPlayer && cTime > m_videoPlayer->duration()) ? m_videoPlayer->duration() : cTime; +} + +QString QuickTimeAudioPlayer::currentTimeString() +{ + return QuickTimeVideoPlayer::timeToString(currentTime()); +} + +bool QuickTimeAudioPlayer::hasAudio() +{ + if (!m_videoPlayer) + return false; + + return m_videoPlayer->hasAudio(); +} + +bool QuickTimeAudioPlayer::soundPlayerIsAwailable() +{ + QuickTimeAudioPlayer player; + ComponentDescription d = player.getAudioNodeDescription(); + return FindNextComponent(0, &d); +} + +ComponentDescription QuickTimeAudioPlayer::getAudioNodeDescription() const +{ + ComponentDescription description; + description.componentType = kAudioUnitType_Generator; + description.componentSubType = kAudioUnitSubType_ScheduledSoundPlayer; + description.componentManufacturer = kAudioUnitManufacturer_Apple; + description.componentFlags = 0; + description.componentFlagsMask = 0; + return description; +} + +void QuickTimeAudioPlayer::initializeAudioUnit() +{ +} + +bool QuickTimeAudioPlayer::fillInStreamSpecification(AudioConnection *connection, ConnectionSide side) +{ + if (!m_videoPlayer){ + if (side == Source) + DEBUG_AUDIO_STREAM("QuickTimeAudioPlayer" << int(this) << "is source, but has no movie to use for stream spec fill.") + return true; + } + + if (side == Source){ + DEBUG_AUDIO_STREAM("QuickTimeAudioPlayer" << int(this) << "is source, and fills in stream spec from movie.") + connection->m_sourceStreamDescription = m_audioStreamDescription; + connection->m_sourceChannelLayout = (AudioChannelLayout *) malloc(m_audioChannelLayoutSize); + memcpy(connection->m_sourceChannelLayout, m_audioChannelLayout, m_audioChannelLayoutSize); + connection->m_sourceChannelLayoutSize = m_audioChannelLayoutSize; + connection->m_hasSourceSpecification = true; + } + return true; +} + +long QuickTimeAudioPlayer::regularTaskFrequency(){ + if (!m_audioEnabled || !m_audioUnit || (m_audioGraph && m_audioGraph->graphCannotPlay())) + return INT_MAX; + + // Calculate how much audio in + // milliseconds our slices can hold: + int packetNeedPerSecond = m_audioStreamDescription.mSampleRate / m_maxExtractionPacketCount; + long bufferTimeLengthSec = float(m_sliceCount) / float(packetNeedPerSecond); + // Make sure we also get some time to fill the + // buffer, so divide the time by two: + return (bufferTimeLengthSec * (1000 / 2)); +} + +void QuickTimeAudioPlayer::initSoundExtraction() +{ +#ifdef QUICKTIME_C_API_AVAILABLE + + // Initilize the extraction: + OSStatus err = noErr; + err = MovieAudioExtractionBegin([m_videoPlayer->qtMovie() quickTimeMovie], 0, &m_audioExtractionRef); + BACKEND_ASSERT2(err == noErr, "Could not start audio extraction on audio player unit", FATAL_ERROR) + m_discrete = false; +#if 0 + // Extract all channels as descrete: + err = MovieAudioExtractionSetProperty(audioExtractionRef, + kQTPropertyClass_MovieAudioExtraction_Movie, + kQTMovieAudioExtractionMoviePropertyID_AllChannelsDiscrete, + sizeof (discrete), + &discrete); + BACKEND_ASSERT2(err == noErr, "Could not set channels discrete on audio player unit", FATAL_ERROR) +#endif + + // Get the size of the audio channel layout (may include offset): + err = MovieAudioExtractionGetPropertyInfo(m_audioExtractionRef, + kQTPropertyClass_MovieAudioExtraction_Audio, + kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, + 0, &m_audioChannelLayoutSize, 0); + BACKEND_ASSERT2(err == noErr, "Could not get channel layout size from audio extraction", FATAL_ERROR) + + // Allocate memory for the layout + m_audioChannelLayout = (AudioChannelLayout *) calloc(1, m_audioChannelLayoutSize); + BACKEND_ASSERT2(m_audioChannelLayout, "Could not allocate memory for channel layout on audio player unit", FATAL_ERROR) + + // Get the layout: + err = MovieAudioExtractionGetProperty(m_audioExtractionRef, + kQTPropertyClass_MovieAudioExtraction_Audio, + kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, + m_audioChannelLayoutSize, m_audioChannelLayout, 0); + BACKEND_ASSERT2(err == noErr, "Could not get channel layout from audio extraction", FATAL_ERROR) + + // Get audio stream description: + err = MovieAudioExtractionGetProperty(m_audioExtractionRef, + kQTPropertyClass_MovieAudioExtraction_Audio, + kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, + sizeof(m_audioStreamDescription), &m_audioStreamDescription, 0); + BACKEND_ASSERT2(err == noErr, "Could not get audio stream description from audio extraction", FATAL_ERROR) + +#endif // QUICKTIME_C_API_AVAILABLE +} + +void QuickTimeAudioPlayer::allocateSoundSlices() +{ +#ifdef QUICKTIME_C_API_AVAILABLE + + // m_sliceList will contain a specified number of ScheduledAudioSlice-s that each can + // carry audio from extraction, and be scheduled for playback at an audio unit. + // Each ScheduledAudioSlice will contain several audio buffers, one for each sound channel. + // Each buffer will carry (at most) a specified number of sound packets, and each packet can + // contain one or more frames. + + // Create a list of ScheduledAudioSlices: + m_sliceList = (ScheduledAudioSlice *) calloc(m_sliceCount, sizeof(ScheduledAudioSlice)); + BACKEND_ASSERT2(m_sliceList, "Could not allocate memory for audio slices", FATAL_ERROR) + bzero(m_sliceList, m_sliceCount * sizeof(ScheduledAudioSlice)); + + // Calculate the size of the different structures needed: + int packetsBufferSize = m_maxExtractionPacketCount * m_audioStreamDescription.mBytesPerPacket; + int channels = m_audioStreamDescription.mChannelsPerFrame; + int audioBufferListSize = int(sizeof(AudioBufferList) + (channels-1) * sizeof(AudioBuffer)); + int mallocSize = audioBufferListSize + (packetsBufferSize * m_audioStreamDescription.mChannelsPerFrame); + + // Round off to Altivec sizes: + packetsBufferSize = int(((packetsBufferSize + 15) / 16) * 16); + audioBufferListSize = int(((audioBufferListSize + 15) / 16) * 16); + + for (int sliceIndex = 0; sliceIndex < m_sliceCount; ++sliceIndex){ + // Create the memory chunk for this audio slice: + AudioBufferList *audioBufferList = (AudioBufferList*) calloc(1, mallocSize); + BACKEND_ASSERT2(audioBufferList, "Could not allocate memory for audio buffer list", FATAL_ERROR) + + // The AudioBufferList contains an AudioBuffer for each channel in the audio stream: + audioBufferList->mNumberBuffers = m_audioStreamDescription.mChannelsPerFrame; + for (uint i = 0; i < audioBufferList->mNumberBuffers; ++i){ + audioBufferList->mBuffers[i].mNumberChannels = 1; + audioBufferList->mBuffers[i].mData = (char *) audioBufferList + audioBufferListSize + (i * packetsBufferSize); + audioBufferList->mBuffers[i].mDataByteSize = packetsBufferSize; + } + + m_sliceList[sliceIndex].mBufferList = audioBufferList; + m_sliceList[sliceIndex].mNumberFrames = m_maxExtractionPacketCount; + m_sliceList[sliceIndex].mTimeStamp.mFlags = kAudioTimeStampSampleTimeValid; + m_sliceList[sliceIndex].mCompletionProcUserData = 0; + m_sliceList[sliceIndex].mCompletionProc = 0; + m_sliceList[sliceIndex].mFlags = kScheduledAudioSliceFlag_Complete; + m_sliceList[sliceIndex].mReserved = 0; + } + +#endif // QUICKTIME_C_API_AVAILABLE +} + +void QuickTimeAudioPlayer::scheduleSoundSlices() +{ +#ifdef QUICKTIME_C_API_AVAILABLE + + PhononAutoReleasePool pool; + // For each completed (or never used) slice, fill and schedule it. + for (int sliceIndex = 0; sliceIndex < m_sliceCount; ++sliceIndex){ + if (m_sliceList[sliceIndex].mFlags & kScheduledAudioSliceFlag_Complete){ + if (m_samplesRemaining == 0) + m_audioExtractionComplete = true; + + if (!m_audioExtractionComplete){ + // Determine how many samples to read: + int samplesCount = m_samplesRemaining; + if ((samplesCount > m_maxExtractionPacketCount) || (samplesCount == -1)) + samplesCount = m_maxExtractionPacketCount; + m_sliceList[sliceIndex].mTimeStamp.mSampleTime = m_sampleTimeStamp; + + // Reset buffer sizes: + int byteSize = samplesCount * m_audioStreamDescription.mBytesPerPacket; + for (uint i = 0; i < m_sliceList[sliceIndex].mBufferList->mNumberBuffers; ++i) + m_sliceList[sliceIndex].mBufferList->mBuffers[i].mDataByteSize = byteSize; + + // Do the extraction: + UInt32 flags = 0; + UInt32 samplesRead = samplesCount; + OSStatus err = MovieAudioExtractionFillBuffer( + m_audioExtractionRef, &samplesRead, m_sliceList[sliceIndex].mBufferList, &flags); + BACKEND_ASSERT2(err == noErr, "Could not fill audio buffers from audio extraction", FATAL_ERROR) + m_audioExtractionComplete = (flags & kQTMovieAudioExtractionComplete); + + // Play the slice: + if (samplesRead != 0 && m_audioUnit != 0){ + m_sliceList[sliceIndex].mNumberFrames = samplesRead; + err = AudioUnitSetProperty(m_audioUnit, + kAudioUnitProperty_ScheduleAudioSlice, kAudioUnitScope_Global, + 0, &m_sliceList[sliceIndex], sizeof(ScheduledAudioSlice)); + BACKEND_ASSERT2(err == noErr, "Could not schedule audio buffers on audio unit", FATAL_ERROR) + } else + m_sliceList[sliceIndex].mFlags = kScheduledAudioSliceFlag_Complete; + + // Move the window: + m_sampleTimeStamp += samplesRead; + if (m_samplesRemaining != -1) + m_samplesRemaining -= samplesRead; + } + } + } + +#endif // QUICKTIME_C_API_AVAILABLE +} + +void QuickTimeAudioPlayer::mediaNodeEvent(const MediaNodeEvent *event) +{ + switch (event->type()){ + case MediaNodeEvent::AudioGraphAboutToBeDeleted: + case MediaNodeEvent::AboutToRestartAudioStream: + case MediaNodeEvent::StartConnectionChange: + m_startTime = currentTime(); + break; + case MediaNodeEvent::AudioGraphInitialized: + case MediaNodeEvent::RestartAudioStreamRequest: + case MediaNodeEvent::EndConnectionChange: + if (m_state == Playing) + seek(m_startTime); + break; + default: + break; + } +} + +}} + +QT_END_NAMESPACE + diff --git a/src/3rdparty/phonon/qt7/quicktimemetadata.h b/src/3rdparty/phonon/qt7/quicktimemetadata.h new file mode 100644 index 0000000..d524183 --- /dev/null +++ b/src/3rdparty/phonon/qt7/quicktimemetadata.h @@ -0,0 +1,67 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_QUICKTIMEMETADATA_H +#define Phonon_QT7_QUICKTIMEMETADATA_H + +#include "backendheader.h" +#include <phonon/mediasource.h> +#include <Carbon/Carbon.h> +#include <QtCore/QString> + +#ifdef QUICKTIME_C_API_AVAILABLE + #include <QuickTime/QuickTime.h> + #undef check // avoid name clash; +#endif + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class QuickTimeVideoPlayer; + class QuickTimeMetaData + { + public: + QuickTimeMetaData(); + virtual ~QuickTimeMetaData(); + + void setVideo(QuickTimeVideoPlayer *videoPlayer); + QMultiMap<QString, QString> metaData(); + + private: + QMultiMap<QString, QString> m_metaData; + bool m_movieChanged; + QuickTimeVideoPlayer *m_videoPlayer; + void readMetaData(); + +#ifdef QUICKTIME_C_API_AVAILABLE + QString stripCopyRightSymbol(const QString &key); + QString convertQuickTimeKeyToUserKey(const QString &key); + OSStatus readMetaValue(QTMetaDataRef, QTMetaDataItem, QTPropertyClass, QTPropertyID, QTPropertyValuePtr *, ByteCount *); + UInt32 getMetaType(QTMetaDataRef metaDataRef, QTMetaDataItem item); + QString getMetaValue(QTMetaDataRef metaDataRef, QTMetaDataItem item, SInt32 id); + void readFormattedData(QTMetaDataRef metaDataRef, OSType format, QMultiMap<QString, QString> &result); +#endif // QUICKTIME_C_API_AVAILABLE + }; + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_QUICKTIMEMETADATA_H diff --git a/src/3rdparty/phonon/qt7/quicktimemetadata.mm b/src/3rdparty/phonon/qt7/quicktimemetadata.mm new file mode 100644 index 0000000..851e707 --- /dev/null +++ b/src/3rdparty/phonon/qt7/quicktimemetadata.mm @@ -0,0 +1,185 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "quicktimemetadata.h" +#include "quicktimevideoplayer.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +QuickTimeMetaData::QuickTimeMetaData() +{ + m_videoPlayer = 0; + m_movieChanged = false; +} + +QuickTimeMetaData::~QuickTimeMetaData() +{ +} + +void QuickTimeMetaData::setVideo(QuickTimeVideoPlayer *videoPlayer) +{ + m_videoPlayer = videoPlayer; + m_movieChanged = true; + m_metaData.clear(); +} + +#ifdef QUICKTIME_C_API_AVAILABLE + +QString QuickTimeMetaData::stripCopyRightSymbol(const QString &key) +{ + return key.right(key.length()-1); +} + +QString QuickTimeMetaData::convertQuickTimeKeyToUserKey(const QString &key) +{ + if (key == QLatin1String("com.apple.quicktime.displayname")) + return QLatin1String("nam"); + else if (key == QLatin1String("com.apple.quicktime.album")) + return QLatin1String("alb"); + else if (key == QLatin1String("com.apple.quicktime.artist")) + return QLatin1String("ART"); + else + return QLatin1String("???"); +} + +OSStatus QuickTimeMetaData::readMetaValue(QTMetaDataRef metaDataRef, QTMetaDataItem item, QTPropertyClass propClass, + QTPropertyID id, QTPropertyValuePtr *value, ByteCount *size) +{ + QTPropertyValueType type; + ByteCount propSize; + UInt32 propFlags; + OSStatus err = QTMetaDataGetItemPropertyInfo(metaDataRef, item, propClass, id, &type, &propSize, &propFlags); + BACKEND_ASSERT3(err == noErr, "Could not read meta data value size", NORMAL_ERROR, err) + + *value = malloc(propSize); + + err = QTMetaDataGetItemProperty(metaDataRef, item, propClass, id, propSize, *value, size); + BACKEND_ASSERT3(err == noErr, "Could not read meta data value", NORMAL_ERROR, err) + + if (type == 'code' || type == 'itsk' || type == 'itlk') { + // convert from native endian to big endian + OSTypePtr pType = (OSTypePtr)*value; + *pType = EndianU32_NtoB(*pType); + } + + return err; +} + +UInt32 QuickTimeMetaData::getMetaType(QTMetaDataRef metaDataRef, QTMetaDataItem item) +{ + QTPropertyValuePtr value = 0; + ByteCount ignore = 0; + OSStatus err = readMetaValue( + metaDataRef, item, kPropertyClass_MetaDataItem, kQTMetaDataItemPropertyID_DataType, &value, &ignore); + BACKEND_ASSERT3(err == noErr, "Could not read meta data type", NORMAL_ERROR, 0) + UInt32 type = *((UInt32 *) value); + if (value) + free(value); + return type; +} + +QString QuickTimeMetaData::getMetaValue(QTMetaDataRef metaDataRef, QTMetaDataItem item, SInt32 id) +{ + QTPropertyValuePtr value = 0; + ByteCount size = 0; + OSStatus err = readMetaValue(metaDataRef, item, kPropertyClass_MetaDataItem, id, &value, &size); + BACKEND_ASSERT3(err == noErr, "Could not read meta data item", NORMAL_ERROR, QString()) + BACKEND_ASSERT3(value != 0, "Could not read meta data item", NORMAL_ERROR, QString()) + + QString string; + UInt32 dataType = getMetaType(metaDataRef, item); + switch (dataType){ + case kQTMetaDataTypeUTF8: + case kQTMetaDataTypeMacEncodedText: + string = PhononCFString::toQString(CFStringCreateWithBytes(0, (UInt8*)value, size, kCFStringEncodingUTF8, false)); + break; + case kQTMetaDataTypeUTF16BE: + string = PhononCFString::toQString(CFStringCreateWithBytes(0, (UInt8*)value, size, kCFStringEncodingUTF16BE, false)); + break; + default: + break; + } + + if (value) + free(value); + return string; +} + +void QuickTimeMetaData::readFormattedData(QTMetaDataRef metaDataRef, OSType format, QMultiMap<QString, QString> &result) +{ + QTMetaDataItem item = kQTMetaDataItemUninitialized; + OSStatus err = QTMetaDataGetNextItem(metaDataRef, format, item, kQTMetaDataKeyFormatWildcard, 0, 0, &item); + while (err == noErr){ + QString key = getMetaValue(metaDataRef, item, kQTMetaDataItemPropertyID_Key); + if (format == kQTMetaDataStorageFormatQuickTime) + key = convertQuickTimeKeyToUserKey(key); + else + key = stripCopyRightSymbol(key); + + if (!result.contains(key)){ + QString val = getMetaValue(metaDataRef, item, kQTMetaDataItemPropertyID_Value); + result.insert(key, val); + } + err = QTMetaDataGetNextItem(metaDataRef, format, item, kQTMetaDataKeyFormatWildcard, 0, 0, &item); + } +} + +#endif // QUICKTIME_C_API_AVAILABLE + +void QuickTimeMetaData::readMetaData() +{ + if (!m_videoPlayer) + return; + QMultiMap<QString, QString> metaMap; + +#ifdef QUICKTIME_C_API_AVAILABLE + QTMetaDataRef metaDataRef; + OSStatus err = QTCopyMovieMetaData([m_videoPlayer->qtMovie() quickTimeMovie], &metaDataRef); + BACKEND_ASSERT2(err == noErr, "Could not read QuickTime meta data", NORMAL_ERROR) + + readFormattedData(metaDataRef, kQTMetaDataStorageFormatUserData, metaMap); + readFormattedData(metaDataRef, kQTMetaDataStorageFormatQuickTime, metaMap); + readFormattedData(metaDataRef, kQTMetaDataStorageFormatiTunes, metaMap); +#else + NSString *name = [m_videoPlayer->qtMovie() attributeForKey:@"QTMovieDisplayNameAttribute"]; + metaMap.insert(QLatin1String("nam"), QString::fromUtf8([name UTF8String])); +#endif // QUICKTIME_C_API_AVAILABLE + + m_metaData.insert(QLatin1String("ARTIST"), metaMap.value(QLatin1String("ART"))); + m_metaData.insert(QLatin1String("ALBUM"), metaMap.value(QLatin1String("alb"))); + m_metaData.insert(QLatin1String("TITLE"), metaMap.value(QLatin1String("nam"))); + m_metaData.insert(QLatin1String("DATE"), metaMap.value(QLatin1String("day"))); + m_metaData.insert(QLatin1String("GENRE"), metaMap.value(QLatin1String("gnre"))); + m_metaData.insert(QLatin1String("TRACKNUMBER"), metaMap.value(QLatin1String("trk"))); + m_metaData.insert(QLatin1String("DESCRIPTION"), metaMap.value(QLatin1String("des"))); +} + +QMultiMap<QString, QString> QuickTimeMetaData::metaData() +{ + if (m_videoPlayer && m_videoPlayer->hasMovie() && m_movieChanged) + readMetaData(); + return m_metaData; +} + +}} + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/quicktimestreamreader.h b/src/3rdparty/phonon/qt7/quicktimestreamreader.h new file mode 100644 index 0000000..0e7590c --- /dev/null +++ b/src/3rdparty/phonon/qt7/quicktimestreamreader.h @@ -0,0 +1,71 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_QUICKTIMESTREAMREADER_H +#define Phonon_QT7_QUICKTIMESTREAMREADER_H + +#include <phonon/mediasource.h> +#include <phonon/streaminterface.h> +#include <QtCore/QReadWriteLock> + +#ifndef QT_MAC_USE_COCOA +#include <QuickTime/Movies.h> +#endif + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class QuickTimeStreamReader : public QObject, Phonon::StreamInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::StreamInterface) + + public: + QuickTimeStreamReader(const Phonon::MediaSource &source); + ~QuickTimeStreamReader(); + + int readData(long offset, long size, void *data); + bool readAllData(); + QByteArray *pointerToData(); + void writeData(const QByteArray &data); + void endOfData(); + void setStreamSize(qint64 newSize); + qint64 streamSize() const; + void setStreamSeekable(bool s); + bool streamSeekable() const; + void setCurrentPos(qint64 pos); + qint64 currentPos() const; + int currentBufferSize() const; +#ifndef QT_MAC_USE_COCOA + Movie movieRef(); +#endif + + QByteArray m_buffer; + mutable QReadWriteLock m_lock; + bool m_seekable; + qint64 m_pos; + qint64 m_size; + }; + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_QUICKTIMESTREAMREADER_H diff --git a/src/3rdparty/phonon/qt7/quicktimestreamreader.mm b/src/3rdparty/phonon/qt7/quicktimestreamreader.mm new file mode 100644 index 0000000..7131616 --- /dev/null +++ b/src/3rdparty/phonon/qt7/quicktimestreamreader.mm @@ -0,0 +1,137 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "backendheader.h" +#include "quicktimestreamreader.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +QuickTimeStreamReader::QuickTimeStreamReader(const Phonon::MediaSource &source) +{ + connectToSource(source); +} + +QuickTimeStreamReader::~QuickTimeStreamReader() +{ +} + +bool QuickTimeStreamReader::readAllData() +{ + int oldSize = m_buffer.size(); + while (m_buffer.size() < m_size){ + needData(); + if (oldSize == currentBufferSize()) + BACKEND_ASSERT3(oldSize != currentBufferSize(), + "Could not create new movie from IO stream. Not enough free memory to preload the whole movie.", + FATAL_ERROR, false) + oldSize = m_buffer.size(); + } + return true; +} + +QByteArray *QuickTimeStreamReader::pointerToData() +{ + return &m_buffer; +} + +int QuickTimeStreamReader::readData(long offset, long size, void *data) +{ +// QReadLocker readLocker(&m_lock); + if (streamSize() != 1 && offset + size > streamSize()){ + size = streamSize() - offset; + } + + if (currentPos() - currentBufferSize() != offset) + setCurrentPos(offset); + + int oldSize = currentBufferSize(); + while (currentBufferSize() < int(size)) { + needData(); + if (oldSize == currentBufferSize()) + break; + oldSize = currentBufferSize(); + } + + int bytesRead = qMin(currentBufferSize(), int(size)); +// QWriteLocker writeLocker(&m_lock); + qMemCopy(data, m_buffer.data(), bytesRead); + m_buffer = m_buffer.mid(bytesRead); + + return bytesRead; +} + +void QuickTimeStreamReader::writeData(const QByteArray &data) +{ + QWriteLocker locker(&m_lock); + m_pos += data.size(); + m_buffer += data; +} + +void QuickTimeStreamReader::endOfData() +{ +} + +void QuickTimeStreamReader::setStreamSize(qint64 newSize) +{ + m_size = newSize; +} + +qint64 QuickTimeStreamReader::streamSize() const +{ + return m_size; +} + +void QuickTimeStreamReader::setStreamSeekable(bool s) +{ + m_seekable = s; +} + +bool QuickTimeStreamReader::streamSeekable() const +{ + return m_seekable; +} + +void QuickTimeStreamReader::setCurrentPos(qint64 pos) +{ + QWriteLocker locker(&m_lock); + m_pos = pos; + seekStream(pos); + m_buffer.clear(); +} + +qint64 QuickTimeStreamReader::currentPos() const +{ + return m_pos; +} + +int QuickTimeStreamReader::currentBufferSize() const +{ + QReadLocker locker(&m_lock); + return m_buffer.size(); +} + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE + +#include "moc_quicktimestreamreader.cpp" + diff --git a/src/3rdparty/phonon/qt7/quicktimevideoplayer.h b/src/3rdparty/phonon/qt7/quicktimevideoplayer.h new file mode 100644 index 0000000..3fa815e --- /dev/null +++ b/src/3rdparty/phonon/qt7/quicktimevideoplayer.h @@ -0,0 +1,175 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_QUICKTIMEVIDEOPLAYER_H +#define Phonon_QT7_QUICKTIMEVIDEOPLAYER_H + +#include "backendheader.h" + +#import <QTKit/QTDataReference.h> +#import <QTKit/QTMovie.h> + +#include <phonon/mediasource.h> +#include <Carbon/Carbon.h> +#include <QtCore/QString> +#include <QtOpenGL/QGLPixelBuffer> +#include "videoframe.h" + +QT_BEGIN_NAMESPACE + +class QGLContext; + +namespace Phonon +{ +namespace QT7 +{ + class QuickTimeStreamReader; + class VideoRenderWidgetQTMovieView; + + class QuickTimeVideoPlayer : QObject + { + public: + enum StateEnum { + Playing = 0x1, + Paused = 0x2, + NoMedia = 0x4, + }; + Q_DECLARE_FLAGS(State, StateEnum); + + QuickTimeVideoPlayer(); + virtual ~QuickTimeVideoPlayer(); + + void setMediaSource(const MediaSource &source); + MediaSource mediaSource() const; + void unsetVideo(); + + void play(); + void pause(); + void seek(quint64 milliseconds); + + bool videoFrameChanged(); + CVOpenGLTextureRef currentFrameAsCVTexture(); + GLuint currentFrameAsGLTexture(); + void *currentFrameAsCIImage(); + QImage currentFrameAsQImage(); + void releaseImageCache(); + QRect videoRect() const; + + quint64 duration() const; + quint64 currentTime() const; + long timeScale() const; + float staticFps(); + QString currentTimeString(); + + void setColors(qreal brightness = 0, qreal contrast = 1, qreal hue = 0, qreal saturation = 1); + void setMasterVolume(float volume); + void setRelativeVolume(float volume); + void setVolume(float masterVolume, float relativeVolume); + void setMute(bool mute); + void enableAudio(bool enable); + bool audioEnabled(); + bool setAudioDevice(int id); + void setPlaybackRate(float rate); + QTMovie *qtMovie() const; + + float playbackRate() const; + float prefferedPlaybackRate() const; + + QuickTimeVideoPlayer::State state() const; + + bool hasAudio() const; + bool hasVideo() const; + bool hasMovie() const; + bool canPlayMedia() const; + bool isPlaying() const; + bool isSeekable() const; + bool isDrmProtected() const; + bool isDrmAuthorized() const; + + bool preRollMovie(qint64 startTime = 0); + float percentageLoaded(); + quint64 timeLoaded(); + + static QString timeToString(quint64 ms); + + // Help functions when drawing to more that one widget in cocoa 64: + void *m_primaryRenderingTarget; + void setPrimaryRenderingTarget(NSObject *target); + + void *primaryRenderingCIImage(); + void setPrimaryRenderingCIImage(void *ciImage); + + private: + QTMovie *m_QTMovie; + State m_state; + QGLPixelBuffer *m_QImagePixelBuffer; + + CVOpenGLTextureRef m_cachedCVTextureRef; + QImage m_cachedQImage; + + bool m_playbackRateSat; + bool m_isDrmProtected; + bool m_isDrmAuthorized; + bool m_mute; + bool m_audioEnabled; + bool m_hasVideo; + float m_masterVolume; + float m_relativeVolume; + float m_playbackRate; + float m_staticFps; + quint64 m_currentTime; + MediaSource m_mediaSource; + + void *m_primaryRenderingCIImage; + qreal m_brightness; + qreal m_contrast; + qreal m_hue; + qreal m_saturation; + +#ifdef QUICKTIME_C_API_AVAILABLE + QTVisualContextRef m_visualContext; +#endif + VideoFrame m_currentFrame; + QuickTimeStreamReader *m_streamReader; + + void createVisualContext(); + void openMovieFromCurrentMediaSource(); + void openMovieFromDataRef(QTDataReference *dataRef); + void openMovieFromFile(); + void openMovieFromUrl(); + void openMovieFromStream(); + void openMovieFromData(QByteArray *data, char *fileType); + void openMovieFromDataGuessType(QByteArray *data); + QString mediaSourcePath(); + bool codecExistsAccordingToSuffix(const QString &fileName); + + void setError(NSError *error); + bool errorOccured(); + void readProtection(); + void calculateStaticFps(); + void checkIfVideoAwailable(); + bool movieNotLoaded(); + void waitStatePlayable(); + }; + + Q_DECLARE_OPERATORS_FOR_FLAGS(QuickTimeVideoPlayer::State); + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_QUICKTIMEVIDEOPLAYER_H diff --git a/src/3rdparty/phonon/qt7/quicktimevideoplayer.mm b/src/3rdparty/phonon/qt7/quicktimevideoplayer.mm new file mode 100644 index 0000000..de7c6ed --- /dev/null +++ b/src/3rdparty/phonon/qt7/quicktimevideoplayer.mm @@ -0,0 +1,1020 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "quicktimevideoplayer.h" +#include "mediaobject.h" +#include "videowidget.h" +#include "audiodevice.h" +#include "quicktimestreamreader.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QEventLoop> +#include <QtCore/QFileInfo> +#include <QtCore/QUrl> +#include <QtOpenGL/QGLContext> + +#import <QTKit/QTTrack.h> +#import <QTKit/QTMedia.h> +#import <QuartzCore/CIContext.h> +#import <QuartzCore/CIFilter.h> + +#ifdef QUICKTIME_C_API_AVAILABLE + #include <QuickTime/QuickTime.h> + #undef check // avoid name clash; + #include <AGL/agl.h> +#endif + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +// Defined in videowidget.cpp: +QGLWidget *PhononSharedQGLWidget(); + +QuickTimeVideoPlayer::QuickTimeVideoPlayer() : QObject(0) +{ + m_state = NoMedia; + m_mediaSource = MediaSource(); + m_QTMovie = 0; + m_streamReader = 0; + m_playbackRate = 1.0f; + m_masterVolume = 1.0f; + m_relativeVolume = 1.0f; + m_currentTime = 0; + m_mute = false; + m_audioEnabled = false; + m_hasVideo = false; + m_staticFps = 0; + m_playbackRateSat = false; + m_isDrmProtected = false; + m_isDrmAuthorized = true; + m_primaryRenderingTarget = 0; + m_primaryRenderingCIImage = 0; + m_QImagePixelBuffer = 0; + m_cachedCVTextureRef = 0; + +#ifdef QUICKTIME_C_API_AVAILABLE + OSStatus err = EnterMovies(); + BACKEND_ASSERT2(err == noErr, "Could not initialize QuickTime", FATAL_ERROR) + createVisualContext(); +#endif +} + +QuickTimeVideoPlayer::~QuickTimeVideoPlayer() +{ + PhononAutoReleasePool pool; + unsetVideo(); + [(NSObject*)m_primaryRenderingTarget release]; + m_primaryRenderingTarget = 0; +#ifdef QUICKTIME_C_API_AVAILABLE + if (m_visualContext) + CFRelease(m_visualContext); +#endif +} + +void QuickTimeVideoPlayer::releaseImageCache() +{ + if (m_cachedCVTextureRef){ + CVOpenGLTextureRelease(m_cachedCVTextureRef); + m_cachedCVTextureRef = 0; + } + m_cachedQImage = QImage(); +} + +void QuickTimeVideoPlayer::createVisualContext() +{ +#ifdef QUICKTIME_C_API_AVAILABLE + PhononSharedQGLWidget()->makeCurrent(); + + PhononAutoReleasePool pool; + CGLContextObj cglContext = CGLGetCurrentContext(); + NSOpenGLPixelFormat *nsglPixelFormat = [NSOpenGLView defaultPixelFormat]; + CGLPixelFormatObj cglPixelFormat = static_cast<CGLPixelFormatObj>([nsglPixelFormat CGLPixelFormatObj]); + BACKEND_ASSERT2(cglContext, "Could not get current CoreVideo GL context (OpenGL)", FATAL_ERROR) + BACKEND_ASSERT2(cglPixelFormat, "Could not get current CoreVideo pixel format (OpenGL)", FATAL_ERROR) + + CFTypeRef keys[] = { kQTVisualContextWorkingColorSpaceKey }; + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CFDictionaryRef textureContextAttributes = CFDictionaryCreate(kCFAllocatorDefault, + (const void **)keys, + (const void **)&colorSpace, 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + OSStatus err = QTOpenGLTextureContextCreate(kCFAllocatorDefault, cglContext, + cglPixelFormat, textureContextAttributes, &m_visualContext); + CFRelease(textureContextAttributes); + BACKEND_ASSERT2(err == noErr, "Could not create visual context (OpenGL)", FATAL_ERROR) +#endif // QUICKTIME_C_API_AVAILABLE +} + +bool QuickTimeVideoPlayer::videoFrameChanged() +{ + if (!m_QTMovie || !m_hasVideo) + return false; + +#ifdef QUICKTIME_C_API_AVAILABLE + if (m_primaryRenderingTarget) + return true; + if (!m_visualContext) + return false; + + QTVisualContextTask(m_visualContext); + bool changed = QTVisualContextIsNewImageAvailable(m_visualContext, 0); + if (changed) + releaseImageCache(); + return changed; + +#elif defined(QT_MAC_USE_COCOA) + return true; + +#else + return false; +#endif +} + +CVOpenGLTextureRef QuickTimeVideoPlayer::currentFrameAsCVTexture() +{ +#ifdef QUICKTIME_C_API_AVAILABLE + if (!m_visualContext) + return 0; + if (!m_cachedCVTextureRef){ + OSStatus err = QTVisualContextCopyImageForTime(m_visualContext, 0, 0, &m_cachedCVTextureRef); + BACKEND_ASSERT3(err == noErr, "Could not copy image for time in QuickTime player", FATAL_ERROR, 0) + } + return m_cachedCVTextureRef; + +#else + return 0; +#endif +} + +QImage QuickTimeVideoPlayer::currentFrameAsQImage() +{ + if (!m_cachedQImage.isNull()) + return m_cachedQImage; + +#ifdef QUICKTIME_C_API_AVAILABLE + QGLContext *prevContext = const_cast<QGLContext *>(QGLContext::currentContext()); + CVOpenGLTextureRef texture = currentFrameAsCVTexture(); + GLenum target = CVOpenGLTextureGetTarget(texture); + GLfloat lowerLeft[2], lowerRight[2], upperRight[2], upperLeft[2]; + + if (!m_QImagePixelBuffer){ + m_QImagePixelBuffer = new QGLPixelBuffer(videoRect().size(), QGLFormat::defaultFormat(), PhononSharedQGLWidget()); + m_QImagePixelBuffer->makeCurrent(); + glEnable(target); + glDisable(GL_BLEND); + glDisable(GL_CULL_FACE); + } else { + m_QImagePixelBuffer->makeCurrent(); + } + + CVOpenGLTextureGetCleanTexCoords(texture, upperLeft, upperRight, lowerRight, lowerLeft); + glBindTexture(target, CVOpenGLTextureGetName(texture)); + glBegin(GL_QUADS); + glTexCoord2f(lowerLeft[0], lowerLeft[1]); + glVertex2i(-1, 1); + glTexCoord2f(lowerRight[0], lowerRight[1]); + glVertex2i(1, 1); + glTexCoord2f(upperRight[0], upperRight[1]); + glVertex2i(1, -1); + glTexCoord2f(upperLeft[0], upperLeft[1]); + glVertex2i(-1, -1); + glEnd(); + + m_cachedQImage = m_QImagePixelBuffer->toImage(); + // Because of QuickTime, m_QImagePixelBuffer->doneCurrent() will fail. + // So we store, and restore, the context our selves: + prevContext->makeCurrent(); + return m_cachedQImage; +#else + CIImage *img = (CIImage *)currentFrameAsCIImage(); + if (!img) + return QImage(); + + NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc] initWithCIImage:img]; + CGRect bounds = [img extent]; + QImage qImg([bitmap bitmapData], bounds.size.width, bounds.size.height, QImage::Format_ARGB32); + m_cachedQImage = qImg.rgbSwapped(); + [bitmap release]; + [img release]; + return m_cachedQImage; +#endif +} + +void QuickTimeVideoPlayer::setPrimaryRenderingCIImage(void *ciImage) +{ + [(CIImage *)m_primaryRenderingCIImage release]; + m_primaryRenderingCIImage = ciImage; + [(CIImage *)m_primaryRenderingCIImage retain]; +} + +void QuickTimeVideoPlayer::setPrimaryRenderingTarget(NSObject *target) +{ + [(NSObject*)m_primaryRenderingTarget release]; + m_primaryRenderingTarget = target; + [(NSObject*)m_primaryRenderingTarget retain]; +} + +void *QuickTimeVideoPlayer::primaryRenderingCIImage() +{ + return m_primaryRenderingCIImage; +} + +void *QuickTimeVideoPlayer::currentFrameAsCIImage() +{ + if (!m_QTMovie) + return 0; + +#if defined(QT_MAC_USE_COCOA) + if (m_primaryRenderingCIImage){ + CIImage *img = (CIImage *)m_primaryRenderingCIImage; + if (m_brightness || m_contrast || m_saturation){ + CIFilter *colorFilter = [CIFilter filterWithName:@"CIColorControls"]; + [colorFilter setValue:[NSNumber numberWithFloat:m_brightness] forKey:@"inputBrightness"]; + [colorFilter setValue:[NSNumber numberWithFloat:(m_contrast < 1) ? m_contrast : 1 + ((m_contrast-1)*3)] forKey:@"inputContrast"]; + [colorFilter setValue:[NSNumber numberWithFloat:m_saturation] forKey:@"inputSaturation"]; + [colorFilter setValue:img forKey:@"inputImage"]; + img = [colorFilter valueForKey:@"outputImage"]; + } + if (m_hue){ + CIFilter *colorFilter = [CIFilter filterWithName:@"CIHueAdjust"]; + [colorFilter setValue:[NSNumber numberWithFloat:(m_hue * 3.14)] forKey:@"inputAngle"]; + [colorFilter setValue:img forKey:@"inputImage"]; + img = [colorFilter valueForKey:@"outputImage"]; + } + return [img retain]; + } +#endif + +#ifdef QUICKTIME_C_API_AVAILABLE + CVOpenGLTextureRef cvImg = currentFrameAsCVTexture(); + CIImage *img = [[CIImage alloc] initWithCVImageBuffer:cvImg]; + return img; +#else + return 0; +#endif +} + +GLuint QuickTimeVideoPlayer::currentFrameAsGLTexture() +{ + CIImage *img = (CIImage *)currentFrameAsCIImage(); + if (!img) + return 0; + + NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc] initWithCIImage:img]; + GLuint texName = 0; + glPixelStorei(GL_UNPACK_ROW_LENGTH, [bitmap pixelsWide]); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glGenTextures(1, &texName); + glBindTexture(GL_TEXTURE_RECTANGLE_EXT, texName); + glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + int samplesPerPixel = [bitmap samplesPerPixel]; + if (![bitmap isPlanar] && (samplesPerPixel == 3 || samplesPerPixel == 4)){ + glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, + samplesPerPixel == 4 ? GL_RGBA8 : GL_RGB8, + [bitmap pixelsWide], [bitmap pixelsHigh], + 0, samplesPerPixel == 4 ? GL_RGBA : GL_RGB, + GL_UNSIGNED_BYTE, [bitmap bitmapData]); + } else { + // Handle other bitmap formats. + } + + [bitmap release]; + [img release]; + return texName; +} + +void QuickTimeVideoPlayer::setMasterVolume(float volume) +{ + setVolume(volume, m_relativeVolume); +} + +void QuickTimeVideoPlayer::setRelativeVolume(float volume) +{ + setVolume(m_masterVolume, volume); +} + +void QuickTimeVideoPlayer::setVolume(float masterVolume, float relativeVolume) +{ + m_masterVolume = masterVolume; + m_relativeVolume = relativeVolume; + if (!m_QTMovie || !m_audioEnabled || m_mute) + return; + [m_QTMovie setVolume:(m_masterVolume * m_relativeVolume)]; +} + +void QuickTimeVideoPlayer::setMute(bool mute) +{ + m_mute = mute; + if (!m_QTMovie || m_state != Playing || !m_audioEnabled) + return; + + // Work-around bug that happends if you set/unset mute + // before movie is playing, and audio is not played + // through graph. Then audio is delayed. + [m_QTMovie setMuted:mute]; + [m_QTMovie setVolume:(mute ? 0 : m_masterVolume * m_relativeVolume)]; +} + +void QuickTimeVideoPlayer::enableAudio(bool enable) +{ + m_audioEnabled = enable; + if (!m_QTMovie || m_state != Playing) + return; + + // Work-around bug that happends if you set/unset mute + // before movie is playing, and audio is not played + // through graph. Then audio is delayed. + [m_QTMovie setMuted:(!enable || m_mute)]; + [m_QTMovie setVolume:((!enable || m_mute) ? 0 : m_masterVolume * m_relativeVolume)]; +} + +bool QuickTimeVideoPlayer::audioEnabled() +{ + return m_audioEnabled; +} + +bool QuickTimeVideoPlayer::setAudioDevice(int id) +{ + if (!m_QTMovie) + return false; + +#ifdef QUICKTIME_C_API_AVAILABLE + // The following code will not work for some media codecs that + // typically mingle audio/video frames (e.g mpeg). + CFStringRef idString = PhononCFString::toCFStringRef(AudioDevice::deviceUID(id)); + QTAudioContextRef context; + QTAudioContextCreateForAudioDevice(kCFAllocatorDefault, idString, 0, &context); + OSStatus err = SetMovieAudioContext([m_QTMovie quickTimeMovie], context); + CFRelease(context); + if (err != noErr) + return false; + return true; +#else + Q_UNUSED(id); + return false; +#endif +} + +void QuickTimeVideoPlayer::setColors(qreal brightness, qreal contrast, qreal hue, qreal saturation) +{ + if (!m_QTMovie) + return; + + // 0 is default value for the colors + // in phonon, so adjust scale: + contrast += 1; + saturation += 1; + + if (m_brightness == brightness + && m_contrast == contrast + && m_hue == hue + && m_saturation == saturation) + return; + + m_brightness = brightness; + m_contrast = contrast; + m_hue = hue; + m_saturation = saturation; +#ifdef QUICKTIME_C_API_AVAILABLE + Float32 value; + value = brightness; + SetMovieVisualBrightness([m_QTMovie quickTimeMovie], value, 0); + value = contrast; + SetMovieVisualContrast([m_QTMovie quickTimeMovie], value, 0); + value = hue; + SetMovieVisualHue([m_QTMovie quickTimeMovie], value, 0); + value = saturation; + SetMovieVisualSaturation([m_QTMovie quickTimeMovie], value, 0); +#endif + releaseImageCache(); +} + +QRect QuickTimeVideoPlayer::videoRect() const +{ + if (!m_QTMovie) + return QRect(); + + PhononAutoReleasePool pool; + NSSize size = [[m_QTMovie attributeForKey:@"QTMovieCurrentSizeAttribute"] sizeValue]; + return QRect(0, 0, size.width, size.height); +} + +void QuickTimeVideoPlayer::unsetVideo() +{ + if (!m_QTMovie) + return; + + [m_QTMovie release]; + m_QTMovie = 0; + delete m_streamReader; + m_streamReader = 0; + m_currentTime = 0; + m_state = NoMedia; + m_isDrmProtected = false; + m_isDrmAuthorized = true; + m_hasVideo = false; + m_staticFps = 0; + m_mediaSource = MediaSource(); + [(CIImage *)m_primaryRenderingCIImage release]; + m_primaryRenderingCIImage = 0; + delete m_QImagePixelBuffer; + m_QImagePixelBuffer = 0; + releaseImageCache(); +} + +QuickTimeVideoPlayer::State QuickTimeVideoPlayer::state() const +{ + return m_state; +} + +quint64 QuickTimeVideoPlayer::timeLoaded() +{ + if (!m_QTMovie) + return 0; +#ifdef QUICKTIME_C_API_AVAILABLE + TimeValue value; + GetMaxLoadedTimeInMovie([m_QTMovie quickTimeMovie], &value); + quint64 loaded = static_cast<quint64>(float(value) / float(GetMovieTimeScale([m_QTMovie quickTimeMovie])) * 1000.0f); + return (loaded == INT_MAX) ? 0 : loaded; +#else + return 0; +#endif +} + +float QuickTimeVideoPlayer::percentageLoaded() +{ + if (!m_QTMovie || !isSeekable()) + return 0; +#ifdef QUICKTIME_C_API_AVAILABLE + TimeValue loaded; + GetMaxLoadedTimeInMovie([m_QTMovie quickTimeMovie], &loaded); + float duration = GetMovieDuration([m_QTMovie quickTimeMovie]); + return duration ? float(loaded) / duration : 0; +#else + return 0; +#endif +} + +void QuickTimeVideoPlayer::waitStatePlayable() +{ +#if defined(QT_MAC_USE_COCOA) + long state = [[m_QTMovie attributeForKey:@"QTMovieLoadStateAttribute"] longValue]; + while (state != QTMovieLoadStateError && state < QTMovieLoadStatePlayable) + state = [[m_QTMovie attributeForKey:@"QTMovieLoadStateAttribute"] longValue]; +#elif defined(QUICKTIME_C_API_AVAILABLE) + long state = GetMovieLoadState([m_QTMovie quickTimeMovie]); + while (state != kMovieLoadStateError && state < kMovieLoadStatePlayable){ + MoviesTask(0, 0); + state = GetMovieLoadState([m_QTMovie quickTimeMovie]); + } +#endif +} + +bool QuickTimeVideoPlayer::movieNotLoaded() +{ + if (!m_QTMovie) + return true; + +#if defined(QT_MAC_USE_COCOA) + long state = [[m_QTMovie attributeForKey:@"QTMovieLoadStateAttribute"] longValue]; + return state == QTMovieLoadStateError; +#elif defined(QUICKTIME_C_API_AVAILABLE) + long state = GetMovieLoadState([m_QTMovie quickTimeMovie]); + return state == kMovieLoadStateError; +#endif +} + +void QuickTimeVideoPlayer::setError(NSError *error) +{ + if (!error) + return; + QString desc = QString::fromUtf8([[error localizedDescription] UTF8String]); + if (desc == "The file is not a movie file.") + desc = QLatin1String("Could not decode media source."); + else if (desc == "A necessary data reference could not be resolved."){ + if (codecExistsAccordingToSuffix(mediaSourcePath())) + desc = QLatin1String("Could not locate media source."); + else + desc = QLatin1String("Could not decode media source."); + } else if (desc == "You do not have sufficient permissions for this operation.") + desc = QLatin1String("Could not open media source."); + SET_ERROR(desc, FATAL_ERROR) +} + +bool QuickTimeVideoPlayer::errorOccured() +{ + if (gGetErrorType() != NO_ERROR){ + return true; + } else if (movieNotLoaded()){ + SET_ERROR("Could not open media source.", FATAL_ERROR) + return true; + } + return false; +} + +bool QuickTimeVideoPlayer::codecExistsAccordingToSuffix(const QString &fileName) +{ + PhononAutoReleasePool pool; + NSArray *fileTypes = [QTMovie movieFileTypes:QTIncludeAllTypes]; + for (uint i=0; i<[fileTypes count]; ++i){ + NSString *type = [fileTypes objectAtIndex:i]; + QString formattedType = QString::fromUtf8([type UTF8String]); + formattedType.remove('\'').remove('.'); + if (fileName.endsWith(QChar('.') + formattedType, Qt::CaseInsensitive)) + return true; + } + return false; +} + +void QuickTimeVideoPlayer::setMediaSource(const MediaSource &mediaSource) +{ + PhononAutoReleasePool pool; + unsetVideo(); + m_mediaSource = mediaSource; + if (mediaSource.type() == MediaSource::Empty || mediaSource.type() == MediaSource::Invalid){ + m_state = NoMedia; + return; + } + openMovieFromCurrentMediaSource(); + if (errorOccured()){ + unsetVideo(); + return; + } + +#ifdef QUICKTIME_C_API_AVAILABLE + if (m_visualContext) + SetMovieVisualContext([m_QTMovie quickTimeMovie], m_visualContext); +#endif + + waitStatePlayable(); + if (errorOccured()){ + unsetVideo(); + return; + } + + readProtection(); + preRollMovie(); + if (errorOccured()){ + unsetVideo(); + return; + } + + if (!m_playbackRateSat) + m_playbackRate = prefferedPlaybackRate(); + checkIfVideoAwailable(); + calculateStaticFps(); + enableAudio(m_audioEnabled); + setMute(m_mute); + setVolume(m_masterVolume, m_relativeVolume); + pause(); +} + +void QuickTimeVideoPlayer::openMovieFromCurrentMediaSource() +{ + switch (m_mediaSource.type()){ + case MediaSource::LocalFile: + openMovieFromFile(); + break; + case MediaSource::Url: + openMovieFromUrl(); + break; + case MediaSource::Disc: + CASE_UNSUPPORTED("Could not open media source.", FATAL_ERROR) + break; + case MediaSource::Stream: + openMovieFromStream(); + break; + case MediaSource::Empty: + case MediaSource::Invalid: + break; + } +} + +QString QuickTimeVideoPlayer::mediaSourcePath() +{ + switch (m_mediaSource.type()){ + case MediaSource::LocalFile:{ + QFileInfo fileInfo(m_mediaSource.fileName()); + return fileInfo.isSymLink() ? fileInfo.symLinkTarget() : fileInfo.canonicalFilePath(); + break;} + case MediaSource::Url: + return m_mediaSource.url().toEncoded(); + break; + default: + break; + } + return QString(); +} + +void QuickTimeVideoPlayer::openMovieFromDataRef(QTDataReference *dataRef) +{ + PhononAutoReleasePool pool; + NSDictionary *attr = [NSDictionary dictionaryWithObjectsAndKeys: + dataRef, QTMovieDataReferenceAttribute, + [NSNumber numberWithBool:YES], QTMovieOpenAsyncOKAttribute, + [NSNumber numberWithBool:YES], QTMovieIsActiveAttribute, + [NSNumber numberWithBool:YES], QTMovieResolveDataRefsAttribute, + [NSNumber numberWithBool:YES], QTMovieDontInteractWithUserAttribute, + nil]; + + NSError *err = 0; + m_QTMovie = [[QTMovie movieWithAttributes:attr error:&err] retain]; + if (err){ + [m_QTMovie release]; + m_QTMovie = 0; + setError(err); + } +} + +void QuickTimeVideoPlayer::openMovieFromData(QByteArray *data, char *fileType) +{ + PhononAutoReleasePool pool; + NSString *type = [NSString stringWithUTF8String:fileType]; + NSData *nsData = [NSData dataWithBytesNoCopy:data->data() length:data->size() freeWhenDone:NO]; + QTDataReference *dataRef = [QTDataReference dataReferenceWithReferenceToData:nsData name:type MIMEType:@""]; + openMovieFromDataRef(dataRef); +} + +void QuickTimeVideoPlayer::openMovieFromDataGuessType(QByteArray *data) +{ + // It turns out to be better to just try the standard file types rather + // than using e.g [QTMovie movieFileTypes:QTIncludeCommonTypes]. Some + // codecs *think* they can decode the stream, and crash... +#define TryOpenMovieWithCodec(type) gClearError(); \ + openMovieFromData(data, "."type); \ + if (m_QTMovie) return; + + TryOpenMovieWithCodec("avi"); + TryOpenMovieWithCodec("mp4"); + TryOpenMovieWithCodec("m4p"); + TryOpenMovieWithCodec("m1s"); + TryOpenMovieWithCodec("mp3"); + TryOpenMovieWithCodec("mpeg"); + TryOpenMovieWithCodec("mov"); + TryOpenMovieWithCodec("ogg"); + TryOpenMovieWithCodec("wav"); + TryOpenMovieWithCodec("wmv"); +#undef TryOpenMovieWithCodec(type) +} + +void QuickTimeVideoPlayer::openMovieFromFile() +{ + NSString *nsFilename = (NSString *)PhononCFString::toCFStringRef(mediaSourcePath()); + QTDataReference *dataRef = [QTDataReference dataReferenceWithReferenceToFile:nsFilename]; + openMovieFromDataRef(dataRef); +} + +void QuickTimeVideoPlayer::openMovieFromUrl() +{ + PhononAutoReleasePool pool; + NSString *urlString = (NSString *)PhononCFString::toCFStringRef(mediaSourcePath()); + NSURL *url = [NSURL URLWithString: urlString]; + QTDataReference *dataRef = [QTDataReference dataReferenceWithReferenceToURL:url]; + openMovieFromDataRef(dataRef); +} + +void QuickTimeVideoPlayer::openMovieFromStream() +{ + m_streamReader = new QuickTimeStreamReader(m_mediaSource); + if (!m_streamReader->readAllData()) + return; + openMovieFromDataGuessType(m_streamReader->pointerToData()); +} + +MediaSource QuickTimeVideoPlayer::mediaSource() const +{ + return m_mediaSource; +} + +QTMovie *QuickTimeVideoPlayer::qtMovie() const +{ + return m_QTMovie; +} + +void QuickTimeVideoPlayer::setPlaybackRate(float rate) +{ + PhononAutoReleasePool pool; + m_playbackRateSat = true; + m_playbackRate = rate; + if (m_QTMovie) + [m_QTMovie setRate:m_playbackRate]; +} + +float QuickTimeVideoPlayer::playbackRate() const +{ + return m_playbackRate; +} + +quint64 QuickTimeVideoPlayer::currentTime() const +{ + if (!m_QTMovie || m_state == Paused) + return m_currentTime; + + PhononAutoReleasePool pool; + QTTime qtTime = [m_QTMovie currentTime]; + quint64 t = static_cast<quint64>(float(qtTime.timeValue) / float(qtTime.timeScale) * 1000.0f); + const_cast<QuickTimeVideoPlayer *>(this)->m_currentTime = t; + return m_currentTime; +} + +long QuickTimeVideoPlayer::timeScale() const +{ + if (!m_QTMovie) + return 0; + + PhononAutoReleasePool pool; + return [[m_QTMovie attributeForKey:@"QTMovieTimeScaleAttribute"] longValue]; +} + +float QuickTimeVideoPlayer::staticFps() +{ + return m_staticFps; +} + +void QuickTimeVideoPlayer::calculateStaticFps() +{ + if (!m_hasVideo){ + m_staticFps = 0; + return; + } + +#ifdef QT_ALLOW_QUICKTIME + Boolean isMpeg = false; + Track videoTrack = GetMovieIndTrackType([m_QTMovie quickTimeMovie], 1, + FOUR_CHAR_CODE('vfrr'), // 'vfrr' means: has frame rate + movieTrackCharacteristic | movieTrackEnabledOnly); + Media media = GetTrackMedia(videoTrack); + MediaHandler mediaH = GetMediaHandler(media); + MediaHasCharacteristic(mediaH, FOUR_CHAR_CODE('mpeg'), &isMpeg); + + if (isMpeg){ + MHInfoEncodedFrameRateRecord frameRate; + Size frameRateSize = sizeof(frameRate); + MediaGetPublicInfo(mediaH, kMHInfoEncodedFrameRate, &frameRate, &frameRateSize); + m_staticFps = float(Fix2X(frameRate.encodedFrameRate)); + } else { + Media media = GetTrackMedia(videoTrack); + long sampleCount = GetMediaSampleCount(media); + TimeValue64 duration = GetMediaDisplayDuration(media); + TimeValue64 timeScale = GetMediaTimeScale(media); + m_staticFps = float((double)sampleCount * (double)timeScale / (double)duration); + } +#else + m_staticFps = 30.0f; +#endif +} + +QString QuickTimeVideoPlayer::timeToString(quint64 ms) +{ + int sec = ms/1000; + int min = sec/60; + int hour = min/60; + return QString(QLatin1String("%1:%2:%3:%4")).arg(hour%60).arg(min%60).arg(sec%60).arg(ms%1000); +} + +QString QuickTimeVideoPlayer::currentTimeString() +{ + return timeToString(currentTime()); +} + +quint64 QuickTimeVideoPlayer::duration() const +{ + if (!m_QTMovie) + return 0; + + PhononAutoReleasePool pool; + QTTime qtTime = [m_QTMovie duration]; + return static_cast<quint64>(float(qtTime.timeValue) / float(qtTime.timeScale) * 1000.0f); +} + +void QuickTimeVideoPlayer::play() +{ + if (!canPlayMedia()) + return; + + PhononAutoReleasePool pool; + m_state = Playing; + enableAudio(m_audioEnabled); + setMute(m_mute); + [m_QTMovie setRate:m_playbackRate]; +} + +void QuickTimeVideoPlayer::pause() +{ + if (!canPlayMedia()) + return; + + PhononAutoReleasePool pool; + currentTime(); + m_state = Paused; + + if (isSeekable()) + [m_QTMovie setRate:0]; + else // pretend to be paused: + [m_QTMovie setMuted:0]; +} + +void QuickTimeVideoPlayer::seek(quint64 milliseconds) +{ + if (!canPlayMedia() || !isSeekable() || milliseconds == currentTime()) + return; + if (milliseconds > duration()) + milliseconds = duration(); + + PhononAutoReleasePool pool; + QTTime newQTTime = [m_QTMovie currentTime]; + newQTTime.timeValue = (milliseconds / 1000.0f) * newQTTime.timeScale; + [m_QTMovie setCurrentTime:newQTTime]; + + // The movie might not have been able to seek + // to the exact point we told it to. So set + // the current time according to what the movie says: + newQTTime = [m_QTMovie currentTime]; + m_currentTime = static_cast<quint64> + (float(newQTTime.timeValue) / float(newQTTime.timeScale) * 1000.0f); + + if (m_state == Paused){ + // We need (for reasons unknown) to task + // the movie twize to make sure that + // a subsequent call to frameAsCvTexture + // returns the correct frame: +#ifdef QUICKTIME_C_API_AVAILABLE + MoviesTask(0, 0); + MoviesTask(0, 0); +#elif defined(QT_MAC_USE_COCOA) + qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers); +#endif + } +} + +bool QuickTimeVideoPlayer::canPlayMedia() const +{ + if (!m_QTMovie) + return false; + return m_isDrmAuthorized; +} + +bool QuickTimeVideoPlayer::isPlaying() const +{ + return m_state == Playing; +} + +bool QuickTimeVideoPlayer::isSeekable() const +{ + return canPlayMedia() && (duration()-1) != INT_MAX; +} + +float QuickTimeVideoPlayer::prefferedPlaybackRate() const +{ + if (!m_QTMovie) + return 0; + + PhononAutoReleasePool pool; + return [[m_QTMovie attributeForKey:@"QTMoviePreferredRateAttribute"] floatValue]; +} + +#ifdef QUICKTIME_C_API_AVAILABLE +void MoviePrePrerollCompleteCallBack(Movie /*theMovie*/, OSErr /*thePrerollErr*/, void * /*userData*/) +{ + // QuickTimeVideoPlayer *player = static_cast<QuickTimeVideoPlayer *>(userData); +} +#endif + +bool QuickTimeVideoPlayer::preRollMovie(qint64 startTime) +{ + if (!canPlayMedia()) + return false; + +#ifdef QUICKTIME_C_API_AVAILABLE + if (PrePrerollMovie([m_QTMovie quickTimeMovie], startTime, FloatToFixed(m_playbackRate), + 0 /*MoviePrePrerollCompleteCallBack*/, this) != noErr) // No callback means wait (synch) + return false; + + if (PrerollMovie([m_QTMovie quickTimeMovie], startTime, FloatToFixed(m_playbackRate)) != noErr) + return false; + + return true; +#else + Q_UNUSED(startTime); + return false; +#endif +} + +bool QuickTimeVideoPlayer::hasAudio() const +{ + if (!m_QTMovie) + return false; + + PhononAutoReleasePool pool; + return [[m_QTMovie attributeForKey:@"QTMovieHasAudioAttribute"] boolValue] == YES; +} + +bool QuickTimeVideoPlayer::hasVideo() const +{ + return m_hasVideo; +} + +bool QuickTimeVideoPlayer::hasMovie() const +{ + return m_QTMovie != 0; +} + +void QuickTimeVideoPlayer::checkIfVideoAwailable() +{ + PhononAutoReleasePool pool; + m_hasVideo = [[m_QTMovie attributeForKey:@"QTMovieHasVideoAttribute"] boolValue] == YES; +} + +bool QuickTimeVideoPlayer::isDrmProtected() const +{ + return m_isDrmProtected; +} + +bool QuickTimeVideoPlayer::isDrmAuthorized() const +{ + return m_isDrmAuthorized; +} +/* +void QuickTimeVideoPlayer::movieCodecIsMPEG() +{ + NSArray *tracks = [m_QTMovie tracks]; + for (QTTrack *track in tracks) + if ([[track media] hasCharacteristic:QTMediaTypeMPEG]) + return true; + return false; +} +*/ + +static void QtGetTrackProtection(QTTrack *track, bool &isDrmProtected, bool &isDrmAuthorized) +{ + isDrmProtected = false; + isDrmAuthorized = true; + +#ifdef QUICKTIME_C_API_AVAILABLE + QTMedia *media = [track media]; + MediaHandler mediaHandler = GetMediaHandler([media quickTimeMedia]); + if (mediaHandler){ + // Regardless, skip message boxes pointing to iTunes regarding DRM: + Boolean boolFalse = false; + QTSetComponentProperty(mediaHandler, + kQTPropertyClass_DRM, kQTDRMPropertyID_InteractWithUser, + sizeof(boolFalse), &boolFalse); + + // Check track: + Boolean value; + OSStatus err = QTGetComponentProperty(mediaHandler, + kQTPropertyClass_DRM, kQTDRMPropertyID_IsProtected, + sizeof(value), &value, 0); + isDrmProtected = (err == noErr) ? bool(value) : false; + err = QTGetComponentProperty(mediaHandler, + kQTPropertyClass_DRM, kQTDRMPropertyID_IsAuthorized, + sizeof(value), &value, 0); + isDrmAuthorized = (err == noErr) ? bool(value) : true; + } +#else + Q_UNUSED(track); +#endif // QUICKTIME_C_API_AVAILABLE +} + +void QuickTimeVideoPlayer::readProtection() +{ + m_isDrmProtected = false; + m_isDrmAuthorized = true; + + NSArray *tracks = [m_QTMovie tracks]; + for (uint i=0; i<[tracks count]; ++i){ + QTTrack *track = [tracks objectAtIndex:i]; + bool isDrmProtected = false; + bool isDrmAuthorized = true; + QtGetTrackProtection(track, isDrmProtected, isDrmAuthorized); + if (isDrmProtected) + m_isDrmProtected = true; + if (!isDrmAuthorized) + m_isDrmAuthorized = false; + } +} + +}} + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/videoeffect.h b/src/3rdparty/phonon/qt7/videoeffect.h new file mode 100644 index 0000000..9189a6a --- /dev/null +++ b/src/3rdparty/phonon/qt7/videoeffect.h @@ -0,0 +1,63 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_VIDEOEFFECT_H +#define Phonon_QT7_VIDEOEFFECT_H + +#include <QtCore/QList> +#include <QtCore/QString> +#include <phonon/effectinterface.h> +#include "medianode.h" +#include "videoframe.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + class EffectParameter; + +namespace QT7 +{ + class MediaNodeEvent; + + class VideoEffect : public MediaNode, public Phonon::EffectInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::EffectInterface) + + public: + VideoEffect(int effectId, QObject *parent); + virtual ~VideoEffect(); + + QList<EffectParameter> parameters() const; + QVariant parameterValue(const EffectParameter &) const; + void setParameterValue(const EffectParameter &, const QVariant &newValue); + + void updateVideo(VideoFrame &frame); + + protected: + void mediaNodeEvent(const MediaNodeEvent *event); + + private: + int effectId; + void *ciFilter; + QString filterName; + }; +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE +#endif // Phonon_QT7_VIDEOEFFECT_H diff --git a/src/3rdparty/phonon/qt7/videoeffect.mm b/src/3rdparty/phonon/qt7/videoeffect.mm new file mode 100644 index 0000000..c33a919 --- /dev/null +++ b/src/3rdparty/phonon/qt7/videoeffect.mm @@ -0,0 +1,76 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "videoeffect.h" +#include "backendheader.h" +#include "objc_help.h" +#include <phonon/effect.h> +#include <phonon/effectparameter.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +VideoEffect::VideoEffect(int effectId, QObject *parent) : MediaNode(VideoSink | VideoSource, 0, parent), effectId(effectId) +{ + ciFilter = objc_createCiFilter(effectId); + if (ciFilter) + filterName = objc_getCiFilterInfo()->filterDisplayNames[effectId]; +} + +VideoEffect::~VideoEffect() +{ + if (ciFilter) + objc_releaseCiFilter(ciFilter); +} + +QList<EffectParameter> VideoEffect::parameters() const +{ + IMPLEMENTED; + return objc_getCiFilterParameterInfo(ciFilter).parameters; +} + +QVariant VideoEffect::parameterValue(const EffectParameter ¶meter) const +{ + IMPLEMENTED; + return objc_getCiFilterParameter(ciFilter, parameter.id()); +} + +void VideoEffect::setParameterValue(const EffectParameter ¶meter, const QVariant &newValue) +{ + IMPLEMENTED; + objc_setCiFilterParameter(ciFilter, parameter.id(), newValue); +} + +void VideoEffect::mediaNodeEvent(const MediaNodeEvent */*event*/) +{ +} + +void VideoEffect::updateVideo(VideoFrame &frame){ + frame.applyCoreImageFilter(ciFilter); + MediaNode::updateVideo(frame); +} + +}} + +QT_END_NAMESPACE + +#include "moc_videoeffect.cpp" + diff --git a/src/3rdparty/phonon/qt7/videoframe.h b/src/3rdparty/phonon/qt7/videoframe.h new file mode 100644 index 0000000..5069607 --- /dev/null +++ b/src/3rdparty/phonon/qt7/videoframe.h @@ -0,0 +1,98 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_VIDEOFRAME_H +#define Phonon_QT7_VIDEOFRAME_H + +#import <QuartzCore/CVOpenGLTexture.h> +#import <AppKit/NSImage.h> +#undef check // avoid name clash; + +#include <QtCore/QRect> +#include <QtGui/QPainter> +#include <QtGui/QImage> + +QT_BEGIN_NAMESPACE + +class QGLContext; + +namespace Phonon +{ +namespace QT7 +{ + class QuickTimeVideoPlayer; + class QNSBitmapImage; + + class VideoFrame + { + public: + VideoFrame(); + VideoFrame(QuickTimeVideoPlayer *videoPlayer); + VideoFrame(const VideoFrame& frame); + void operator=(const VideoFrame& frame); + ~VideoFrame(); + + + CVOpenGLTextureRef cachedCVTexture() const; + void *cachedCIImage() const; + GLuint glTextureRef() const; + + void drawQImage(QPainter *p, const QRect &rect) const; + void drawCIImage(const CGRect &rect, float opacity = 1.0f) const; + void drawCIImage(const QRect &rect, float opacity = 1.0f) const; + void drawCVTexture(const QRect &rect, float opacity = 1.0f) const; + void drawGLTexture(const QRect &rect, float opacity = 1.0f) const; + + void applyCoreImageFilter(void *filter); + void setColors(qreal brightness, qreal contrast, qreal hue, qreal saturation); + bool hasColorAdjustments(); + void setBaseOpacity(qreal opacity); + void setBackgroundFrame(const VideoFrame &frame); + + bool isEmpty(); + QRect frameRect() const; + QuickTimeVideoPlayer *videoPlayer(); + + void retain() const; + void release() const; + + static CGRect QRectToCGRect(const QRect & qrect); + + private: + CVOpenGLTextureRef m_cachedCVTextureRef; + void *m_cachedCIImage; + QImage m_cachedQImage; + NSBitmapImageRep *m_cachedNSBitmap; + + QuickTimeVideoPlayer *m_videoPlayer; + VideoFrame *m_backgroundFrame; + + qreal m_brightness; + qreal m_contrast; + qreal m_hue; + qreal m_saturation; + qreal m_opacity; + + void initMembers(); + void copyMembers(const VideoFrame& frame); + void invalidateImage() const; + }; + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE +#endif // Phonon_QT7_VIDEOFRAME_H diff --git a/src/3rdparty/phonon/qt7/videoframe.mm b/src/3rdparty/phonon/qt7/videoframe.mm new file mode 100644 index 0000000..7b67b5e --- /dev/null +++ b/src/3rdparty/phonon/qt7/videoframe.mm @@ -0,0 +1,398 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "videoframe.h" +#include "quicktimevideoplayer.h" +#import <QuartzCore/CIFilter.h> +#import <QuartzCore/CIContext.h> + +//#define CACHE_CV_TEXTURE + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + + VideoFrame::VideoFrame() + { + initMembers(); + } + + VideoFrame::VideoFrame(QuickTimeVideoPlayer *videoPlayer) + { + initMembers(); + m_videoPlayer = videoPlayer; + } + + VideoFrame::VideoFrame(const VideoFrame& frame) + { + copyMembers(frame); + retain(); + } + + void VideoFrame::operator=(const VideoFrame& frame) + { + if (this == &frame) + return; + + release(); + copyMembers(frame); + retain(); + } + + void VideoFrame::initMembers() + { + m_cachedCVTextureRef = 0; + m_cachedCIImage = 0; + m_cachedNSBitmap = 0; + m_videoPlayer = 0; + m_brightness = 0; + m_contrast = 0; + m_hue = 0; + m_saturation = 0; + m_opacity = 1; + m_backgroundFrame = 0; + } + + void VideoFrame::copyMembers(const VideoFrame& frame) + { +#ifdef CACHE_CV_TEXTURE + m_cachedCVTextureRef = frame.m_cachedCVTextureRef; +#endif + m_cachedCIImage = frame.m_cachedCIImage; + m_cachedQImage = frame.m_cachedQImage; + m_cachedNSBitmap = frame.m_cachedNSBitmap; + m_videoPlayer = frame.m_videoPlayer; + m_brightness = frame.m_brightness; + m_contrast = frame.m_contrast; + m_hue = frame.m_hue; + m_saturation = frame.m_saturation; + m_opacity = frame.m_opacity; + m_backgroundFrame = frame.m_backgroundFrame; + } + + VideoFrame::~VideoFrame() + { + release(); + } + + QuickTimeVideoPlayer *VideoFrame::videoPlayer() + { + return m_videoPlayer; + } + + void VideoFrame::setBackgroundFrame(const VideoFrame &frame) + { + m_backgroundFrame = new VideoFrame(frame); + } + + QRect VideoFrame::frameRect() const + { + return m_videoPlayer->videoRect(); + } + + CVOpenGLTextureRef VideoFrame::cachedCVTexture() const + { +#ifdef CACHE_CV_TEXTURE + if (!m_cachedCVTextureRef && m_videoPlayer){ + m_videoPlayer->setColors(m_brightness, m_contrast, m_hue, m_saturation); + (const_cast<VideoFrame *>(this))->m_cachedCVTextureRef = m_videoPlayer->currentFrameAsCVTexture(); + CVOpenGLTextureRetain((const_cast<VideoFrame *>(this))->m_cachedCVTextureRef); + } + return m_cachedCVTextureRef; +#else + if (m_videoPlayer){ + m_videoPlayer->setColors(m_brightness, m_contrast, m_hue, m_saturation); + return m_videoPlayer->currentFrameAsCVTexture(); + } + return 0; +#endif + } + + void *VideoFrame::cachedCIImage() const + { + if (!m_cachedCIImage && m_videoPlayer){ + m_videoPlayer->setColors(m_brightness, m_contrast, m_hue, m_saturation); + (const_cast<VideoFrame *>(this))->m_cachedCIImage = m_videoPlayer->currentFrameAsCIImage(); + } + return m_cachedCIImage; + } + + GLuint VideoFrame::glTextureRef() const + { + return CVOpenGLTextureGetName(cachedCVTexture()); + } + + void VideoFrame::setColors(qreal brightness, qreal contrast, qreal hue, qreal saturation) + { + if (m_backgroundFrame) + m_backgroundFrame->setColors(brightness, contrast, hue, saturation); + if (m_brightness == brightness + && m_contrast == contrast + && m_hue == hue + && m_saturation == saturation) + return; + + m_brightness = brightness; + m_contrast = contrast; + m_hue = hue; + m_saturation = saturation; + + invalidateImage(); + } + + CGRect VideoFrame::QRectToCGRect(const QRect & qrect) + { + CGRect cgrect; + cgrect.origin.x = qrect.x(); + cgrect.origin.y = qrect.y() + qrect.height(); + cgrect.size.width = qrect.width(); + cgrect.size.height = -qrect.height(); + return cgrect; + } + + bool VideoFrame::hasColorAdjustments() + { + return (m_brightness || m_contrast || m_saturation || m_hue); + } + + void VideoFrame::setBaseOpacity(qreal opacity) + { + m_opacity = opacity; + } + + void VideoFrame::drawQImage(QPainter *p, const QRect &rect) const + { + if (!m_videoPlayer) + return; +#ifdef QUICKTIME_C_API_AVAILABLE + if (m_cachedQImage.isNull()){ + m_videoPlayer->setColors(m_brightness, m_contrast, m_hue, m_saturation); + (const_cast<VideoFrame *>(this))->m_cachedQImage = m_videoPlayer->currentFrameAsQImage(); + } +#else + // Since cocoa-64 doesn't give us OpenGL textures directly, the process of converting + // CIImges into QImages takes time. We could still call m_videoPlayer->currentFrameAsQImage(), + // but because of bitmap memory management issues, and the fact that we need to swap red and + // blue, we can optimize the process a bit here since we are going to draw immidiatly: + CIImage *img = (CIImage*)cachedCIImage(); + if (!img) + return; + + if (!m_cachedNSBitmap){ + (const_cast<VideoFrame *>(this))->m_cachedNSBitmap = + [[NSBitmapImageRep alloc] initWithCIImage:img]; + CGRect bounds = [img extent]; + int w = bounds.size.width; + int h = bounds.size.height; + (const_cast<VideoFrame *>(this))->m_cachedQImage = + QImage([m_cachedNSBitmap bitmapData], w, h, QImage::Format_ARGB32); + // Swap red and blue (same as QImage::rgbSwapped, but without copy) + for (int i=0; i<h; ++i) { + uint *p = (uint*) m_cachedQImage.scanLine(i); + uint *end = p + w; + while (p < end) { + *p = ((*p << 16) & 0xff0000) | ((*p >> 16) & 0xff) | (*p & 0xff00ff00); + p++; + } + } + } +#endif + p->drawImage(rect, m_cachedQImage); + } + + void VideoFrame::drawCIImage(const QRect &rect, float opacity) const + { + drawCIImage(QRectToCGRect(rect), opacity); + } + + void VideoFrame::drawCIImage(const CGRect &rect, float opacity) const + { + Q_UNUSED(opacity); + CIImage *img = (CIImage *) cachedCIImage(); + if (!img) + return; + + CIContext* ciContext = [[NSGraphicsContext currentContext] CIContext]; + [ciContext drawImage:img inRect:rect fromRect:[img extent]]; + } + + void VideoFrame::drawCVTexture(const QRect &rect, float opacity) const + { + if (!m_videoPlayer) + return; + if (m_backgroundFrame) + m_backgroundFrame->drawCVTexture(rect, opacity); + + CVOpenGLTextureRef texRef = cachedCVTexture(); + if (!texRef) + return; + + glPushMatrix(); + glDisable(GL_CULL_FACE); + GLenum target = CVOpenGLTextureGetTarget(texRef); + glEnable(target); + + opacity *= m_opacity; + if (opacity < 1){ + glEnable(GL_BLEND); + glColor4f(1, 1, 1, opacity); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } else { + glColor3f(1, 1, 1); + glDisable(GL_BLEND); + } + + glBindTexture(target, CVOpenGLTextureGetName(texRef)); + glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + GLfloat lowerLeft[2], lowerRight[2], upperRight[2], upperLeft[2]; + CVOpenGLTextureGetCleanTexCoords(texRef, lowerLeft, lowerRight, upperRight, upperLeft); + + glBegin(GL_QUADS); + glTexCoord2f(lowerLeft[0], lowerLeft[1]); + glVertex2i(rect.topLeft().x(), rect.topLeft().y()); + glTexCoord2f(lowerRight[0], lowerRight[1]); + glVertex2i(rect.topRight().x() + 1, rect.topRight().y()); + glTexCoord2f(upperRight[0], upperRight[1]); + glVertex2i(rect.bottomRight().x() + 1, rect.bottomRight().y() + 1); + glTexCoord2f(upperLeft[0], upperLeft[1]); + glVertex2i(rect.bottomLeft().x(), rect.bottomLeft().y() + 1); + glEnd(); + glPopMatrix(); + } + + void VideoFrame::drawGLTexture(const QRect &rect, float opacity) const + { + if (!m_videoPlayer) + return; + if (m_backgroundFrame) + m_backgroundFrame->drawGLTexture(rect, opacity); + + GLuint texture = m_videoPlayer->currentFrameAsGLTexture(); + if (!texture) + return; + + glPushMatrix(); + glDisable(GL_CULL_FACE); + glEnable(GL_TEXTURE_RECTANGLE_EXT); + + opacity *= m_opacity; + if (opacity < 1){ + glEnable(GL_BLEND); + glColor4f(1, 1, 1, opacity); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } else { + glColor3f(1, 1, 1); + glDisable(GL_BLEND); + } + + glBindTexture(GL_TEXTURE_RECTANGLE_EXT, texture); + glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + QRect videoRect = m_videoPlayer->videoRect(); + GLfloat lowerLeft[2], lowerRight[2], upperRight[2], upperLeft[2]; + lowerLeft[0] = 0; + lowerLeft[1] = videoRect.height(); + lowerRight[0] = videoRect.width(); + lowerRight[1] = videoRect.height(); + upperRight[0] = videoRect.width(); + upperRight[1] = 0; + upperLeft[0] = 0; + upperLeft[1] = 0; + + glBegin(GL_QUADS); + glTexCoord2f(lowerLeft[0], lowerLeft[1]); + glVertex2i(rect.topLeft().x(), rect.topLeft().y()); + glTexCoord2f(lowerRight[0], lowerRight[1]); + glVertex2i(rect.topRight().x() + 1, rect.topRight().y()); + glTexCoord2f(upperRight[0], upperRight[1]); + glVertex2i(rect.bottomRight().x() + 1, rect.bottomRight().y() + 1); + glTexCoord2f(upperLeft[0], upperLeft[1]); + glVertex2i(rect.bottomLeft().x(), rect.bottomLeft().y() + 1); + glEnd(); + glPopMatrix(); + + + // FOR NOW. FREE THE TEXTURE: + glDeleteTextures(1, &texture); + } + + bool VideoFrame::isEmpty() + { + return (m_videoPlayer == 0); + } + + void VideoFrame::invalidateImage() const + { +#ifdef CACHE_CV_TEXTURE + if (m_cachedCVTextureRef){ + CVOpenGLTextureRelease(m_cachedCVTextureRef); + (const_cast<VideoFrame *>(this))->m_cachedCVTextureRef = 0; + } +#endif + if (m_cachedCIImage){ + [(CIImage *) m_cachedCIImage release]; + (const_cast<VideoFrame *>(this))->m_cachedCIImage = 0; + } + if (m_cachedNSBitmap){ + [m_cachedNSBitmap release]; + (const_cast<VideoFrame *>(this))->m_cachedNSBitmap = 0; + } + (const_cast<VideoFrame *>(this))-> m_cachedQImage = QImage(); + } + + void VideoFrame::retain() const + { +#ifdef CACHE_CV_TEXTURE + if (m_cachedCVTextureRef) + CVOpenGLTextureRetain(m_cachedCVTextureRef); +#endif + if (m_cachedCIImage) + [(CIImage *) m_cachedCIImage retain]; + if (m_backgroundFrame) + m_backgroundFrame->retain(); + if (m_cachedNSBitmap) + [m_cachedNSBitmap retain]; + } + + void VideoFrame::release() const + { +#ifdef CACHE_CV_TEXTURE + if (m_cachedCVTextureRef){ + CVOpenGLTextureRelease(m_cachedCVTextureRef); + (const_cast<VideoFrame *>(this))->m_cachedCVTextureRef = 0; + } +#endif + if (m_cachedCIImage) + [(CIImage *) m_cachedCIImage release]; + if (m_backgroundFrame) + m_backgroundFrame->release(); + if (m_cachedNSBitmap) + [m_cachedNSBitmap release]; + + (const_cast<VideoFrame *>(this))->m_backgroundFrame = 0; + (const_cast<VideoFrame *>(this))->m_cachedCIImage = 0; + (const_cast<VideoFrame *>(this))->m_cachedNSBitmap = 0; + } + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/videowidget.h b/src/3rdparty/phonon/qt7/videowidget.h new file mode 100644 index 0000000..8d084ec --- /dev/null +++ b/src/3rdparty/phonon/qt7/videowidget.h @@ -0,0 +1,71 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_VIDEOWIDGET_H +#define Phonon_QT7_VIDEOWIDGET_H + +#include <QtGui/QPaintEngine> +#include <phonon/videowidgetinterface.h> +#include "medianode.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class MediaNodeEvent; + class VideoRenderWidget; + + class VideoWidget : public MediaNode, public Phonon::VideoWidgetInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::VideoWidgetInterface) + + public: + VideoWidget(QObject *parent); + virtual ~VideoWidget(); + + Phonon::VideoWidget::AspectRatio aspectRatio() const; + void setAspectRatio(Phonon::VideoWidget::AspectRatio aspectRatio); + qreal brightness() const; + void setBrightness(qreal); + Phonon::VideoWidget::ScaleMode scaleMode() const; + void setScaleMode(Phonon::VideoWidget::ScaleMode scaleMode); + qreal contrast() const; + void setContrast(qreal); + qreal hue() const; + void setHue(qreal); + qreal saturation() const; + void setSaturation(qreal); + + QWidget *widget(); + + void updateVideo(VideoFrame &frame); + + protected: + void mediaNodeEvent(const MediaNodeEvent *event); + + private: + VideoRenderWidget *m_videoRenderWidget; + }; + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_VIDEOWIDGET_H diff --git a/src/3rdparty/phonon/qt7/videowidget.mm b/src/3rdparty/phonon/qt7/videowidget.mm new file mode 100644 index 0000000..736dcdf --- /dev/null +++ b/src/3rdparty/phonon/qt7/videowidget.mm @@ -0,0 +1,883 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include <QtCore/qglobal.h> +#ifdef QT_MAC_USE_COCOA +#import <QTKit/QTMovieLayer.h> +#endif + +#include "videowidget.h" +#include "backendheader.h" +#include "quicktimevideoplayer.h" +#include "medianode.h" +#include "medianodeevent.h" +#include "mediaobject.h" + +#include <QtOpenGL/QGLWidget> +#include <QtCore/QTime> +#include <QtCore/QEvent> +#include <QtCore/QCoreApplication> + +#import <AppKit/NSImage.h> +#import <QTKit/QTMovieView.h> + +///////////////////////////////////////////////////////////////////////////////////////// + +#ifdef QT_MAC_USE_COCOA // Rendering to a QTMovieView can only be done in Cocoa + +#define VIDEO_TRANSPARENT(m) -(void)m:(NSEvent *)e{[[self superview] m:e];} + +@interface SharedQTMovieView : QTMovieView +{ +@private + Phonon::QT7::QuickTimeVideoPlayer *m_player; + QList<QWidget *> *m_parents; + QWidget *m_window; + QRect *m_drawRect; + bool m_newImageReady; + bool m_usingWindow; +} + +- (SharedQTMovieView *) init; +- (void) registerParent:(QWidget *)parent; +- (void) unregisterParent:(QWidget *)parent; +- (void) setDrawRect:(QRect &)rect; +- (void) drawVideoFrame:(Phonon::QT7::VideoFrame &)frame forWidget:(QWidget *)widget shareImages:(bool)share; +- (void) useOffscreenWindow:(bool)offscreen; +- (void) applyDrawRectOnSelf; +@end + +///////////////////////////////////////////////////////////////////////////////////////// + +@implementation SharedQTMovieView + +- (SharedQTMovieView *) init +{ + self = [super initWithFrame:NSZeroRect]; + if (self){ + [self setControllerVisible:NO]; + m_parents = new QList<QWidget *>(); + m_drawRect = new QRect(0, 0, 1, 1); + [self applyDrawRectOnSelf]; + m_usingWindow = false; + } + return self; +} + +- (void) dealloc +{ + Phonon::QT7::PhononAutoReleasePool pool; + delete m_window; + delete m_drawRect; + delete m_parents; + [super dealloc]; +} + +- (void) applyDrawRectOnSelf +{ + NSRect nsrect; + nsrect.origin.x = m_drawRect->x(); + nsrect.origin.y = m_drawRect->y(); + nsrect.size.width = m_drawRect->width(); + nsrect.size.height = m_drawRect->height(); + [self setFrame:nsrect]; +} + +- (void) setDrawRect:(QRect &)rect +{ + *m_drawRect = rect; + if (!m_usingWindow) + [self applyDrawRectOnSelf]; +} + +- (void) waitForFrame +{ + if (m_usingWindow){ + QTMovie *movie = [self movie]; + if (movie){ + // CIImages are expected, but not received. + // Try to wait a couple of seconds for them: + m_newImageReady = false; + float rate = [movie rate]; + if (!rate) + [movie setRate:1]; + QTime t; t.start(); + while (!m_newImageReady && t.elapsed() < 2000) + ; + [movie setRate:rate]; + } + } +} + +- (void) useOffscreenWindow:(bool)offscreen +{ + if (offscreen == m_usingWindow) + return; + if (offscreen){ + if (!m_window){ + m_window = new QWidget; + m_window->setWindowOpacity(0.0); + m_window->show(); + m_window->hide(); + } + m_usingWindow = true; + [self setDelegate:self]; + [self waitForFrame]; + foreach(QWidget *w, *m_parents) + w->repaint(); + qApp->processEvents(); + [self removeFromSuperview]; + [(NSView *)m_window->winId() addSubview:self]; + } else if (!m_parents->isEmpty()){ + m_usingWindow = false; + [self removeFromSuperview]; + [(NSView*)m_parents->first()->winId() addSubview:self]; + [self setDelegate:0]; + [self setDrawRect:*m_drawRect]; + } +} + +- (void) drawVideoFrame:(Phonon::QT7::VideoFrame &)frame forWidget:(QWidget *)widget shareImages:(bool)share; +{ + // Detect if the video that produces the frame has changed: + Phonon::QT7::QuickTimeVideoPlayer *player = frame.videoPlayer(); + if (player && player->qtMovie() != [self movie]){ + m_player = player; + [self setMovie:player->qtMovie()]; + [self waitForFrame]; + } + + [self useOffscreenWindow:(share || m_parents->size() > 1)]; + if (m_usingWindow) + widget->update(); +} + +// Override this method so that the movie doesn't stop if +// the window becomes invisible +- (void)viewWillMoveToWindow:(NSWindow *)newWindow +{ + Q_UNUSED(newWindow); +} + +- (CIImage *) view:(QTMovieView *)view willDisplayImage:(CIImage *)img +{ + // This method is called from QTMovieView just + // before the image will be drawn. + Q_UNUSED(view); + m_player->setPrimaryRenderingCIImage(img); + m_newImageReady = true; + return img; +} + +- (void) registerParent:(QWidget *)parent +{ + if (m_parents->contains(parent)) + return; + m_parents->append(parent); + if (m_parents->size() == 1){ + Phonon::QT7::PhononAutoReleasePool pool; + m_usingWindow = true; + [self applyDrawRectOnSelf]; + [self useOffscreenWindow:NO]; + } +} + +- (void) unregisterParent:(QWidget *)parent +{ + m_parents->removeAll(parent); + if (m_parents->size() == 1) + [self applyDrawRectOnSelf]; +} + +VIDEO_TRANSPARENT(mouseDown); +VIDEO_TRANSPARENT(mouseDragged); +VIDEO_TRANSPARENT(mouseUp); +VIDEO_TRANSPARENT(mouseMoved); +VIDEO_TRANSPARENT(mouseEntered); +VIDEO_TRANSPARENT(mouseExited); +VIDEO_TRANSPARENT(rightMouseDown); +VIDEO_TRANSPARENT(rightMouseDragged); +VIDEO_TRANSPARENT(rightMouseUp); +VIDEO_TRANSPARENT(otherMouseDown); +VIDEO_TRANSPARENT(otherMouseDragged); +VIDEO_TRANSPARENT(otherMouseUp); +VIDEO_TRANSPARENT(keyDown); +VIDEO_TRANSPARENT(keyUp); +VIDEO_TRANSPARENT(scrollWheel) + +@end + +#endif // QT_MAC_USE_COCOA + +///////////////////////////////////////////////////////////////////////////////////////// + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +class IVideoRenderDrawWidget +{ +public: + virtual ~IVideoRenderDrawWidget(){} + virtual void setVideoFrame(VideoFrame &) = 0; + virtual void setDrawFrameRect(const QRect &) = 0; + virtual void updateVideoOutputCount(int){} + virtual void setMovieIsPaused(bool){} +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +QGLWidget *PhononSharedQGLWidget(){ + static QGLWidget *glWidget = 0; + if (!glWidget) + glWidget = new QGLWidget(); + return glWidget; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +class RenderOpenGL : public QGLWidget, public IVideoRenderDrawWidget +{ +public: + VideoFrame m_currentFrame; + QRect m_drawFrameRect; + + RenderOpenGL(QWidget *parent, const QGLFormat &format, const QSize &size) : QGLWidget(format, parent, PhononSharedQGLWidget()) + { + resize(size); + setAutoFillBackground(false); + show(); + } + + void initializeGL() + { + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + } + + void resizeGL(int w, int h) + { + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glViewport(0, 0, GLsizei(w), GLsizei(h)); + gluOrtho2D(0, GLsizei(w), 0, GLsizei(h)); + updateGL(); + } + + void paintGL() + { + glClear(GL_COLOR_BUFFER_BIT); + m_currentFrame.drawCVTexture(m_drawFrameRect); + } + + void setVideoFrame(VideoFrame &frame) + { + m_currentFrame = frame; + makeCurrent(); + paintGL(); + swapBuffers(); + } + + void setDrawFrameRect(const QRect &rect) + { + m_drawFrameRect = rect; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +class RenderQTMovieView : public QWidget, public IVideoRenderDrawWidget +{ +public: +#if defined(QT_MAC_USE_COCOA) + QRect m_drawRect; + VideoFrame m_videoFrame; + SharedQTMovieView *m_currentView; + bool m_setDrawRectPending; + bool m_share; + + RenderQTMovieView(bool share, QWidget *parent, const QSize &size=QSize()) : QWidget(parent), m_currentView(0) + { + m_setDrawRectPending = true; + m_share = share; + setAutoFillBackground(false); + if (share){ + // In 'share' mode, this widget will only make sure + // that CIIImages are produced, and not actually + // draw anything: + hide(); + } else { + resize(size); + show(); + } + } + + ~RenderQTMovieView() + { + [m_currentView unregisterParent:this]; + } + + void showEvent(QShowEvent *) + { + if (m_share) + return; + [m_currentView registerParent:this]; + } + + void hideEvent(QHideEvent *) + { + if (m_share) + return; + [m_currentView unregisterParent:this]; + } + + void paintEvent(QPaintEvent *) + { + if (m_share) + return; + QPainter p(this); + p.fillRect(rect(), Qt::black); + m_videoFrame.drawCIImage(m_drawRect); + } + + void updateVideoOutputCount(int count) + { + Q_UNUSED(count); + } + + void setMovieIsPaused(bool paused) + { + Q_UNUSED(paused); + } + + void setVideoFrame(VideoFrame &frame) + { + m_videoFrame = frame; + + if (!m_videoFrame.isEmpty()){ + Phonon::QT7::QuickTimeVideoPlayer *player = m_videoFrame.videoPlayer(); + if (!player->m_primaryRenderingTarget){ + // First movie view. Create the shared resource: + SharedQTMovieView *view = [[[SharedQTMovieView alloc] init] autorelease]; + player->setPrimaryRenderingTarget(view); + } + + SharedQTMovieView *view = static_cast<SharedQTMovieView *>(player->m_primaryRenderingTarget); + if (!m_share && view != m_currentView){ + [m_currentView unregisterParent:this]; + m_currentView = view; + [m_currentView registerParent:this]; + } + + [view drawVideoFrame:m_videoFrame forWidget:this shareImages:m_share || m_videoFrame.hasColorAdjustments()]; + + if (m_setDrawRectPending){ + m_setDrawRectPending = false; + [view setDrawRect:m_drawRect]; + } + } + } + + void setDrawFrameRect(const QRect &rect) + { + m_drawRect = rect; + Phonon::QT7::QuickTimeVideoPlayer *player = m_videoFrame.videoPlayer(); + if (player && player->m_primaryRenderingTarget){ + SharedQTMovieView *view = static_cast<SharedQTMovieView *>(player->m_primaryRenderingTarget); + [view setDrawRect:m_drawRect]; + } else + m_setDrawRectPending = true; + } + +#else // QT_MAC_USE_COCOA == false + RenderQTMovieView(bool, QWidget *, const QSize& = QSize()){} + void setVideoFrame(VideoFrame &){} + void setDrawFrameRect(const QRect &){} +#endif +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +class RenderQTMovieLayer : public QWidget, public IVideoRenderDrawWidget +{ +public: +#ifdef QT_MAC_USE_COCOA + QTMovieLayer *m_movieLayer; + + RenderQTMovieLayer(QWidget *parent, const QSize&) : QWidget(parent) + { + PhononAutoReleasePool pool; + setAutoFillBackground(false); + m_movieLayer = 0; + [(NSView *)winId() setWantsLayer:YES]; + } + + void setVideoFrame(VideoFrame &frame) + { + QuickTimeVideoPlayer *player = frame.videoPlayer(); + if (!player || player->qtMovie() == [m_movieLayer movie]) + return; + + if (m_movieLayer) + [m_movieLayer setMovie:player->qtMovie()]; + else { + m_movieLayer = [QTMovieLayer layerWithMovie:player->qtMovie()]; + [(NSView *)winId() setLayer:m_movieLayer]; + } + } + + void setDrawFrameRect(const QRect &rect) + { + m_movieLayer.frame.origin.x = rect.x(); + m_movieLayer.frame.origin.y = rect.y(); + m_movieLayer.frame.size.width = rect.width(); + m_movieLayer.frame.size.height = rect.height(); + } + +#else // QT_MAC_USE_COCOA == false + RenderQTMovieLayer(QWidget *, const QSize&){} + void setVideoFrame(VideoFrame &){} + void setDrawFrameRect(const QRect &){} +#endif +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +class VideoRenderWidget : public QWidget +{ +public: + enum RenderSystem { RS_NoRendering = 0, + RS_QGLWidget = 1, + RS_QPainter = 2, + RS_CIImage = 3, + RS_CVTexture = 4, + RS_QImage = 5, + RS_QTMovieView = 6, + RS_QTMovieLayer = 7 + } m_renderSystem; + + VideoFrame m_currentFrame; + QRect m_movieFrameRect; + QRect m_drawFrameRect; + Phonon::VideoWidget::ScaleMode m_scaleMode; + Phonon::VideoWidget::AspectRatio m_aspect; + IVideoRenderDrawWidget *m_renderDrawWidget; + + qreal m_brightness; + qreal m_contrast; + qreal m_hue; + qreal m_saturation; + qreal m_opacity; + + VideoRenderWidget() : QWidget(0), + m_scaleMode(Phonon::VideoWidget::FitInView), m_aspect(Phonon::VideoWidget::AspectRatioAuto) + { + PhononAutoReleasePool pool; + m_brightness = 0; + m_contrast = 0; + m_hue = 0; + m_saturation = 0; + m_opacity = 1; + m_renderDrawWidget = 0; + m_renderSystem = RS_NoRendering; + + setAutoFillBackground(false); + updateDrawFrameRect(); + } + + RenderSystem selectBestRenderSystem(){ + if (!isVisible()) + return RS_NoRendering; + else if (window() && window()->testAttribute(Qt::WA_DontShowOnScreen)) + return RS_QPainter; + else { +#ifdef QUICKTIME_C_API_AVAILABLE + return RS_QGLWidget; +#else + return RS_QTMovieView; +#endif + } + } + + void setRenderSystem(RenderSystem renderSystem){ + PhononAutoReleasePool pool; + static QString userSystem = qgetenv("PHONON_RENDER_SYSTEM"); + if (!userSystem.isEmpty()) + renderSystem = RenderSystem(userSystem.toInt()); + + if (m_renderSystem == renderSystem) + return; + + m_renderSystem = renderSystem; + if (m_renderDrawWidget){ + delete m_renderDrawWidget; + m_renderDrawWidget = 0; + } + + switch (m_renderSystem){ + case RS_QGLWidget:{ + QGLFormat format = QGLFormat::defaultFormat(); + format.setSwapInterval(1); // Vertical sync (avoid tearing) + m_renderDrawWidget = new RenderOpenGL(this, format, size()); + break;} + case RS_QTMovieView:{ + m_renderDrawWidget = new RenderQTMovieView(false, this, size()); + break;} + case RS_QTMovieLayer:{ + m_renderDrawWidget = new RenderQTMovieLayer(this, size()); + break;} + case RS_QPainter: + case RS_CIImage: + case RS_CVTexture: + case RS_QImage: +#ifndef QUICKTIME_C_API_AVAILABLE + // On cocoa-64, let QTMovieView produce + // video frames for us: + m_renderDrawWidget = new RenderQTMovieView(true, this); +#endif + break; + case RS_NoRendering: + break; + } + + if (m_renderDrawWidget){ + m_renderDrawWidget->setVideoFrame(m_currentFrame); + m_renderDrawWidget->setDrawFrameRect(m_drawFrameRect); + } + } + + QSize sizeHint() const + { + return m_movieFrameRect.size(); + } + + bool event(QEvent *event) + { + switch (event->type()){ + // Try to detect if one of this objects + // anchestors might have changed: + case QEvent::Resize:{ + PhononAutoReleasePool pool; + updateDrawFrameRect(); + if (m_renderDrawWidget) + dynamic_cast<QWidget *>(m_renderDrawWidget)->resize(size()); + break; } + case QEvent::Paint:{ + PhononAutoReleasePool pool; + float opacity = parentWidget() ? parentWidget()->windowOpacity() : 1; + switch (m_renderSystem){ + case RS_QPainter:{ + QPainter p(this); + p.fillRect(rect(), Qt::black); + if (p.paintEngine()->type() == QPaintEngine::OpenGL) + m_currentFrame.drawCVTexture(m_drawFrameRect, opacity); + else + m_currentFrame.drawQImage(&p, m_drawFrameRect); + break; } + case RS_CIImage: + m_currentFrame.drawCIImage(m_drawFrameRect, opacity); + break; + case RS_CVTexture: + m_currentFrame.drawCVTexture(m_drawFrameRect, opacity); + break; + case RS_QImage:{ + QPainter p(this); + p.fillRect(rect(), Qt::black); + m_currentFrame.drawQImage(&p, m_drawFrameRect); + break; } + case RS_QGLWidget: + case RS_QTMovieView: + case RS_QTMovieLayer: + // draw in separate widget + break; + case RS_NoRendering: + QPainter p(this); + p.fillRect(rect(), Qt::black); + break; + } + break; } + default: + break; + } + + return QWidget::event(event); + } + + void setVideoFrame(VideoFrame &frame) + { + PhononAutoReleasePool pool; + m_currentFrame = frame; + m_currentFrame.setColors(m_brightness, m_contrast, m_hue, m_saturation); + + if (m_renderDrawWidget) + m_renderDrawWidget->setVideoFrame(m_currentFrame); + + setRenderSystem(selectBestRenderSystem()); + switch (m_renderSystem){ + case RS_QGLWidget: + case RS_QTMovieView: + case RS_QTMovieLayer: + case RS_NoRendering: + break; + case RS_CIImage: + case RS_CVTexture: + case RS_QImage: + case RS_QPainter: + repaint(); + break; + } + } + + void updateVideoFrame() + { + setVideoFrame(m_currentFrame); + } + + void setMovieRect(const QRect &mrect) + { + if (mrect == m_movieFrameRect) + return; + m_movieFrameRect = mrect; + updateDrawFrameRect(); + updateGeometry(); + if (isVisible()) + qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers); + } + + void setScaleMode(Phonon::VideoWidget::ScaleMode scaleMode) + { + m_scaleMode = scaleMode; + updateDrawFrameRect(); + updateVideoFrame(); + repaint(); + } + + void setAspectRatio(Phonon::VideoWidget::AspectRatio aspect) + { + m_aspect = aspect; + updateDrawFrameRect(); + updateVideoFrame(); + repaint(); + } + + void updateVideoOutputCount(int count) + { + if (m_renderDrawWidget) + m_renderDrawWidget->updateVideoOutputCount(count); + } + + void setMovieIsPaused(bool paused) + { + if (m_renderDrawWidget) + m_renderDrawWidget->setMovieIsPaused(paused); + } + + void updateDrawFrameRect() + { + if (m_movieFrameRect.width() <= 0 || m_movieFrameRect.height() <= 0) + m_movieFrameRect = QRect(0, 0, 640, 480); + + // Set m_drawFrameRect to be the size of the smallest possible + // rect conforming to the aspect and containing the whole frame: + switch(m_aspect){ + case Phonon::VideoWidget::AspectRatioWidget: + m_drawFrameRect = rect(); + break; + case Phonon::VideoWidget::AspectRatio4_3: + m_drawFrameRect = scaleToAspect(m_movieFrameRect, 4, 3); + break; + case Phonon::VideoWidget::AspectRatio16_9: + m_drawFrameRect = scaleToAspect(m_movieFrameRect, 16, 9); + break; + case Phonon::VideoWidget::AspectRatioAuto: + default: + m_drawFrameRect = m_movieFrameRect; + break; + } + + // Scale m_drawFrameRect to fill the widget + // without breaking aspect: + int widgetWidth = rect().width(); + int widgetHeight = rect().height(); + int frameWidth = widgetWidth; + int frameHeight = m_drawFrameRect.height() * float(widgetWidth) / float(m_drawFrameRect.width()); + + switch(m_scaleMode){ + case Phonon::VideoWidget::ScaleAndCrop: + if (frameHeight < widgetHeight){ + frameWidth *= float(widgetHeight) / float(frameHeight); + frameHeight = widgetHeight; + } + break; + case Phonon::VideoWidget::FitInView: + default: + if (frameHeight > widgetHeight){ + frameWidth *= float(widgetHeight) / float(frameHeight); + frameHeight = widgetHeight; + } + break; + } + + m_drawFrameRect.setSize(QSize(frameWidth, frameHeight)); + m_drawFrameRect.moveTo((widgetWidth - frameWidth) / 2.0f, (widgetHeight - frameHeight) / 2.0f); + + if (m_renderDrawWidget) + m_renderDrawWidget->setDrawFrameRect(m_drawFrameRect); + } + + QRect scaleToAspect(QRect srcRect, int w, int h) + { + int width = srcRect.width(); + int 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, width, height); + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +VideoWidget::VideoWidget(QObject *parent) : MediaNode(VideoSink, parent) +{ + m_videoRenderWidget = new VideoRenderWidget(); +} + +VideoWidget::~VideoWidget() +{ + delete m_videoRenderWidget; +} + +QWidget *VideoWidget::widget() +{ + IMPLEMENTED; + return m_videoRenderWidget; +} + +Phonon::VideoWidget::AspectRatio VideoWidget::aspectRatio() const +{ + IMPLEMENTED; + return m_videoRenderWidget->m_aspect; +} + +void VideoWidget::setAspectRatio(Phonon::VideoWidget::AspectRatio aspect) +{ + IMPLEMENTED; + m_videoRenderWidget->setAspectRatio(aspect); +} + +Phonon::VideoWidget::ScaleMode VideoWidget::scaleMode() const +{ + IMPLEMENTED; + return m_videoRenderWidget->m_scaleMode; +} + +void VideoWidget::setScaleMode(Phonon::VideoWidget::ScaleMode scaleMode) +{ + IMPLEMENTED; + m_videoRenderWidget->setScaleMode(scaleMode); +} + +qreal VideoWidget::brightness() const +{ + IMPLEMENTED; + return m_videoRenderWidget->m_brightness; +} + +void VideoWidget::setBrightness(qreal value) +{ + IMPLEMENTED; + m_videoRenderWidget->m_brightness = value; + if (m_owningMediaObject && m_owningMediaObject->state() == Phonon::PausedState) + m_videoRenderWidget->updateVideoFrame(); +} + +qreal VideoWidget::contrast() const +{ + IMPLEMENTED; + return m_videoRenderWidget->m_contrast; +} + +void VideoWidget::setContrast(qreal value) +{ + IMPLEMENTED; + m_videoRenderWidget->m_contrast = value; + if (m_owningMediaObject && m_owningMediaObject->state() == Phonon::PausedState) + m_videoRenderWidget->updateVideoFrame(); +} + +qreal VideoWidget::hue() const +{ + IMPLEMENTED; + return m_videoRenderWidget->m_hue; +} + +void VideoWidget::setHue(qreal value) +{ + IMPLEMENTED; + m_videoRenderWidget->m_hue = value; + if (m_owningMediaObject && m_owningMediaObject->state() == Phonon::PausedState) + m_videoRenderWidget->updateVideoFrame(); +} + +qreal VideoWidget::saturation() const +{ + IMPLEMENTED; + return m_videoRenderWidget->m_saturation; +} + +void VideoWidget::setSaturation(qreal value) +{ + IMPLEMENTED; + m_videoRenderWidget->m_saturation = value; + if (m_owningMediaObject && m_owningMediaObject->state() == Phonon::PausedState) + m_videoRenderWidget->updateVideoFrame(); +} + +void VideoWidget::mediaNodeEvent(const MediaNodeEvent *event) +{ + switch (event->type()){ + case MediaNodeEvent::VideoFrameSizeChanged: + m_videoRenderWidget->setMovieRect(*static_cast<QRect *>(event->data())); + break; + case MediaNodeEvent::VideoOutputCountChanged: + m_videoRenderWidget->updateVideoOutputCount(*static_cast<int *>(event->data())); + break; + case MediaNodeEvent::MediaPlaying: + m_videoRenderWidget->setMovieIsPaused(!(*static_cast<bool *>(event->data()))); + break; + default: + break; + } +} + +void VideoWidget::updateVideo(VideoFrame &frame){ + PhononAutoReleasePool pool; + m_videoRenderWidget->setVideoFrame(frame); + MediaNode::updateVideo(frame); +} + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#include "moc_videowidget.cpp" diff --git a/src/3rdparty/phonon/waveout/audiooutput.cpp b/src/3rdparty/phonon/waveout/audiooutput.cpp new file mode 100644 index 0000000..f842dc9 --- /dev/null +++ b/src/3rdparty/phonon/waveout/audiooutput.cpp @@ -0,0 +1,78 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#include "audiooutput.h" +#include "mediaobject.h" + +#include <QtCore/QVector> + +#include <cmath> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace WaveOut + { + AudioOutput::AudioOutput(Backend *, QObject *parent) + { + setParent(parent); + m_volume = 0xffff; + } + + AudioOutput::~AudioOutput() + { + } + + int AudioOutput::outputDevice() const + { + return 0; + } + + void AudioOutput::setVolume(qreal newVolume) + { + m_volume = newVolume; + emit volumeChanged(newVolume); + } + + void AudioOutput::setCrossFadingProgress(short currentIndex, qreal progress) + { + Q_UNUSED(currentIndex); + Q_UNUSED(progress); + } + + bool AudioOutput::setOutputDevice(const AudioOutputDevice & newDevice) + { + return setOutputDevice(newDevice.index()); + } + + qreal AudioOutput::volume() const + { + return m_volume; + } + + bool AudioOutput::setOutputDevice(int newDevice) + { + + return (newDevice == 0); + } + + } +} + +QT_END_NAMESPACE + diff --git a/src/3rdparty/phonon/waveout/audiooutput.h b/src/3rdparty/phonon/waveout/audiooutput.h new file mode 100644 index 0000000..43f8222 --- /dev/null +++ b/src/3rdparty/phonon/waveout/audiooutput.h @@ -0,0 +1,65 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_AUDIOOUTPUT_H +#define PHONON_AUDIOOUTPUT_H + +#include <QtCore/QFile> +#include <phonon/audiooutputinterface.h> + +#include "backend.h" + +struct IBaseFilter; +struct IBasicAudio; + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace WaveOut + { + class AudioOutput : public QObject, public Phonon::AudioOutputInterface + { + Q_OBJECT + + Q_INTERFACES(Phonon::AudioOutputInterface) + public: + AudioOutput(Backend *back, QObject *parent); + ~AudioOutput(); + + // Attributes Getters: + qreal volume() const; + int outputDevice() const; + void setVolume(qreal newVolume); + bool setOutputDevice(int newDevice); + bool setOutputDevice(const AudioOutputDevice & newDevice); + void setCrossFadingProgress(short currentIndex, qreal progress); + + Q_SIGNALS: + void audioDeviceFailed(); + void volumeChanged(qreal); + private: + unsigned int m_volume; + + + }; + } +} + +QT_END_NAMESPACE + +#endif // PHONON_AUDIOOUTPUT_H diff --git a/src/3rdparty/phonon/waveout/backend.cpp b/src/3rdparty/phonon/waveout/backend.cpp new file mode 100644 index 0000000..8faa26e --- /dev/null +++ b/src/3rdparty/phonon/waveout/backend.cpp @@ -0,0 +1,131 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#include "backend.h" + + +#include "audiooutput.h" +#include "mediaobject.h" + + + +#include <QtCore/QSettings> +#include <QtCore/QSet> +#include <QtCore/QVariant> +#include <QtCore/QStringList> + +#include <QtCore/QtPlugin> + + +QT_BEGIN_NAMESPACE + +// export as Qt/KDE factory as required + +Q_EXPORT_PLUGIN2(phonon_waveout, Phonon::WaveOut::Backend); + +namespace Phonon +{ + namespace WaveOut + { + + Backend::Backend(QObject *parent, const QVariantList &) + : QObject(parent) + { + } + + Backend::~Backend() + { + } + + QObject *Backend::createObject(BackendInterface::Class c, QObject *parent, const QList<QVariant> &args) + { + Q_UNUSED(args); + switch (c) + { + case MediaObjectClass: + return new MediaObject(parent); + case AudioOutputClass: + return new AudioOutput(this, parent); + default: + return 0; + } + } + + bool Backend::supportsVideo() const + { + return false; + } + + QStringList Backend::availableMimeTypes() const + { + QStringList ret; + return ret; + } + + + QList<int> Backend::objectDescriptionIndexes(Phonon::ObjectDescriptionType type) const + { + QList<int> r; + if (type == Phonon::AudioOutputDeviceType) + r.append(0); + return r; + } + + QHash<QByteArray, QVariant> Backend::objectDescriptionProperties(Phonon::ObjectDescriptionType type, int index) const + { + Q_UNUSED(index); + QHash<QByteArray, QVariant> r; + if (type == Phonon::AudioOutputDeviceType) + r["name"] = QLatin1String("default audio device"); + return r; + } + + + bool Backend::connectNodes(QObject *node1, QObject *node2) + { + MediaObject *mediaObject = qobject_cast<MediaObject*> (node1); + AudioOutput *audioOutput = qobject_cast<AudioOutput*> (node2); + + if (mediaObject && audioOutput) + mediaObject->setAudioOutput(audioOutput); + return true; + } + + bool Backend::disconnectNodes(QObject *node1, QObject *node2) + { + Q_UNUSED(node1); + Q_UNUSED(node2); + return true; + } + + //transaction management + bool Backend::startConnectionChange(QSet<QObject *>) + { + return true; + } + + bool Backend::endConnectionChange(QSet<QObject *>) + { + return true; + } + + } +} + +QT_END_NAMESPACE + +#include "moc_backend.cpp" diff --git a/src/3rdparty/phonon/waveout/backend.h b/src/3rdparty/phonon/waveout/backend.h new file mode 100644 index 0000000..060d853 --- /dev/null +++ b/src/3rdparty/phonon/waveout/backend.h @@ -0,0 +1,69 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_BACKEND_H +#define PHONON_BACKEND_H + +#include <phonon/backendinterface.h> +#include <phonon/phononnamespace.h> + +#include <QtCore/QList> + + + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace WaveOut + { + class AudioOutput; + class MediaObject; + + class Backend : public QObject, public Phonon::BackendInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::BackendInterface) + public: + Backend(QObject *parent = 0, const QVariantList & = QVariantList()); + virtual ~Backend(); + + QObject *createObject(Phonon::BackendInterface::Class, QObject *parent, const QList<QVariant> &args); + + bool supportsVideo() const; + QStringList availableMimeTypes() const; + + QList<int> objectDescriptionIndexes(Phonon::ObjectDescriptionType type) const; + QHash<QByteArray, QVariant> objectDescriptionProperties(Phonon::ObjectDescriptionType type, int index) const; + + bool connectNodes(QObject *, QObject *); + bool disconnectNodes(QObject *, QObject *); + + //transaction management + bool startConnectionChange(QSet<QObject *>); + bool endConnectionChange(QSet<QObject *>); + + Q_SIGNALS: + void objectDescriptionChanged(ObjectDescriptionType); + + }; + } +} + +QT_END_NAMESPACE + +#endif // PHONON_BACKEND_H diff --git a/src/3rdparty/phonon/waveout/mediaobject.cpp b/src/3rdparty/phonon/waveout/mediaobject.cpp new file mode 100644 index 0000000..35d9e0b --- /dev/null +++ b/src/3rdparty/phonon/waveout/mediaobject.cpp @@ -0,0 +1,686 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#include "mediaobject.h" +#include "audiooutput.h" + +#include <QtCore/QVector> +#include <QtCore/QTimerEvent> +#include <QtCore/QTimer> +#include <QtCore/QTime> +#include <QtCore/QLibrary> +#include <QtCore/QUrl> +#include <QtCore/QWriteLocker> + +#include <phonon/streaminterface.h> + + +#define WAVEHEADER_OFFSET_FORMATTAG 20 +#define WAVEHEADER_OFFSET_CHANNELS 22 +#define WAVEHEADER_OFFSET_SAMPLESPERSEC 24 +#define WAVEHEADER_OFFSET_AVGBYTESPERSEC 28 +#define WAVEHEADER_OFFSET_BLOCKALIGN 32 +#define WAVEHEADER_OFFSET_BITSPERSAMPLE 34 +#define WAVEHEADER_OFFSET_DATA 44 +#define WAVEHEADER_SIZE WAVEHEADER_OFFSET_DATA + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + namespace WaveOut + { + static unsigned int buffer_size = (16 * 1024 * 4); + + QString getErrorText(MMRESULT error) + { + ushort b[256]; + waveOutGetErrorText(error, (LPWSTR)b, 256); + return QString::fromUtf16(b); + } + + class WorkerThread : public QThread + { + Q_OBJECT + public slots: + void stream(QIODevice *file, QByteArray *buffer, bool *finished); + }; + + void WorkerThread::stream(QIODevice *ioStream, QByteArray *buffer, bool *finished) + { + (*finished) = false; + memset((void*) buffer->data(), 0, buffer->size()); + qint64 i = ioStream->read(buffer->data(), buffer_size); + buffer->resize(i); + (*finished) = true; + } + + + void CALLBACK MediaObject::WaveOutCallBack(HWAVEOUT m_hWaveOut, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) + { + Q_UNUSED(m_hWaveOut); + Q_UNUSED(dwInstance); + Q_UNUSED(dwParam2); + + switch(uMsg) + { + case WOM_OPEN: + break; + case WOM_DONE: + { + WAVEHDR *waveHeader = (WAVEHDR*)dwParam1; + MediaObject* mediaObject = reinterpret_cast<MediaObject *>(waveHeader->dwUser); + if (mediaObject) { + mediaObject->swapBuffers(); + } + } + break; + case WOM_CLOSE: + break; + } + } + + class StreamReader : public Phonon::StreamInterface + { + public: + StreamReader(QObject *parent, const Phonon::MediaSource &source) : + m_seekable(false), m_pos(0), m_size(-1) + { + Q_UNUSED(parent); + connectToSource(source); + } + + //for Phonon::StreamInterface + void writeData(const QByteArray &data) + { + QWriteLocker locker(&m_lock); + m_pos += data.size(); + m_buffer += data; + } + + void endOfData() + { + } + + void setStreamSize(qint64 newSize) + { + QWriteLocker locker(&m_lock); + m_size = newSize; + } + + qint64 streamSize() const + { + QReadLocker locker(&m_lock); + return m_size; + } + + void setStreamSeekable(bool s) + { + QWriteLocker locker(&m_lock); + m_seekable = s; + } + + bool streamSeekable() const + { + QReadLocker locker(&m_lock); + return m_seekable; + } + + void setCurrentPos(qint64 pos) + { + QWriteLocker locker(&m_lock); + m_pos = pos; + seekStream(pos); + m_buffer.clear(); + } + + qint64 currentPos() const + { + QReadLocker locker(&m_lock); + return m_pos; + } + + int currentBufferSize() const + { + QReadLocker locker(&m_lock); + return m_buffer.size(); + } + + //for Phonon::StreamInterface + QByteArray m_buffer; + bool m_seekable; + qint64 m_pos; + qint64 m_size; + mutable QReadWriteLock m_lock; + }; + + class IOWrapper : public QIODevice { + public: + IOWrapper(QObject *parent, const Phonon::MediaSource &source) : m_streamReader(this, source) + { + Q_UNUSED(parent); + setOpenMode(QIODevice::ReadOnly); + } + bool seek(qint64 pos); + qint64 size() const; + qint64 pos(); + bool isReadable() const; + protected: + qint64 readData (char * data, qint64 maxSize); + qint64 writeData(const char *,qint64); + private: + StreamReader m_streamReader; + }; + + bool IOWrapper::isReadable () const + { + return true; + } + + qint64 IOWrapper::pos() + { + return (m_streamReader.streamSeekable() ? m_streamReader.currentPos() : 0); + } + + bool IOWrapper::seek( qint64 pos) + { + if (!m_streamReader.streamSeekable()) + return false; + m_streamReader.setCurrentPos(pos); + return true; + } + + qint64 IOWrapper::size() const + { + return m_streamReader.streamSize(); + } + + qint64 IOWrapper::readData(char * data, qint64 maxSize) + { + int oldSize = m_streamReader.currentBufferSize(); + while (m_streamReader.currentBufferSize() < maxSize) { + m_streamReader.needData(); + if (oldSize == m_streamReader.currentBufferSize()) { + break; //we didn't get any data + } + oldSize = m_streamReader.currentBufferSize(); + } + + qint64 bytesRead = qMin(qint64(m_streamReader.currentBufferSize()), maxSize); + { + QWriteLocker locker(&m_streamReader.m_lock); + qMemCopy(data, m_streamReader.m_buffer.data(), bytesRead); + //truncate the buffer + m_streamReader.m_buffer = m_streamReader.m_buffer.mid(bytesRead); + } + return bytesRead; + } + + qint64 IOWrapper::writeData(const char *,qint64) + { + return 0; + } + + MediaObject::MediaObject(QObject *parent) : m_file(0), m_stream(0), + m_hWaveOut(0), m_nextBufferIndex(1), + m_mediaSize(-1), m_bufferingFinished(0), + m_paused(0), m_tickInterval(0), + m_hasNextSource(0), m_hasSource(0), + m_sourceIsValid(0), m_errorType(Phonon::NoError), + m_currentTime(0), m_transitionTime(0), + m_tick(0), m_volume(100), m_prefinishMark(0), + m_tickIntervalResolution(0), m_bufferPrepared(0), + m_stopped(0) + { + m_thread = new WorkerThread(); + connect(this, SIGNAL(outOfData(QIODevice*, QByteArray*, bool*)), m_thread, SLOT(stream(QIODevice*, QByteArray*, bool*))); + m_thread->start(); + m_soundBuffer1.waveHeader = new WAVEHDR; + m_soundBuffer2.waveHeader = new WAVEHDR; + setParent(parent); + setState(Phonon::LoadingState); + } + + MediaObject::~MediaObject() + { + stop(); + disconnect(this, SIGNAL(outOfData(QIODevice*, QByteArray*, bool*)), m_thread, SLOT(stream(QIODevice*, QByteArray*, bool*))); + do { //The event loop of m_thread might not be started, yet + m_thread->quit(); //If the event loop is not started yet quit() does nothing + m_thread->wait(100); + } while (m_thread->isRunning()); + delete m_thread; + deleteValidWaveOutDevice(); + delete m_soundBuffer1.waveHeader; + delete m_soundBuffer2.waveHeader; + } + + Phonon::State MediaObject::state() const + { + return m_state; + } + + bool MediaObject::hasVideo() const + { + return false; + } + + bool MediaObject::isSeekable() const + { + if (!m_stream) + return false; + return !m_stream->isSequential(); + } + + qint64 MediaObject::totalTime() const + { + return m_totalTime; + } + + qint64 MediaObject::currentTime() const + { + //this handles inaccuracy when stopping on a title + return m_currentTime; + } + + qint32 MediaObject::tickInterval() const + { + return m_tickInterval * m_tickIntervalResolution; + } + + void MediaObject::setTickInterval(qint32 newTickInterval) + { + if ((m_tickIntervalResolution == 0) || (newTickInterval == 0)) + return; + m_tickInterval = newTickInterval / m_tickIntervalResolution; + if ((newTickInterval > 0) && (m_tickInterval == 0)) + m_tickInterval = 1; + } + + void MediaObject::pause() + { + if (!m_paused) { + m_paused = true; + setState(Phonon::PausedState); + if (!(waveOutPause(m_hWaveOut) == MMSYSERR_NOERROR)) + { + setError(Phonon::NormalError, QLatin1String("cannot pause (system error)")); + } + } + } + + void MediaObject::stop() + { + setState(Phonon::StoppedState); + m_stopped = true; + m_paused = false; + seek(0); + if (!(waveOutReset(m_hWaveOut) == MMSYSERR_NOERROR)) + setError(Phonon::NormalError, QLatin1String("cannot stop (system error)")); + } + + void MediaObject::play() + { + if ((m_state == Phonon::PlayingState) && !m_paused && !m_stopped) + return; + if ((m_state == Phonon::LoadingState) || + (m_state == Phonon::BufferingState) || + (m_state == Phonon::ErrorState)) { + setError(Phonon::FatalError, QLatin1String("illegale state for playback")); + return; + } + + if (m_state == Phonon::StoppedState) + stop(); + if (m_sourceIsValid) { + setState(Phonon::PlayingState); + if (!m_paused) { + m_nextBufferIndex = true; + m_stopped = false; + playBuffer(m_soundBuffer1.waveHeader); + playBuffer(m_soundBuffer2.waveHeader); + } else { + if (!(waveOutRestart(m_hWaveOut) == MMSYSERR_NOERROR)) + setError(Phonon::NormalError, QLatin1String("cannot resume (system)")); + } + } else { + setError(Phonon::FatalError, QLatin1String("cannot playback invalid source")); + } + m_paused = false; + } + + QString MediaObject::errorString() const + { + + return m_errorString; + } + + Phonon::ErrorType MediaObject::errorType() const + { + return Phonon::ErrorType(); + } + + qint32 MediaObject::prefinishMark() const + { + return m_prefinishMark; + } + + void MediaObject::setPrefinishMark(qint32 newPrefinishMark) + { + m_prefinishMark = newPrefinishMark; + } + + qint32 MediaObject::transitionTime() const + { + return m_transitionTime; + } + + void MediaObject::setTransitionTime(qint32 time) + { + m_transitionTime = time; + } + + qint64 MediaObject::remainingTime() const + { + return m_totalTime - m_currentTime; + } + + Phonon::MediaSource MediaObject::source() const + { + return Phonon::MediaSource(); + } + + void MediaObject::setNextSource(const Phonon::MediaSource &source) + { + m_nextSource = source; + m_hasNextSource = true; + } + + void MediaObject::setSource(const Phonon::MediaSource &source) + { + if (m_state == Phonon::PlayingState) + { + setError(Phonon::NormalError, QLatin1String("source changed while playing")); + stop(); + } + + m_source = source; + m_hasSource = true; + m_sourceIsValid = false; + + emit currentSourceChanged(source); + + if (source.type() == Phonon::MediaSource::LocalFile) { + if (!openWaveFile(source.fileName())) { + setError(Phonon::FatalError, QLatin1String("cannot open media file")); + return ; + } + } else if (source.type() == Phonon::MediaSource::Stream) { + if (m_stream) + delete m_stream; + m_stream = new IOWrapper(this, source); + m_mediaSize = m_stream->size(); + } else if (source.type() == Phonon::MediaSource::Url) { + if (!openWaveFile(source.url().toLocalFile())) { + setError(Phonon::FatalError, QLatin1String("cannot open media file")); + return ; + } + } else { + setError(Phonon::FatalError, QLatin1String("type of source not supported")); + return ; + } + setState(Phonon::LoadingState); + + if (!readHeader()) + setError(Phonon::FatalError, QLatin1String("invalid header")); + else if (!getWaveOutDevice()) + setError(Phonon::FatalError, QLatin1String("No waveOut device available")); + else if (!fillBuffers()) + setError(Phonon::FatalError, QLatin1String("no data for buffering")); + else if (!prepareBuffers()) + setError(Phonon::FatalError, QLatin1String("cannot prepare buffers")); + else + m_sourceIsValid = true; + + if (m_sourceIsValid) + setState(Phonon::StoppedState); + } + + void MediaObject::seek(qint64 time) + { + if (!m_sourceIsValid) { + setError(Phonon::NormalError, QLatin1String("source is not valid")); + return; + } + if ((time >= 0) && (time < m_totalTime)) { + int counter = 0; + while (!m_bufferingFinished && (counter < 200)) { + Sleep(20); + counter ++; + } + if (counter >= 200) { + setError(Phonon::NormalError, QLatin1String("buffering timed out")); + return; + } + + m_stream->seek(WAVEHEADER_SIZE + time * m_waveFormatEx.nSamplesPerSec * m_waveFormatEx.wBitsPerSample * m_waveFormatEx.nChannels / 8 / 1000); + m_currentTime = time; + if (m_state == Phonon::PlayingState) + play(); + } else { + setError(Phonon::NormalError, QLatin1String("seeking out of range")); + } + } + + void MediaObject::unPrepareBuffers() + { + if (m_bufferPrepared) { + DWORD err1 = waveOutUnprepareHeader(m_hWaveOut, m_soundBuffer1.waveHeader, sizeof(WAVEHDR)); + DWORD err2 = waveOutUnprepareHeader(m_hWaveOut, m_soundBuffer2.waveHeader, sizeof(WAVEHDR)); + if (!(err1 == MMSYSERR_NOERROR) || !(err2 == MMSYSERR_NOERROR)) + setError(Phonon::NormalError, QLatin1String("cannot unprepare buffer") + getErrorText(err1) + getErrorText(err2)); + } + m_bufferPrepared = false; + } + + bool MediaObject::prepareBuffers() + { + memset((void*)m_soundBuffer1.waveHeader, 0, sizeof(WAVEHDR)); + m_soundBuffer1.waveHeader->lpData = m_soundBuffer1.data.data(); + m_soundBuffer1.waveHeader->dwBufferLength = m_soundBuffer1.data.size(); + m_soundBuffer1.waveHeader->dwUser = (DWORD_PTR) this; + + ZeroMemory((void*)m_soundBuffer2.waveHeader, sizeof(WAVEHDR)); + m_soundBuffer2.waveHeader->lpData = m_soundBuffer2.data.data(); + m_soundBuffer2.waveHeader->dwBufferLength = m_soundBuffer1.data.size(); + m_soundBuffer2.waveHeader->dwUser = (DWORD_PTR) this; + + m_bufferPrepared = (waveOutPrepareHeader(m_hWaveOut, m_soundBuffer1.waveHeader, sizeof(WAVEHDR)) == MMSYSERR_NOERROR) + && (waveOutPrepareHeader(m_hWaveOut, m_soundBuffer2.waveHeader, sizeof(WAVEHDR)) == MMSYSERR_NOERROR); + return m_bufferPrepared; + } + + void MediaObject::deleteValidWaveOutDevice() + { + if (m_hWaveOut) { + unPrepareBuffers(); + if (!(waveOutClose(m_hWaveOut) == MMSYSERR_NOERROR)) + setError(Phonon::NormalError, QLatin1String("cannot close wave device")); + } + } + + bool MediaObject::getWaveOutDevice() + { + deleteValidWaveOutDevice(); + + for(UINT deviceId = 0; deviceId < waveOutGetNumDevs(); deviceId++) + { + if(deviceId == waveOutGetNumDevs()) + return false; + if(waveOutOpen(&m_hWaveOut, WAVE_MAPPER, &m_waveFormatEx, (DWORD)WaveOutCallBack, 0, CALLBACK_FUNCTION) == MMSYSERR_NOERROR) + return m_hWaveOut; //m_hWaveOut !=0; + } + return false; + } + + bool MediaObject::openWaveFile(QString fileName) + { + if (m_file) + delete m_file; + m_file = new QFile(fileName); + m_file->setParent(this); + m_stream = m_file; + m_mediaSize = m_file->size(); + return (m_file->open(QIODevice::ReadOnly)); + } + + bool MediaObject::readHeader() + { + QByteArray header = m_stream->read(WAVEHEADER_SIZE); + + if (header.size() == WAVEHEADER_SIZE) { + + m_waveFormatEx.wFormatTag = *((WORD* )(header.data() + WAVEHEADER_OFFSET_FORMATTAG )); + m_waveFormatEx.nChannels = *((WORD* )(header.data() + WAVEHEADER_OFFSET_CHANNELS )); + m_waveFormatEx.nSamplesPerSec = *((DWORD*)(header.data() + WAVEHEADER_OFFSET_SAMPLESPERSEC )); + m_waveFormatEx.nAvgBytesPerSec = *((DWORD*)(header.data() + WAVEHEADER_OFFSET_AVGBYTESPERSEC)); + m_waveFormatEx.nBlockAlign = *((WORD* )(header.data() + WAVEHEADER_OFFSET_BLOCKALIGN )); + m_waveFormatEx.wBitsPerSample = *((WORD* )(header.data() + WAVEHEADER_OFFSET_BITSPERSAMPLE )); + + m_tickIntervalResolution = (qint64(buffer_size) * 8 * 1000) / m_waveFormatEx.nSamplesPerSec / m_waveFormatEx.wBitsPerSample / m_waveFormatEx.nChannels; + if (m_mediaSize > 0) + m_totalTime = ((m_mediaSize - WAVEHEADER_SIZE) * 8 * 1000) / m_waveFormatEx.nSamplesPerSec / m_waveFormatEx.wBitsPerSample / m_waveFormatEx.nChannels; + else + m_totalTime = -1; + emit totalTimeChanged(m_totalTime); + return true; + } else { + return false; + } + } + + bool MediaObject::fillBuffers() + { + + m_soundBuffer1.data = m_stream->read(buffer_size); + m_soundBuffer2.data = m_stream->read(buffer_size); + + m_bufferingFinished = true; + + if (!(m_soundBuffer1.data.size() > 0)) + setError(Phonon::NormalError, QLatin1String("cannot read source")); + return true; + } + + void MediaObject::setState(Phonon::State newState) + { + if (m_state == newState) + return; + emit stateChanged(newState, m_state); + m_state = newState; + } + + void MediaObject::setError(ErrorType errorType, QString errorMessage) + { + m_errorType = errorType; + setState(Phonon::ErrorState); + m_errorString = errorMessage; + } + + void MediaObject::setAudioOutput(QObject *audioOutput) + { + m_audioOutput = qobject_cast<AudioOutput*>(audioOutput); + + if (m_audioOutput) { + m_volume = m_audioOutput->volume(); + connect(m_audioOutput, SIGNAL(volumeChanged(qreal)), this, SLOT(setVolume(qreal))); + } + } + + void MediaObject::setVolume(qreal newVolume) + { + m_volume = newVolume; + } + + void MediaObject::swapBuffers() + { + if (m_stopped || m_paused) + return; + + m_currentTime += m_tickIntervalResolution; + if (m_tickInterval) { + m_tick ++; + if (m_tick > (m_tickInterval - 1)) { + emit tick(m_currentTime); + m_tick = 0; + } + } + if ((m_prefinishMark > 0)&& (m_prefinishMark < m_currentTime)) + emit prefinishMarkReached(m_totalTime - m_currentTime); + + while (!m_bufferingFinished) { + setState(Phonon::BufferingState); + qWarning() << QLatin1String("buffer underun"); + Sleep(20); + } + + setState(Phonon::PlayingState); + + //if size == o then stop... + if (m_nextBufferIndex) { + int size = m_soundBuffer1.waveHeader->dwBufferLength = m_soundBuffer1.data.size(); + if (size == buffer_size) { + playBuffer(m_soundBuffer1.waveHeader); + emit outOfData(m_stream, &m_soundBuffer1.data, &m_bufferingFinished); + } else { + playBuffer(m_soundBuffer1.waveHeader); + m_stopped = true; + setState(Phonon::StoppedState); + emit finished(); + seek(0); + } + } else { + int size = m_soundBuffer2.waveHeader->dwBufferLength = m_soundBuffer2.data.size(); + if (size == buffer_size) { + playBuffer(m_soundBuffer2.waveHeader); + emit outOfData(m_stream, &m_soundBuffer2.data, &m_bufferingFinished); + } else { + playBuffer(m_soundBuffer2.waveHeader); + m_stopped = true; + setState(Phonon::StoppedState); + emit finished(); + seek(0); + } + } + m_nextBufferIndex =! m_nextBufferIndex; + } + + + void MediaObject::playBuffer(WAVEHDR *waveHeader) + { + DWORD err = waveOutWrite(m_hWaveOut, waveHeader, sizeof(WAVEHDR)); + if (!err == MMSYSERR_NOERROR) { + setError(Phonon::FatalError, QLatin1String("cannot play sound buffer (system) ") + getErrorText(err)); + m_stopped = true; + } + } + } +} + +QT_END_NAMESPACE + +#include "mediaobject.moc" diff --git a/src/3rdparty/phonon/waveout/mediaobject.h b/src/3rdparty/phonon/waveout/mediaobject.h new file mode 100644 index 0000000..dd6b24b --- /dev/null +++ b/src/3rdparty/phonon/waveout/mediaobject.h @@ -0,0 +1,162 @@ +/* This file is part of the KDE project. + +Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + +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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef PHONON_MEDIAOBJECT_H +#define PHONON_MEDIAOBJECT_H + +#include <phonon/mediaobjectinterface.h> + +#include <QtCore/QHash> +#include <QtCore/QObject> +#include <QtCore/QQueue> +#include <QtCore/QBasicTimer> +#include <QtCore/QWaitCondition> +#include <QtCore/QMutex> +#include <QtCore/QThread> +#include <QFile> +#include <QIODevice> + +#include <windows.h> + + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + class MediaSource; + + namespace WaveOut + { + class WorkerThread; + class AudioOutput; + + class MediaObject : public QObject, public Phonon::MediaObjectInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::MediaObjectInterface) + + public: + MediaObject(QObject *parent); + ~MediaObject(); + Phonon::State state() const; + bool hasVideo() const; + bool isSeekable() const; + qint64 currentTime() const; + qint32 tickInterval() const; + + void setTickInterval(qint32 newTickInterval); + void play(); + void pause(); + void stop(); + void seek(qint64 time); + + QString errorString() const; + Phonon::ErrorType errorType() const; + qint64 totalTime() const; + qint32 prefinishMark() const; + void setPrefinishMark(qint32 newPrefinishMark); + qint32 transitionTime() const; + void setTransitionTime(qint32); + qint64 remainingTime() const; + MediaSource source() const; + void setSource(const MediaSource &source); + void setNextSource(const MediaSource &source); + + + Q_SIGNALS: + void stateChanged(Phonon::State newstate, Phonon::State oldstate); + void tick(qint64 time); + void metaDataChanged(QMultiMap<QString, QString>); + void seekableChanged(bool); + void hasVideoChanged(bool); + void bufferStatus(int); + void finished(); + void prefinishMarkReached(qint32); + void aboutToFinish(); + void totalTimeChanged(qint64 length) const; + void currentSourceChanged(const MediaSource &); + void outOfData(QIODevice *ioStream, QByteArray *buffer, bool *m_bufferingFinshed); + + protected: + void setAudioOutput(QObject *audioOutput); + + private Q_SLOTS: + void setVolume(qreal newVolume); + + private: + bool m_nextBufferIndex; + bool prepareBuffers(); + void unPrepareBuffers(); + bool getWaveOutDevice(); + bool openWaveFile(QString fileName); + bool readHeader(); + bool boolUpdateBuffer(); + bool fillBuffers(); + void swapBuffers(); + void setState(Phonon::State newState); + void setError(ErrorType errorType, QString errorMessage); + void deleteValidWaveOutDevice(); + void playBuffer(WAVEHDR *waveHeader); + + static void CALLBACK WaveOutCallBack(HWAVEOUT hWaveOut, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2); + + struct { + WAVEHDR *waveHeader; + QByteArray data; + } m_soundBuffer1, m_soundBuffer2; + + WAVEFORMATEX m_waveFormatEx; + HWAVEOUT m_hWaveOut; + + QFile *m_file; + QIODevice *m_stream; + QString m_errorString; + + WorkerThread *m_thread; + + MediaSource m_source; + MediaSource m_nextSource; + AudioOutput *m_audioOutput; + ErrorType m_errorType; + + qreal m_volume; + qint64 m_mediaSize; + qint64 m_totalTime; + qint64 m_currentTime; + qint64 m_transitionTime; + qint64 m_prefinishMark; + qint64 m_tickIntervalResolution; + qint32 m_tickInterval; + qint32 m_tick; + Phonon::State m_state; + + bool m_bufferingFinished; + bool m_paused; + bool m_stopped; + bool m_hasNextSource; + bool m_hasSource; + bool m_sourceIsValid; + bool m_bufferPrepared; + + friend class Backend; + }; + } +} + +QT_END_NAMESPACE + +#endif // PHONON_MEDIAOBJECT_H |