diff --git a/CMakeLists.txt b/CMakeLists.txt index 59e6ca21..72540781 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,193 +1,181 @@ cmake_minimum_required(VERSION 3.5 FATAL_ERROR) project(Phonon VERSION "4.10.60") include(FeatureSummary) -option(PHONON_ASSERT_STATES "Enable code to assert backend state transitions" ON) option(PHONON_BUILD_EXPERIMENTAL "Build the experimental library" ON) option(PHONON_BUILD_DEMOS "Build the demos" OFF) option(PHONON_BUILD_DESIGNER_PLUGIN "Build the Qt Designer plugin" ON) option(PHONON_BUILD_DOC "Build the API documentation" OFF) option(PHONON_BUILD_SETTINGS "Build the settings GUI" ON) option(PHONON_NO_CAPTURE "Disable the capture capabilities") -# Compat: we used to have our own option, now we defer to KDEInstallDirs -if(PHONON_INSTALL_QT_EXTENSIONS_INTO_SYSTEM_QT) - message(DEPRECATION "PHONON_INSTALL_QT_EXTENSIONS_INTO_SYSTEM_QT is deprecated! use KDE_INSTALL_USE_QT_SYS_PATHS") - set(KDE_INSTALL_USE_QT_SYS_PATHS ON) -endif() - # Not set by ECM (yet) set(CMAKE_AUTOUIC TRUE) set(CMAKE_AUTORCC TRUE) find_package(ECM 5.60 NO_MODULE) set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules" URL "https://api.kde.org/frameworks/extra-cmake-modules/html/index.html") feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(CMakePackageConfigHelpers) include(ECMGeneratePriFile) include(ECMGeneratePkgConfigFile) include(KDEInstallDirs) include(ECMPoQmTools) include(KDECMakeSettings) include(KDECompilerSettings) set(PHONON_LIB_SONAME phonon4qt5) set(PHONON_LIB_SONAME_CAMEL Phonon4Qt5) include(ECMSetupVersion) ecm_setup_version(PROJECT VARIABLE_PREFIX PHONON SOVERSION 4 VERSION_HEADER phonon/phonon_version.h PACKAGE_VERSION_FILE ${PHONON_LIB_SONAME_CAMEL}ConfigVersion.cmake ) # Glorified write_basic_package_version_file call for experimental. Experimental # inherits the versions from Phonon proper, so we technically don't need this. ecm_setup_version(${PHONON_VERSION} VARIABLE_PREFIX PHONON_EXPERIMENTAL SOVERSION ${PHONON_SOVERSION} PACKAGE_VERSION_FILE ${PHONON_LIB_SONAME_CAMEL}ExperimentalConfigVersion.cmake ) -if (PHONON_ASSERT_STATES) - message(STATUS "Enabling backend state machine validation.") - add_definitions(-DPHONON_ASSERT_STATES) -endif (PHONON_ASSERT_STATES) - set(INCLUDE_INSTALL_DIR "${KDE_INSTALL_INCLUDEDIR}/${PHONON_LIB_SONAME}") set(PLUGIN_INSTALL_DIR ${KDE_INSTALL_QTPLUGINDIR}) if(WIN32) # Imported from Phonon VLC set(PLUGIN_INSTALL_DIR ${KDE_INSTALL_BINDIR}) endif() set(BACKEND_DIR_SUFFIX "${PHONON_LIB_SONAME}_backend") set(BACKEND_INSTALL_DIR "${PLUGIN_INSTALL_DIR}/${BACKEND_DIR_SUFFIX}") set(PHONON_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") ################################# Requirements ################################# # Core Requirements find_package(Qt5Core) set_package_properties(Qt5Core PROPERTIES TYPE REQUIRED DESCRIPTION "Qt5 core module" URL "https://www.qt.io/download") find_package(Qt5Gui) set_package_properties(Qt5Gui PROPERTIES TYPE REQUIRED DESCRIPTION "Qt5 GUI module" URL "https://www.qt.io/download") find_package(Qt5Widgets) set_package_properties(Qt5Widgets PROPERTIES TYPE REQUIRED DESCRIPTION "Qt5 widgets module" URL "https://www.qt.io/download") # Designer Plugin find_package(Qt5Designer) set_package_properties(Qt5Designer PROPERTIES TYPE RECOMMENDED DESCRIPTION "Needed to build the Qt Designer plugin" URL "https://www.qt.io/download") set(BUILDSYSTEM_INSTALL_DIR ${CMAKE_INSTALL_FULL_DATAROOTDIR}/${PHONON_LIB_SONAME}/buildsystem/) add_subdirectory(cmake) if(Qt5Designer_FOUND AND PHONON_BUILD_DESIGNER_PLUGIN) add_subdirectory(designer) endif() if(PHONON_BUILD_DOC) add_subdirectory(doc) endif() add_subdirectory(phonon) add_subdirectory(includes) if(PHONON_BUILD_DEMOS) message(STATUS "Building demos.") # Demos may also be built standalone, so we need to skip their find_package call! set(CMAKE_DISABLE_FIND_PACKAGE_Phonon4Qt5 TRUE) add_subdirectory(demos) endif() if(PHONON_BUILD_SETTINGS) add_subdirectory(settings) endif() set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/${PHONON_LIB_SONAME}") configure_package_config_file( PhononConfig.cmake.in ${PHONON_LIB_SONAME_CAMEL}Config.cmake PATH_VARS INCLUDE_INSTALL_DIR KDE_INSTALL_LIBDIR BUILDSYSTEM_INSTALL_DIR INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PHONON_LIB_SONAME_CAMEL}Config.cmake ${CMAKE_CURRENT_BINARY_DIR}/${PHONON_LIB_SONAME_CAMEL}ConfigVersion.cmake DESTINATION ${CMAKECONFIG_INSTALL_DIR} COMPONENT Devel) install(EXPORT PhononLibs DESTINATION ${CMAKECONFIG_INSTALL_DIR} NAMESPACE Phonon:: FILE PhononTargets.cmake) if(PHONON_BUILD_EXPERIMENTAL) configure_package_config_file( PhononExperimentalConfig.cmake.in ${PHONON_LIB_SONAME_CAMEL}ExperimentalConfig.cmake PATH_VARS INCLUDE_INSTALL_DIR KDE_INSTALL_LIBDIR BUILDSYSTEM_INSTALL_DIR INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PHONON_LIB_SONAME_CAMEL}ExperimentalConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/${PHONON_LIB_SONAME_CAMEL}ExperimentalConfigVersion.cmake DESTINATION ${CMAKECONFIG_INSTALL_DIR} COMPONENT Devel ) install(EXPORT PhononExperimentalLibs DESTINATION ${CMAKECONFIG_INSTALL_DIR} NAMESPACE Phonon:: FILE PhononExperimentalTargets.cmake) endif() ecm_generate_pkgconfig_file( BASE_NAME ${PHONON_LIB_SONAME} INCLUDE_INSTALL_DIR ${INCLUDE_INSTALL_DIR} DESCRIPTION "Phonon library needed to build applications" INSTALL ) ecm_generate_pri_file( BASE_NAME ${PHONON_LIB_SONAME} LIB_NAME ${PHONON_LIB_SONAME} DEPS "core widgets" FILENAME_VAR pri_filename INCLUDE_INSTALL_DIR ${INCLUDE_INSTALL_DIR}/phonon ) install(FILES ${pri_filename} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po") ecm_install_po_files_as_qm(po) endif() feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/phonon/statesvalidator.cpp b/phonon/statesvalidator.cpp index f17752ae..a7097e8c 100644 --- a/phonon/statesvalidator.cpp +++ b/phonon/statesvalidator.cpp @@ -1,203 +1,199 @@ /* Copyright (C) 2012 Trever Fischer Copyright (C) 2012 Harald Sitter This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), Nokia Corporation (or its successors, if any) and the KDE Free Qt Foundation, which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "statesvalidator_p.h" #include "mediaobject.h" #include "phononnamespace_p.h" -#ifdef PHONON_ASSERT_STATES #define P_INVALID_STATE(msg) Q_ASSERT_X(0, __FILE__, msg) -#else -#define P_INVALID_STATE(msg) pDebug() << "State assert failed:" << msg -#endif namespace Phonon { StatesValidator::StatesValidator(MediaObject *parent) : QObject(parent) , m_mediaObject(parent) , m_prevState(Phonon::ErrorState) , m_sourceQueued(false) , m_aboutToFinishEmitted(false) , m_aboutToFinishBeforeSeek(false) , m_aboutToFinishPos(-1) { connect(m_mediaObject, SIGNAL(stateChanged(Phonon::State,Phonon::State)), this, SLOT(validateStateChange(Phonon::State,Phonon::State))); connect(m_mediaObject, SIGNAL(currentSourceChanged(Phonon::MediaSource)), this, SLOT(validateSourceChange())); connect(m_mediaObject, SIGNAL(tick(qint64)), this, SLOT(validateTick(qint64))); connect(m_mediaObject, SIGNAL(aboutToFinish()), this, SLOT(validateAboutToFinish())); connect(m_mediaObject, SIGNAL(finished()), this, SLOT(validateFinished())); connect(m_mediaObject, SIGNAL(bufferStatus(int)), this, SLOT(validateBufferStatus())); } StatesValidator::~StatesValidator() { } /** * The aboutToFinish signal is emitted when the queue is coming to an end. * This in particular means that it must not be emitted twice, unless no track * is in the queue and the user seeked back in time before finished * Since we track the frontend signal here, we only get this signal when the * queue is in fact empty in the frontend. * It can however happen that the frontend already delivered the last queue item, * then the user seeks and the backend forgets to use the already delivered item, * emitting a bogus aboutToFinish. */ void StatesValidator::validateAboutToFinish() { if (m_aboutToFinishEmitted) P_INVALID_STATE("aboutToFinish emitted more than once!"); m_aboutToFinishEmitted = true; m_aboutToFinishPos = m_pos; } void StatesValidator::validateFinished() { if (m_mediaObject->state() != Phonon::PlayingState) P_INVALID_STATE("Playback finished when we weren't playing!"); } void StatesValidator::validateSourceChange() { if (m_mediaObject->state() != Phonon::StoppedState && m_mediaObject->state() != Phonon::PlayingState && m_mediaObject->state() != Phonon::PausedState && m_mediaObject->state() != Phonon::BufferingState) { P_INVALID_STATE("Source got changed outside a valid state"); } m_sourceQueued = false; // Once we get the signal the backend internal one-source queue is definitely cleared. m_aboutToFinishEmitted = false; m_aboutToFinishBeforeSeek = false; } void StatesValidator::validateBufferStatus() { if (m_mediaObject->state() != Phonon::PlayingState && m_mediaObject->state() != Phonon::PausedState && m_mediaObject->state() != Phonon::BufferingState) { P_INVALID_STATE("Buffer status changed when we weren't supposed to be buffering"); } } void StatesValidator::validateTick(qint64 pos) { // Mind that Buffering is a concurrent state, you may have been playing and // then start buffering for example for a seek. if (m_mediaObject->state() != Phonon::PlayingState && (m_prevState != Phonon::PlayingState && m_mediaObject->state() != Phonon::BufferingState)) P_INVALID_STATE("Received tick outside of Playing state."); // If and only if we did not queue a new source may a seek back in time // result in a reemission of the signal. It should not, but it is allowed. // Point being, if the API consumer did not set one the first time, they // likely will not care about it a second time either. if (m_aboutToFinishEmitted && (pos < m_aboutToFinishPos) && !m_sourceQueued) m_aboutToFinishEmitted = false; m_pos = pos; } void StatesValidator::validateStateChange(Phonon::State newstate, Phonon::State oldstate) { if (!validateStateTransition(newstate, oldstate)) { pDebug() << "Invalid state transition:" << oldstate << "->" << newstate; P_INVALID_STATE("Invalid state transition"); } else { pDebug() << "Valid state transition:" << oldstate << "->" << newstate; } m_prevState = oldstate; } bool StatesValidator::validateStateTransition(Phonon::State newstate, Phonon::State oldstate) { // Non-playback states trigger a reset of aboutToFinish. switch (oldstate) { case Phonon::StoppedState: switch (newstate) { case Phonon::LoadingState: case Phonon::PlayingState: case Phonon::PausedState: return true; default: return false; } break; case Phonon::LoadingState: switch (newstate) { case Phonon::ErrorState: case Phonon::StoppedState: return true; default: return false; } break; case Phonon::ErrorState: switch (newstate) { case Phonon::LoadingState: return true; default: return false; } break; case Phonon::PlayingState: switch (newstate) { case Phonon::PausedState: case Phonon::BufferingState: case Phonon::ErrorState: case Phonon::StoppedState: return true; default: return false; } break; case Phonon::PausedState: switch (newstate) { case Phonon::PlayingState: case Phonon::BufferingState: case Phonon::ErrorState: case Phonon::StoppedState: return true; default: return false; } break; case Phonon::BufferingState: switch (newstate) { case Phonon::PlayingState: case Phonon::PausedState: case Phonon::ErrorState: // TODO: buffering state needs fixing, should not transit to stop case Phonon::StoppedState: return true; default: return false; } } return false; } } // namespace Phonon