diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 000000000..c5ec1048c --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,22 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 90 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 10 +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - security + - enhancement +# Label to use when marking an issue as stale +staleLabel: stale +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as **stale** because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false +# Only close issues +only: issues +# Don't close issues which are assigned to somebody +exemptAssignees: true diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 612f234cf..f998c3d45 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -24,7 +24,7 @@ linux-builder: - ~/auto-update-docs "$CI_PROJECT_DIR/build" "$CI_COMMIT_REF_NAME" - mv install-x64/lib/python3.4/site-packages/*openshot* install-x64/python - echo -e "CI_PROJECT_NAME:$CI_PROJECT_NAME\nCI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME\nCI_COMMIT_SHA:$CI_COMMIT_SHA\nCI_JOB_ID:$CI_JOB_ID" > "install-x64/share/$CI_PROJECT_NAME" - - git log $(git describe --tags --abbrev=0)..HEAD --oneline --pretty=format:"%C(auto,yellow)%h%C(auto,magenta)% %C(auto,blue)%>(12,trunc)%ad %C(auto,green)%<(25,trunc)%aN%C(auto,reset)%s%C(auto,red)% gD% D" --date=short > "install-x64/share/$CI_PROJECT_NAME.log" + - git log $(git describe --tags --abbrev=0 @^)..@ --oneline --pretty=format:"%C(auto,yellow)%h%C(auto,magenta)% %C(auto,blue)%>(12,trunc)%ad %C(auto,green)%<(25,trunc)%aN%C(auto,reset)%s%C(auto,red)% gD% D" --date=short > "install-x64/share/$CI_PROJECT_NAME.log" when: always except: - tags @@ -46,12 +46,12 @@ mac-builder: - export LIBOPENSHOT_AUDIO_DIR=$CI_PROJECT_DIR/build/install-x64 - mkdir -p build; cd build; - mkdir -p install-x64/python; - - cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -D"CMAKE_INSTALL_PREFIX:PATH=$CI_PROJECT_DIR/build/install-x64" -DCMAKE_CXX_COMPILER=/usr/local/opt/gcc48/bin/g++-4.8 -DCMAKE_C_COMPILER=/usr/local/opt/gcc48/bin/gcc-4.8 -DCMAKE_PREFIX_PATH=/usr/local/qt5/5.5/clang_64 -DPYTHON_INCLUDE_DIR=/Library/Frameworks/Python.framework/Versions/3.6/include/python3.6m -DPYTHON_LIBRARY=/Library/Frameworks/Python.framework/Versions/3.6/lib/libpython3.6.dylib -DPython_FRAMEWORKS=/Library/Frameworks/Python.framework/ -D"CMAKE_BUILD_TYPE:STRING=Debug" -D"CMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk" -D"CMAKE_OSX_DEPLOYMENT_TARGET=10.9" -D"CMAKE_INSTALL_RPATH_USE_LINK_PATH=1" -D"ENABLE_RUBY=0" ../ + - cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -D"CMAKE_INSTALL_PREFIX:PATH=$CI_PROJECT_DIR/build/install-x64" -DCMAKE_CXX_COMPILER=/usr/local/opt/gcc48/bin/g++-4.8 -DCMAKE_C_COMPILER=/usr/local/opt/gcc48/bin/gcc-4.8 -DCMAKE_PREFIX_PATH=/usr/local/qt5/5.5/clang_64 -DPYTHON_INCLUDE_DIR=/Library/Frameworks/Python.framework/Versions/3.6/include/python3.6m -DPYTHON_LIBRARY=/Library/Frameworks/Python.framework/Versions/3.6/lib/libpython3.6.dylib -DPython_FRAMEWORKS=/Library/Frameworks/Python.framework/ -D"CMAKE_BUILD_TYPE:STRING=Release" -D"CMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk" -D"CMAKE_OSX_DEPLOYMENT_TARGET=10.9" -D"CMAKE_INSTALL_RPATH_USE_LINK_PATH=1" -D"ENABLE_RUBY=0" ../ - make - make install - mv install-x64/lib/python3.6/site-packages/*openshot* install-x64/python - echo -e "CI_PROJECT_NAME:$CI_PROJECT_NAME\nCI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME\nCI_COMMIT_SHA:$CI_COMMIT_SHA\nCI_JOB_ID:$CI_JOB_ID" > "install-x64/share/$CI_PROJECT_NAME" - - git log $(git describe --tags --abbrev=0)..HEAD --oneline --pretty=format:"%C(auto,yellow)%h%C(auto,magenta)% %C(auto,blue)%>(12,trunc)%ad %C(auto,green)%<(25,trunc)%aN%C(auto,reset)%s%C(auto,red)% gD% D" --date=short > "install-x64/share/$CI_PROJECT_NAME.log" + - git log $(git describe --tags --abbrev=0 @^)..@ --oneline --pretty=format:"%C(auto,yellow)%h%C(auto,magenta)% %C(auto,blue)%>(12,trunc)%ad %C(auto,green)%<(25,trunc)%aN%C(auto,reset)%s%C(auto,red)% gD% D" --date=short > "install-x64/share/$CI_PROJECT_NAME.log" when: always except: - tags @@ -79,8 +79,8 @@ windows-builder-x64: - mingw32-make install - Move-Item -Force -path "install-x64\lib\python3.7\site-packages\*openshot*" -destination "install-x64\python\" - New-Item -path "install-x64/share/" -Name "$CI_PROJECT_NAME" -Value "CI_PROJECT_NAME:$CI_PROJECT_NAME`nCI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME`nCI_COMMIT_SHA:$CI_COMMIT_SHA`nCI_JOB_ID:$CI_JOB_ID" -ItemType file -force - - $PREV_GIT_LABEL=(git describe --tags --abbrev=0) - - git log "$PREV_GIT_LABEL..HEAD" --oneline --pretty=format:"%C(auto,yellow)%h%C(auto,magenta)% %C(auto,blue)%>(12,trunc)%ad %C(auto,green)%<(25,trunc)%aN%C(auto,reset)%s%C(auto,red)% gD% D" --date=short > "install-x64/share/$CI_PROJECT_NAME.log" + - $PREV_GIT_LABEL=(git describe --tags --abbrev=0 '@^') + - git log "$PREV_GIT_LABEL..@" --oneline --pretty=format:"%C(auto,yellow)%h%C(auto,magenta)% %C(auto,blue)%>(12,trunc)%ad %C(auto,green)%<(25,trunc)%aN%C(auto,reset)%s%C(auto,red)% gD% D" --date=short > "install-x64/share/$CI_PROJECT_NAME.log" when: always except: - tags @@ -108,8 +108,8 @@ windows-builder-x86: - mingw32-make install - Move-Item -Force -path "install-x86\lib\python3.7\site-packages\*openshot*" -destination "install-x86\python\" - New-Item -path "install-x86/share/" -Name "$CI_PROJECT_NAME" -Value "CI_PROJECT_NAME:$CI_PROJECT_NAME`nCI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME`nCI_COMMIT_SHA:$CI_COMMIT_SHA`nCI_JOB_ID:$CI_JOB_ID" -ItemType file -force - - $PREV_GIT_LABEL=(git describe --tags --abbrev=0) - - git log "$PREV_GIT_LABEL..HEAD" --oneline --pretty=format:"%C(auto,yellow)%h%C(auto,magenta)% %C(auto,blue)%>(12,trunc)%ad %C(auto,green)%<(25,trunc)%aN%C(auto,reset)%s%C(auto,red)% gD% D" --date=short > "install-x86/share/$CI_PROJECT_NAME.log" + - $PREV_GIT_LABEL=(git describe --tags --abbrev=0 '@^') + - git log "$PREV_GIT_LABEL..@" --oneline --pretty=format:"%C(auto,yellow)%h%C(auto,magenta)% %C(auto,blue)%>(12,trunc)%ad %C(auto,green)%<(25,trunc)%aN%C(auto,reset)%s%C(auto,red)% gD% D" --date=short > "install-x86/share/$CI_PROJECT_NAME.log" when: always except: - tags diff --git a/.travis.yml b/.travis.yml index 1034042d9..e2c3d5f15 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,7 @@ addons: - qtmultimedia5-dev - doxygen - graphviz + - curl packages: &ff_common # Common set of FFmpeg packages - *p_common - libfdk-aac-dev @@ -33,20 +34,12 @@ addons: matrix: include: - - name: "FFmpeg 2 GCC (Ubuntu 16.04 Xenial)" - env: BUILD_VERSION=ffmpeg2 - os: linux - dist: xenial - addons: - apt: - sources: - - sourceline: 'ppa:openshot.developers/libopenshot-daily' - - sourceline: 'ppa:beineri/opt-qt-5.10.0-xenial' - packages: - - *ff_common - - name: "FFmpeg 3 GCC (Ubuntu 18.04 Bionic)" - env: BUILD_VERSION=ffmpeg3 + - name: "Coverage + FFmpeg 3.4 GCC (Ubuntu 18.04 Bionic)" + env: + - BUILD_VERSION=coverage_ffmpeg34 + - CMAKE_EXTRA_ARGS="-DENABLE_COVERAGE=1" + - TEST_TARGET=coverage os: linux dist: bionic addons: @@ -57,9 +50,15 @@ matrix: packages: - *ff_common - qt5-default + - libjsoncpp-dev + - lcov + - binutils-common # For c++filt - name: "FFmpeg 4 GCC (Ubuntu 18.04 Bionic)" - env: BUILD_VERSION=ffmpeg4 + env: + - BUILD_VERSION=ffmpeg4 + - CMAKE_EXTRA_ARGS="" + - TEST_TARGET=test os: linux dist: bionic addons: @@ -71,6 +70,7 @@ matrix: packages: - *ff_common - qt5-default + - libjsoncpp-dev - libavcodec58 - libavformat58 - libavdevice58 @@ -81,8 +81,11 @@ matrix: - libavresample4 - libswresample3 - - name: "FFmpeg 3 Clang (Ubuntu 18.04 Bionic)" - env: BUILD_VERSION=ffmpeg3 + - name: "FFmpeg 3.4 Clang (Ubuntu 18.04 Bionic)" + env: + - BUILD_VERSION=clang_ffmpeg34 + - CMAKE_EXTRA_ARGS="" + - TEST_TARGET=test os: linux dist: bionic compiler: clang @@ -96,9 +99,53 @@ matrix: - qt5-default - libomp-dev + - name: "FFmpeg 3.2 GCC (Ubuntu 16.04 Xenial)" + env: + - BUILD_VERSION=ffmpeg32 + - CMAKE_EXTRA_ARGS="" + - TEST_TARGET="os_test" + os: linux + dist: xenial + addons: + apt: + sources: + - sourceline: 'ppa:openshot.developers/libopenshot-daily' + - sourceline: 'ppa:beineri/opt-qt-5.10.0-xenial' + - sourceline: 'ppa:jon-hedgerows/ffmpeg-backports' + packages: + - *ff_common + - libavcodec57 + - libavdevice57 + - libavfilter6 + - libavformat57 + - libavresample3 + - libavutil55 + - libpostproc54 + - libswresample2 + - libswscale4 + + - name: "FFmpeg 2 GCC (Ubuntu 16.04 Xenial)" + env: + - BUILD_VERSION=ffmpeg2 + - CMAKE_EXTRA_ARGS="" + - TEST_TARGET="os_test" + os: linux + dist: xenial + addons: + apt: + sources: + - sourceline: 'ppa:openshot.developers/libopenshot-daily' + - sourceline: 'ppa:beineri/opt-qt-5.10.0-xenial' + packages: + - *ff_common + script: - mkdir -p build; cd build; - - cmake -DCMAKE_BUILD_TYPE:STRING="Debug" ../ + - cmake -DCMAKE_BUILD_TYPE:STRING="Debug" ${CMAKE_EXTRA_ARGS} ../ - make VERBOSE=1 - - make os_test + - make ${TEST_TARGET} - make install DESTDIR="$BUILD_VERSION" + - cd .. + +after_success: + - if [ "x$TEST_TARGET" = "xcoverage" ]; then bash <(curl -s https://codecov.io/bash) -f build/coverage.info || echo "Codecov did not collect coverage reports"; fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 5cfb678c9..a0e8a5feb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,8 +40,8 @@ For more information, please visit . set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/Modules") ################ PROJECT VERSION #################### -set(PROJECT_VERSION_FULL "0.2.3-dev1") -set(PROJECT_SO_VERSION 17) +set(PROJECT_VERSION_FULL "0.2.5-dev2") +set(PROJECT_SO_VERSION 19) # Remove the dash and anything following, to get the #.#.# version for project() STRING(REGEX REPLACE "\-.*$" "" VERSION_NUM "${PROJECT_VERSION_FULL}") @@ -66,12 +66,44 @@ Generating build files for OpenShot with CMake ${CMAKE_VERSION} # in order to properly configure CMAKE_INSTALL_LIBDIR path include(GNUInstallDirs) +# Collect and display summary of options/dependencies +include(FeatureSummary) + +################ OPTIONS ################## +# Optional build settings for libopenshot +option(USE_SYSTEM_JSONCPP "Use system installed JsonCpp, if found" ON) +option(DISABLE_BUNDLED_JSONCPP "Don't fall back to bundled JsonCpp" OFF) +option(ENABLE_IWYU "Enable 'Include What You Use' scanner (CMake 3.3+)" OFF) +option(ENABLE_TESTS "Build unit tests (requires UnitTest++)" ON) +option(ENABLE_DOCS "Build API documentation (requires Doxygen)" ON) + +# Legacy commandline override +if (DISABLE_TESTS) + if(ENABLE_COVERAGE) + message(WARNING "ENABLE_COVERAGE requires tests, overriding DISABLE_TESTS") + set(ENABLE_TESTS ON) + else() + set(ENABLE_TESTS OFF) + endif() +endif() + +if(DEFINED ENABLE_TESTS) + set(ENABLE_TESTS ${ENABLE_TESTS} CACHE BOOL "Build unit tests (requires UnitTest++)" FORCE) +endif() + ########## Configure Version.h header ############## configure_file(include/OpenShotVersion.h.in include/OpenShotVersion.h @ONLY) # We'll want that installed later install(FILES ${CMAKE_CURRENT_BINARY_DIR}/include/OpenShotVersion.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libopenshot) +#### Work around a GCC < 9 bug with handling of _Pragma() in macros +#### See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55578 +if ((${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") AND + (${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS "9.0.0")) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -no-integrated-cpp") +endif() + #### Enable C++11 (for std::shared_ptr support) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -85,29 +117,67 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_BINARY_DIR}/include) +############## Code Coverage ######################### +if (DISABLE_TESTS AND ENABLE_COVERAGE) + message(WARNING "ENABLE_COVERAGE requires tests, overriding DISABLE_TESTS") + set(DISABLE_TESTS OFF CACHE BOOL "Don't build unit tests" FORCE) +endif() + +if (ENABLE_COVERAGE) + if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Debug") + message(STATUS "Coverage enabled, setting build type to Debug") + endif() + include(CodeCoverage) + append_coverage_compiler_flags() +endif() +add_feature_info("Coverage" ENABLE_COVERAGE "analyze test coverage and generate report") + ############## PROCESS src/ DIRECTORIES ############## add_subdirectory(src) ################### DOCUMENTATION ################### # Find Doxygen (used for documentation) -include(cmake/Modules/UseDoxygen.cmake) - -# Doxygen was found -if (TARGET doc) - message(STATUS "Doxygen found, documentation target enabled") - message("\nTo compile documentation in doc/html, run: 'make doc'") - - # Install docs, if the user builds them with `make doc` - install(CODE "MESSAGE(\"Checking for documentation files to install...\")") - install(CODE "MESSAGE(\"(Compile with 'make doc' command, requires Doxygen)\")") - - install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc/html/ - DESTINATION ${CMAKE_INSTALL_DOCDIR}/API - MESSAGE_NEVER # Don't spew about file copies - OPTIONAL ) # No error if the docs aren't found +set(DOCS_ENABLED FALSE) # Only set true if Doxygen is found and configured +if (ENABLE_DOCS) + include(cmake/Modules/UseDoxygen.cmake) + + # Doxygen was found + if (TARGET doc) + message(STATUS "Doxygen found, documentation target enabled") + set(DOCS_ENABLED TRUE) + + # Install docs, if the user builds them with `make doc` + install(CODE "MESSAGE(\"Checking for documentation files to install...\")") + install(CODE "MESSAGE(\"(Compile with 'make doc' command, requires Doxygen)\")") + + install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc/html/ + DESTINATION ${CMAKE_INSTALL_DOCDIR}/API + MESSAGE_NEVER # Don't spew about file copies + OPTIONAL ) # No error if the docs aren't found + endif() endif() +add_feature_info("Documentation" DOCS_ENABLED "Build API documentation with 'make doc'") ############# PROCESS tests/ DIRECTORY ############## -if(NOT DISABLE_TESTS) +if(ENABLE_TESTS) + set(TESTS_ENABLED TRUE) # May be overridden by tests/CMakeLists.txt add_subdirectory(tests) endif() +add_feature_info("Unit tests" TESTS_ENABLED "Compile unit tests for library functions") + +############## COVERAGE REPORTING ################# +if (ENABLE_COVERAGE) + setup_target_for_coverage_lcov( + NAME coverage + LCOV_ARGS "--no-external" + EXECUTABLE openshot-test + DEPENDENCIES openshot-test) + message("Generate coverage report with 'make coverage'") +endif() + +########### PRINT FEATURE SUMMARY ############## +feature_summary(WHAT ALL + INCLUDE_QUIET_PACKAGES + FATAL_ON_MISSING_REQUIRED_PACKAGES + DESCRIPTION "Displaying feature summary\n\nBuild configuration:") diff --git a/cmake/Modules/CodeCoverage.cmake b/cmake/Modules/CodeCoverage.cmake new file mode 100644 index 000000000..fde7f535c --- /dev/null +++ b/cmake/Modules/CodeCoverage.cmake @@ -0,0 +1,435 @@ +# Copyright (c) 2012 - 2017, Lars Bilke +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# CHANGES: +# +# 2012-01-31, Lars Bilke +# - Enable Code Coverage +# +# 2013-09-17, Joakim Söderberg +# - Added support for Clang. +# - Some additional usage instructions. +# +# 2016-02-03, Lars Bilke +# - Refactored functions to use named parameters +# +# 2017-06-02, Lars Bilke +# - Merged with modified version from github.com/ufz/ogs +# +# 2019-05-06, Anatolii Kurotych +# - Remove unnecessary --coverage flag +# +# 2019-12-13, FeRD (Frank Dana) +# - Deprecate COVERAGE_LCOVR_EXCLUDES and COVERAGE_GCOVR_EXCLUDES lists in favor +# of tool-agnostic COVERAGE_EXCLUDES variable, or EXCLUDE setup arguments. +# - CMake 3.4+: All excludes can be specified relative to BASE_DIRECTORY +# - All setup functions: accept BASE_DIRECTORY, EXCLUDE list +# - Set lcov basedir with -b argument +# - Add automatic --demangle-cpp in lcovr, if 'c++filt' is available (can be +# overridden with NO_DEMANGLE option in setup_target_for_coverage_lcovr().) +# - Delete output dir, .info file on 'make clean' +# - Remove Python detection, since version mismatches will break gcovr +# - Minor cleanup (lowercase function names, update examples...) +# +# 2019-12-19, FeRD (Frank Dana) +# - Rename Lcov outputs, make filtered file canonical, fix cleanup for targets +# +# 2020-01-19, Bob Apthorpe +# - Added gfortran support +# +# 2020-02-17, FeRD (Frank Dana) +# - Make all add_custom_target()s VERBATIM to auto-escape wildcard characters +# in EXCLUDEs, and remove manual escaping from gcovr targets +# +# USAGE: +# +# 1. Copy this file into your cmake modules path. +# +# 2. Add the following line to your CMakeLists.txt: +# include(CodeCoverage) +# +# 3. Append necessary compiler flags: +# append_coverage_compiler_flags() +# +# 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og +# +# 4. If you need to exclude additional directories from the report, specify them +# using full paths in the COVERAGE_EXCLUDES variable before calling +# setup_target_for_coverage_*(). +# Example: +# set(COVERAGE_EXCLUDES +# '${PROJECT_SOURCE_DIR}/src/dir1/*' +# '/path/to/my/src/dir2/*') +# Or, use the EXCLUDE argument to setup_target_for_coverage_*(). +# Example: +# setup_target_for_coverage_lcov( +# NAME coverage +# EXECUTABLE testrunner +# EXCLUDE "${PROJECT_SOURCE_DIR}/src/dir1/*" "/path/to/my/src/dir2/*") +# +# 4.a NOTE: With CMake 3.4+, COVERAGE_EXCLUDES or EXCLUDE can also be set +# relative to the BASE_DIRECTORY (default: PROJECT_SOURCE_DIR) +# Example: +# set(COVERAGE_EXCLUDES "dir1/*") +# setup_target_for_coverage_gcovr_html( +# NAME coverage +# EXECUTABLE testrunner +# BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src" +# EXCLUDE "dir2/*") +# +# 5. Use the functions described below to create a custom make target which +# runs your test executable and produces a code coverage report. +# +# 6. Build a Debug build: +# cmake -DCMAKE_BUILD_TYPE=Debug .. +# make +# make my_coverage_target +# + +include(CMakeParseArguments) + +# Check prereqs +find_program( GCOV_PATH gcov ) +find_program( LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl) +find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat ) +find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test) +find_program( CPPFILT_PATH NAMES c++filt ) + +if(NOT GCOV_PATH) + message(FATAL_ERROR "gcov not found! Aborting...") +endif() # NOT GCOV_PATH + +if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang") + if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3) + message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...") + endif() +elseif(NOT CMAKE_COMPILER_IS_GNUCXX) + if("${CMAKE_Fortran_COMPILER_ID}" MATCHES "[Ff]lang") + # Do nothing; exit conditional without error if true + elseif("${CMAKE_Fortran_COMPILER_ID}" MATCHES "GNU") + # Do nothing; exit conditional without error if true + else() + message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...") + endif() +endif() + +set(COVERAGE_COMPILER_FLAGS "-g -fprofile-arcs -ftest-coverage" + CACHE INTERNAL "") + +set(CMAKE_Fortran_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the Fortran compiler during coverage builds." + FORCE ) +set(CMAKE_CXX_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the C++ compiler during coverage builds." + FORCE ) +set(CMAKE_C_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the C compiler during coverage builds." + FORCE ) +set(CMAKE_EXE_LINKER_FLAGS_COVERAGE + "" + CACHE STRING "Flags used for linking binaries during coverage builds." + FORCE ) +set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE + "" + CACHE STRING "Flags used by the shared libraries linker during coverage builds." + FORCE ) +mark_as_advanced( + CMAKE_Fortran_FLAGS_COVERAGE + CMAKE_CXX_FLAGS_COVERAGE + CMAKE_C_FLAGS_COVERAGE + CMAKE_EXE_LINKER_FLAGS_COVERAGE + CMAKE_SHARED_LINKER_FLAGS_COVERAGE ) + +if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading") +endif() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug" + +if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") + link_libraries(gcov) +endif() + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_lcov( +# NAME testrunner_coverage # New target name +# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES testrunner # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative +# # to BASE_DIRECTORY, with CMake 3.4+) +# NO_DEMANGLE # Don't demangle C++ symbols +# # even if c++filt is found +# ) +function(setup_target_for_coverage_lcov) + + set(options NO_DEMANGLE) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT LCOV_PATH) + message(FATAL_ERROR "lcov not found! Aborting...") + endif() # NOT LCOV_PATH + + if(NOT GENHTML_PATH) + message(FATAL_ERROR "genhtml not found! Aborting...") + endif() # NOT GENHTML_PATH + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if(${Coverage_BASE_DIRECTORY}) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else() + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif() + + # Collect excludes (CMake 3.4+: Also compute absolute paths) + set(LCOV_EXCLUDES "") + foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_LCOV_EXCLUDES}) + if(CMAKE_VERSION VERSION_GREATER 3.4) + get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) + endif() + list(APPEND LCOV_EXCLUDES "${EXCLUDE}") + endforeach() + list(REMOVE_DUPLICATES LCOV_EXCLUDES) + + # Conditional arguments + if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE}) + set(GENHTML_EXTRA_ARGS "--demangle-cpp") + endif() + + # Setup target + add_custom_target(${Coverage_NAME} + + # Cleanup lcov + COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory . -b ${BASEDIR} --zerocounters + # Create baseline to make sure untouched files show up in the report + COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c -i -d . -b ${BASEDIR} -o ${Coverage_NAME}.base + + # Run tests + COMMAND ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} + + # Capturing lcov counters and generating report + COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . -b ${BASEDIR} --capture --output-file ${Coverage_NAME}.capture + # add baseline counters + COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base -a ${Coverage_NAME}.capture --output-file ${Coverage_NAME}.total + # filter collected data to final coverage report + COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove ${Coverage_NAME}.total ${LCOV_EXCLUDES} --output-file ${Coverage_NAME}.info + + # Generate HTML output + COMMAND ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o ${Coverage_NAME} ${Coverage_NAME}.info + + # Set output files as GENERATED (will be removed on 'make clean') + BYPRODUCTS + ${Coverage_NAME}.base + ${Coverage_NAME}.capture + ${Coverage_NAME}.total + ${Coverage_NAME}.info + ${Coverage_NAME} # report directory + + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." + ) + + # Show where to find the lcov info report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info." + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." + ) + +endfunction() # setup_target_for_coverage_lcov + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_gcovr_xml( +# NAME ctest_coverage # New target name +# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES executable_target # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative +# # to BASE_DIRECTORY, with CMake 3.4+) +# ) +function(setup_target_for_coverage_gcovr_xml) + + set(options NONE) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT GCOVR_PATH) + message(FATAL_ERROR "gcovr not found! Aborting...") + endif() # NOT GCOVR_PATH + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if(${Coverage_BASE_DIRECTORY}) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else() + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif() + + # Collect excludes (CMake 3.4+: Also compute absolute paths) + set(GCOVR_EXCLUDES "") + foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES}) + if(CMAKE_VERSION VERSION_GREATER 3.4) + get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) + endif() + list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") + endforeach() + list(REMOVE_DUPLICATES GCOVR_EXCLUDES) + + # Combine excludes to several -e arguments + set(GCOVR_EXCLUDE_ARGS "") + foreach(EXCLUDE ${GCOVR_EXCLUDES}) + list(APPEND GCOVR_EXCLUDE_ARGS "-e") + list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}") + endforeach() + + add_custom_target(${Coverage_NAME} + # Run tests + ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} + + # Running gcovr + COMMAND ${GCOVR_PATH} --xml + -r ${BASEDIR} ${GCOVR_EXCLUDE_ARGS} + --object-directory=${PROJECT_BINARY_DIR} + -o ${Coverage_NAME}.xml + BYPRODUCTS ${Coverage_NAME}.xml + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Running gcovr to produce Cobertura code coverage report." + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml." + ) +endfunction() # setup_target_for_coverage_gcovr_xml + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_gcovr_html( +# NAME ctest_coverage # New target name +# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES executable_target # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative +# # to BASE_DIRECTORY, with CMake 3.4+) +# ) +function(setup_target_for_coverage_gcovr_html) + + set(options NONE) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT GCOVR_PATH) + message(FATAL_ERROR "gcovr not found! Aborting...") + endif() # NOT GCOVR_PATH + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if(${Coverage_BASE_DIRECTORY}) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else() + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif() + + # Collect excludes (CMake 3.4+: Also compute absolute paths) + set(GCOVR_EXCLUDES "") + foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES}) + if(CMAKE_VERSION VERSION_GREATER 3.4) + get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) + endif() + list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") + endforeach() + list(REMOVE_DUPLICATES GCOVR_EXCLUDES) + + # Combine excludes to several -e arguments + set(GCOVR_EXCLUDE_ARGS "") + foreach(EXCLUDE ${GCOVR_EXCLUDES}) + list(APPEND GCOVR_EXCLUDE_ARGS "-e") + list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}") + endforeach() + + add_custom_target(${Coverage_NAME} + # Run tests + ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} + + # Create folder + COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME} + + # Running gcovr + COMMAND ${GCOVR_PATH} --html --html-details + -r ${BASEDIR} ${GCOVR_EXCLUDE_ARGS} + --object-directory=${PROJECT_BINARY_DIR} + -o ${Coverage_NAME}/index.html + + BYPRODUCTS ${PROJECT_BINARY_DIR}/${Coverage_NAME} # report directory + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Running gcovr to produce HTML code coverage report." + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." + ) + +endfunction() # setup_target_for_coverage_gcovr_html + +function(append_coverage_compiler_flags) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}") +endfunction() # append_coverage_compiler_flags diff --git a/cmake/Modules/FindRESVG.cmake b/cmake/Modules/FindRESVG.cmake index b03a0667e..0538eacd5 100644 --- a/cmake/Modules/FindRESVG.cmake +++ b/cmake/Modules/FindRESVG.cmake @@ -1,28 +1,115 @@ -# - Try to find RESVG -# Once done this will define -# RESVG_FOUND - System has RESVG -# RESVG_INCLUDE_DIRS - The RESVG include directories -# RESVG_LIBRARIES - The libraries needed to use RESVG -find_path ( RESVG_INCLUDE_DIR ResvgQt.h - PATHS ${RESVGDIR}/include/resvg - $ENV{RESVGDIR}/include/resvg - $ENV{RESVGDIR}/include - /usr/include/resvg - /usr/include - /usr/local/include/resvg - /usr/local/include ) - -find_library ( RESVG_LIBRARY NAMES resvg - PATHS /usr/lib - /usr/local/lib - $ENV{RESVGDIR} - $ENV{RESVGDIR}/lib ) - -set ( RESVG_LIBRARIES ${RESVG_LIBRARY} ) -set ( RESVG_INCLUDE_DIRS ${RESVG_INCLUDE_DIR} ) - -include ( FindPackageHandleStandardArgs ) -# handle the QUIETLY and REQUIRED arguments and set RESVG_FOUND to TRUE -# if all listed variables are TRUE -find_package_handle_standard_args ( RESVG "Could NOT find RESVG, using Qt SVG parsing instead" RESVG_LIBRARY RESVG_INCLUDE_DIR ) -mark_as_advanced( RESVG_LIBRARY RESVG_INCLUDE_DIR ) +# vim: ts=2 sw=2 +#[=======================================================================[.rst: +FindRESVG +--------- +Try to find the shared-library build of resvg, the Rust SVG library + +IMPORTED targets +^^^^^^^^^^^^^^^^ + +This module defines :prop_tgt:`IMPORTED` target ``RESVG::resvg`` when +the library and headers are found. + +Result Variables +^^^^^^^^^^^^^^^^ + +This module defines the following variables: + +:: + + RESVG_FOUND - Library and header files found + RESVG_INCLUDE_DIRS - Include directory path + RESVG_LIBRARIES - Link path to the library + RESVG_DEFINITIONS - Compiler switches (currently unused) + +Backwards compatibility +^^^^^^^^^^^^^^^^^^^^^^^ + +For compatibility with previous versions of this module, uppercase names +for FFmpeg and for all components are also recognized, and all-uppercase +versions of the cache variables are also created. + +Control variables +^^^^^^^^^^^^^^^^^ + +The following variables can be used to provide path hints to the module: + +RESVGDIR - Set in the calling CMakeLists.txt or on the command line +ENV{RESVGDIR} - An environment variable in the cmake process context + +Copyright (c) 2020, FeRD (Frank Dana) +#]=======================================================================] +include(FindPackageHandleStandardArgs) + +# CMake 3.4+ only: Convert relative paths to absolute +if(DEFINED RESVGDIR AND CMAKE_VERSION VERSION_GREATER 3.4) + get_filename_component(RESVGDIR "${RESVGDIR}" ABSOLUTE + BASE_DIR ${CMAKE_CURRENT_BINARY_DIR}) +endif() + +find_path(RESVG_INCLUDE_DIRS + ResvgQt.h + PATHS + ${RESVGDIR} + ${RESVGDIR}/include + $ENV{RESVGDIR} + $ENV{RESVGDIR}/include + /usr/include + /usr/local/include + PATH_SUFFIXES + resvg + capi/include + resvg/capi/include +) + +find_library(RESVG_LIBRARIES + NAMES resvg + PATHS + ${RESVGDIR} + ${RESVGDIR}/lib + $ENV{RESVGDIR} + $ENV{RESVGDIR}/lib + /usr/lib + /usr/local/lib + PATH_SUFFIXES + resvg + target/release + resvg/target/release +) + +if (RESVG_INCLUDE_DIRS AND RESVG_LIBRARIES) + set(RESVG_FOUND TRUE) +endif() +set(RESVG_LIBRARIES ${RESVG_LIBRARIES} CACHE STRING "The Resvg library link path") +set(RESVG_INCLUDE_DIRS ${RESVG_INCLUDE_DIRS} CACHE STRING "The Resvg include directories") +set(RESVG_DEFINITIONS "" CACHE STRING "The Resvg CFLAGS") + +mark_as_advanced(RESVG_LIBRARIES RESVG_INCLUDE_DIRS RESVG_DEFINITIONS) + +# Give a nice error message if some of the required vars are missing. +find_package_handle_standard_args(RESVG + "Could NOT find RESVG, using Qt SVG parsing instead" + RESVG_LIBRARIES RESVG_INCLUDE_DIRS ) + +# Export target +if(RESVG_FOUND AND NOT TARGET RESVG::resvg) + message(STATUS "Creating IMPORTED target RESVG::resvg") + if (WIN32) + # Windows mis-links SHARED library targets + add_library(RESVG::resvg UNKNOWN IMPORTED) + else() + # Linux needs SHARED to link because libresvg has no SONAME + add_library(RESVG::resvg SHARED IMPORTED) + set_property(TARGET RESVG::resvg APPEND PROPERTY + IMPORTED_NO_SONAME TRUE) + endif() + + set_property(TARGET RESVG::resvg APPEND PROPERTY + INTERFACE_INCLUDE_DIRECTORIES "${RESVG_INCLUDE_DIRS}") + + set_property(TARGET RESVG::resvg APPEND PROPERTY + INTERFACE_COMPILE_DEFINITIONS "${RESVG_DEFINITIONS}") + + set_property(TARGET RESVG::resvg APPEND PROPERTY + IMPORTED_LOCATION "${RESVG_LIBRARIES}") +endif() diff --git a/cmake/Modules/FindUnitTest++.cmake b/cmake/Modules/FindUnitTest++.cmake index 545f62a88..ce1bdc458 100644 --- a/cmake/Modules/FindUnitTest++.cmake +++ b/cmake/Modules/FindUnitTest++.cmake @@ -1,43 +1,59 @@ -# Locate UNITTEST +# Locate UnitTest++ # This module defines -# UNITTEST++_LIBRARY -# UNITTEST++_FOUND, if false, do not try to link to gdal -# UNITTEST++_INCLUDE_DIR, where to find the headers - -FIND_PATH(UNITTEST++_INCLUDE_DIR UnitTest++.h - ${UNITTEST_DIR}/include/unittest++ - $ENV{UNITTEST_DIR}/include/unittest++ - $ENV{UNITTEST_DIR}/src +# UnitTest++_FOUND, if successful +# UnitTest++_LIBRARIES, the library path +# UnitTest++_INCLUDE_DIRS, where to find the headers + +find_package(PkgConfig QUIET) +if(PKG_CONFIG_FOUND) + pkg_check_modules(PC_UnitTest QUIET UnitTest++) + set(UnitTest++_VERSION ${PC_UnitTest_VERSION}) +endif() + + +FIND_PATH(UnitTest++_INCLUDE_DIRS UnitTest++.h + DOC + "Location of UnitTest++ header files" + PATH_SUFFIXES + unittest++ + UnitTest++ # Fedora, Arch + unittest-cpp # openSUSE + HINTS + ${PC_UnitTest++_INCLUDEDIR} + ${PC_UnitTest++_INCLUDE_DIRS} + PATHS + ${UnitTest++_ROOT} + ${UNITTEST_DIR} + $ENV{UNITTEST_DIR}/src $ENV{UNITTEST_DIR} ~/Library/Frameworks /Library/Frameworks - /usr/local/include - /usr/include - /usr/include/unittest++ - /usr/include/UnitTest++ # Fedora - /usr/include/unittest-cpp # openSUSE - /usr/local/include/UnitTest++/ # Arch - /sw/include # Fink - /opt/local/include # DarwinPorts - /opt/local/include/UnitTest++ - /opt/csw/include # Blastwave - /opt/include + /usr/local + /sw # Fink + /opt + /opt/local # DarwinPorts + /opt/csw # Blastwave [HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session\ Manager\\Environment]/include - /usr/freeware/include + /usr/freeware ) -FIND_LIBRARY(UNITTEST++_LIBRARY - NAMES unittest++ UnitTest++ - PATHS - ${UNITTEST_DIR}/lib - $ENV{UNITTEST_DIR}/lib - $ENV{UNITTEST_DIR}/build +FIND_LIBRARY(UnitTest++_LIBRARIES + NAMES unittest++ UnitTest++ + DOC + "Location of UnitTest++ shared library" + HINTS + ${PC_UnitTest++_LIBDIR} + ${PC_UnitTest++_LIBRARY_DIRS} + PATHS + ${UnitTest++_ROOT} + ${UnitTest++_ROOT}/lib + ${UNITTEST_DIR} $ENV{UNITTEST_DIR} + $ENV{UNITTEST_DIR}/lib + $ENV{UNITTEST_DIR}/build ~/Library/Frameworks /Library/Frameworks /usr/local/lib - /usr/lib - /usr/lib64/ # Fedora /sw/lib /opt/local/lib /opt/csw/lib @@ -46,13 +62,24 @@ FIND_LIBRARY(UNITTEST++_LIBRARY /usr/freeware/lib64 ) -SET(UNITTEST++_FOUND "NO") -IF(UNITTEST++_LIBRARY AND UNITTEST++_INCLUDE_DIR) - SET(UNITTEST++_FOUND "YES") -ENDIF(UNITTEST++_LIBRARY AND UNITTEST++_INCLUDE_DIR) +if(UnitTest++_LIBRARIES AND UnitTest++_INCLUDE_DIRS) + set(UnitTest++_FOUND TRUE) +endif() include(FindPackageHandleStandardArgs) -# handle the QUIETLY and REQUIRED arguments and set UNITTEST++_FOUND to TRUE -# if all listed variables are TRUE -find_package_handle_standard_args(UNITTEST++ DEFAULT_MSG - UNITTEST++_LIBRARY UNITTEST++_INCLUDE_DIR) +find_package_handle_standard_args(UnitTest++ + REQUIRED_VARS + UnitTest++_LIBRARIES + UnitTest++_INCLUDE_DIRS + VERSION_VAR + UnitTest++_VERSION +) + +# Excessive backwards-compatibility paranoia +set(UnitTest++_LIBRARY "${UnitTest++_LIBRARIES}" PARENT_SCOPE) +set(UnitTest++_INCLUDE_DIR "${UnitTest++_INCLUDE_DIRS}" PARENT_SCOPE) +# Even more excessive backwards-compatibility paranoia +set(UNITTEST++_FOUND "${UnitTest++_FOUND}" PARENT_SCOPE) +set(UNITTEST++_LIBRARY "${UnitTest++_LIBRARIES}" PARENT_SCOPE) +set(UNITTEST++_INCLUDE_DIR "${UnitTest++_INCLUDE_DIRS}" PARENT_SCOPE) + diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 000000000..dfdc93755 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,15 @@ +codecov: + branch: default +coverage: + status: + project: + default: + base: pr # Only post a status to pull requests + informational: true # Don't block PRs based on coverage stats (yet?) +ignore: + - "/src/examples" + - "/src/Qt/demo" + - "/thirdparty" + - "/doc" + - "/cmake" + - "/*.md" diff --git a/doc/HW-ACCEL.md b/doc/HW-ACCEL.md index cbcf5e63f..497ae2b9b 100644 --- a/doc/HW-ACCEL.md +++ b/doc/HW-ACCEL.md @@ -26,7 +26,7 @@ The following table summarizes our current level of support: ## Supported FFmpeg Versions -* HW accel is supported from FFmpeg version 3.2 (3.3 for nVidia drivers) +* HW accel is supported from FFmpeg version 3.4 * HW accel was removed for nVidia drivers in Ubuntu for FFmpeg 4+ **Notice:** The FFmpeg versions of Ubuntu and PPAs for Ubuntu show the diff --git a/include/AudioBufferSource.h b/include/AudioBufferSource.h index 42e55c943..3a17feb3c 100644 --- a/include/AudioBufferSource.h +++ b/include/AudioBufferSource.h @@ -31,14 +31,6 @@ #ifndef OPENSHOT_AUDIOBUFFERSOURCE_H #define OPENSHOT_AUDIOBUFFERSOURCE_H -/// Do not include the juce unittest headers, because it collides with unittest++ -#define __JUCE_UNITTEST_JUCEHEADER__ - -#ifndef _NDEBUG - /// Define NO debug for JUCE on mac os - #define _NDEBUG -#endif - #include #include "JuceHeader.h" diff --git a/include/AudioReaderSource.h b/include/AudioReaderSource.h index 33030adf1..c4e2d2480 100644 --- a/include/AudioReaderSource.h +++ b/include/AudioReaderSource.h @@ -31,14 +31,6 @@ #ifndef OPENSHOT_AUDIOREADERSOURCE_H #define OPENSHOT_AUDIOREADERSOURCE_H -/// Do not include the juce unittest headers, because it collides with unittest++ -#define __JUCE_UNITTEST_JUCEHEADER__ - -#ifndef _NDEBUG - /// Define NO debug for JUCE on mac os - #define _NDEBUG -#endif - #include #include "ReaderBase.h" #include "JuceHeader.h" diff --git a/include/AudioResampler.h b/include/AudioResampler.h index 96615cb3f..d88eb7cbe 100644 --- a/include/AudioResampler.h +++ b/include/AudioResampler.h @@ -31,16 +31,6 @@ #ifndef OPENSHOT_RESAMPLER_H #define OPENSHOT_RESAMPLER_H -/// Do not include the juce unittest headers, because it collides with unittest++ -#ifndef __JUCE_UNITTEST_JUCEHEADER__ - #define __JUCE_UNITTEST_JUCEHEADER__ -#endif - -#ifndef _NDEBUG - // Define NO debug for JUCE on mac os - #define _NDEBUG -#endif - #include "AudioBufferSource.h" #include "Exceptions.h" #include "JuceHeader.h" diff --git a/include/CacheBase.h b/include/CacheBase.h index aad65a846..da72e5dbd 100644 --- a/include/CacheBase.h +++ b/include/CacheBase.h @@ -110,9 +110,9 @@ namespace openshot { /// Get and Set JSON methods virtual std::string Json() = 0; ///< Generate JSON string of this object - virtual void SetJson(std::string value) = 0; ///< Load JSON string into this object - virtual Json::Value JsonValue() = 0; ///< Generate Json::JsonValue for this object - virtual void SetJsonValue(Json::Value root) = 0; ///< Load Json::JsonValue into this object + virtual void SetJson(const std::string value) = 0; ///< Load JSON string into this object + virtual Json::Value JsonValue() = 0; ///< Generate Json::Value for this object + virtual void SetJsonValue(const Json::Value root) = 0; ///< Load Json::Value into this object virtual ~CacheBase() = default; }; diff --git a/include/CacheDisk.h b/include/CacheDisk.h index e69cc3578..09ebd4abc 100644 --- a/include/CacheDisk.h +++ b/include/CacheDisk.h @@ -129,9 +129,9 @@ namespace openshot { /// Get and Set JSON methods std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue(); ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object }; } diff --git a/include/CacheMemory.h b/include/CacheMemory.h index fb3c75f66..ba771cedf 100644 --- a/include/CacheMemory.h +++ b/include/CacheMemory.h @@ -111,9 +111,9 @@ namespace openshot { /// Get and Set JSON methods std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue(); ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object }; } diff --git a/include/ChunkReader.h b/include/ChunkReader.h index 9d99fcd47..4d72c31da 100644 --- a/include/ChunkReader.h +++ b/include/ChunkReader.h @@ -157,10 +157,10 @@ namespace openshot std::string Name() { return "ChunkReader"; }; /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Open the reader. This is required before you can access frames or data from the reader. void Open(); diff --git a/include/Clip.h b/include/Clip.h index 4fdccea98..68d4622a1 100644 --- a/include/Clip.h +++ b/include/Clip.h @@ -31,11 +31,6 @@ #ifndef OPENSHOT_CLIP_H #define OPENSHOT_CLIP_H -/// Do not include the juce unittest headers, because it collides with unittest++ -#ifndef __JUCE_UNITTEST_JUCEHEADER__ - #define __JUCE_UNITTEST_JUCEHEADER__ -#endif - #include #include #include @@ -192,18 +187,18 @@ namespace openshot { openshot::ReaderBase* Reader(); /// Override End() method - float End(); ///< Get end position (in seconds) of clip (trim end of video), which can be affected by the time curve. + float End() const; ///< Get end position (in seconds) of clip (trim end of video), which can be affected by the time curve. void End(float value) { end = value; } ///< Set end position (in seconds) of clip (trim end of video) /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Get all properties for a specific frame (perfect for a UI to display the current state /// of all properties at any time) - std::string PropertiesJSON(int64_t requested_frame); + std::string PropertiesJSON(int64_t requested_frame) const override; /// @brief Remove an effect from the clip /// @param effect Remove an effect from the clip. diff --git a/include/ClipBase.h b/include/ClipBase.h index 2890f5d8f..1f7f55c47 100644 --- a/include/ClipBase.h +++ b/include/ClipBase.h @@ -31,11 +31,6 @@ #ifndef OPENSHOT_CLIPBASE_H #define OPENSHOT_CLIPBASE_H -/// Do not include the juce unittest headers, because it collides with unittest++ -#ifndef __JUCE_UNITTEST_JUCEHEADER__ - #define __JUCE_UNITTEST_JUCEHEADER__ -#endif - #include #include #include "Exceptions.h" @@ -61,10 +56,10 @@ namespace openshot { std::string previous_properties; ///< This string contains the previous JSON properties /// Generate JSON for a property - Json::Value add_property_json(std::string name, float value, std::string type, std::string memo, Keyframe* keyframe, float min_value, float max_value, bool readonly, int64_t requested_frame); + Json::Value add_property_json(std::string name, float value, std::string type, std::string memo, const Keyframe* keyframe, float min_value, float max_value, bool readonly, int64_t requested_frame) const; /// Generate JSON choice for a property (dropdown properties) - Json::Value add_property_choice_json(std::string name, int value, int selected_value); + Json::Value add_property_choice_json(std::string name, int value, int selected_value) const; public: @@ -78,12 +73,12 @@ namespace openshot { bool operator>= ( ClipBase& a) { return (Position() >= a.Position()); } /// Get basic properties - std::string Id() { return id; } ///< Get the Id of this clip object - float Position() { return position; } ///< Get position on timeline (in seconds) - int Layer() { return layer; } ///< Get layer of clip on timeline (lower number is covered by higher numbers) - float Start() { return start; } ///< Get start position (in seconds) of clip (trim start of video) - float End() { return end; } ///< Get end position (in seconds) of clip (trim end of video) - float Duration() { return end - start; } ///< Get the length of this clip (in seconds) + std::string Id() const { return id; } ///< Get the Id of this clip object + float Position() const { return position; } ///< Get position on timeline (in seconds) + int Layer() const { return layer; } ///< Get layer of clip on timeline (lower number is covered by higher numbers) + float Start() const { return start; } ///< Get start position (in seconds) of clip (trim start of video) + float End() const { return end; } ///< Get end position (in seconds) of clip (trim end of video) + float Duration() const { return end - start; } ///< Get the length of this clip (in seconds) /// Set basic properties void Id(std::string value) { id = value; } ///> Set the Id of this clip object @@ -93,14 +88,14 @@ namespace openshot { void End(float value) { end = value; } ///< Set end position (in seconds) of clip (trim end of video) /// Get and Set JSON methods - virtual std::string Json() = 0; ///< Generate JSON string of this object - virtual void SetJson(std::string value) = 0; ///< Load JSON string into this object - virtual Json::Value JsonValue() = 0; ///< Generate Json::JsonValue for this object - virtual void SetJsonValue(Json::Value root) = 0; ///< Load Json::JsonValue into this object + virtual std::string Json() const = 0; ///< Generate JSON string of this object + virtual void SetJson(const std::string value) = 0; ///< Load JSON string into this object + virtual Json::Value JsonValue() const = 0; ///< Generate Json::Value for this object + virtual void SetJsonValue(const Json::Value root) = 0; ///< Load Json::Value into this object /// Get all properties for a specific frame (perfect for a UI to display the current state /// of all properties at any time) - virtual std::string PropertiesJSON(int64_t requested_frame) = 0; + virtual std::string PropertiesJSON(int64_t requested_frame) const = 0; virtual ~ClipBase() = default; }; diff --git a/include/Color.h b/include/Color.h index 47db29a7f..2b4a6d771 100644 --- a/include/Color.h +++ b/include/Color.h @@ -69,10 +69,10 @@ namespace openshot { static long GetDistance(long R1, long G1, long B1, long R2, long G2, long B2); /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJson(std::string value); ///< Load JSON string into this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const; ///< Generate JSON string of this object + Json::Value JsonValue() const; ///< Generate Json::Value for this object + void SetJson(const std::string value); ///< Load JSON string into this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object }; diff --git a/include/Coordinate.h b/include/Coordinate.h index 5bfd893a8..a4b9599d2 100644 --- a/include/Coordinate.h +++ b/include/Coordinate.h @@ -66,10 +66,10 @@ namespace openshot { Coordinate(double x, double y); /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJson(std::string value); ///< Load JSON string into this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const; ///< Generate JSON string of this object + Json::Value JsonValue() const; ///< Generate Json::Value for this object + void SetJson(const std::string value); ///< Load JSON string into this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object }; } diff --git a/include/DecklinkReader.h b/include/DecklinkReader.h index de077bdf8..d846cca90 100644 --- a/include/DecklinkReader.h +++ b/include/DecklinkReader.h @@ -118,10 +118,10 @@ namespace openshot std::string Name() { return "DecklinkReader"; }; /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Open device and video stream - which is called by the constructor automatically void Open(); diff --git a/include/DummyReader.h b/include/DummyReader.h index 0b40f0790..fd17bed1c 100644 --- a/include/DummyReader.h +++ b/include/DummyReader.h @@ -87,10 +87,10 @@ namespace openshot std::string Name() { return "DummyReader"; }; /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Open File - which is called by the constructor automatically void Open(); diff --git a/include/EffectBase.h b/include/EffectBase.h index 29e98b589..1f967a021 100644 --- a/include/EffectBase.h +++ b/include/EffectBase.h @@ -50,7 +50,6 @@ namespace openshot struct EffectInfoStruct { std::string class_name; ///< The class name of the effect - std::string short_name; ///< A short name of the effect, commonly used for icon names, etc... std::string name; ///< The name of the effect std::string description; ///< The description of this effect and what it does bool has_video; ///< Determines if this effect manipulates the image of a frame @@ -95,14 +94,14 @@ namespace openshot void InitEffectInfo(); /// Get and Set JSON methods - virtual std::string Json() = 0; ///< Generate JSON string of this object - virtual void SetJson(std::string value) = 0; ///< Load JSON string into this object - virtual Json::Value JsonValue() = 0; ///< Generate Json::JsonValue for this object - virtual void SetJsonValue(Json::Value root) = 0; ///< Load Json::JsonValue into this object - Json::Value JsonInfo(); ///< Generate JSON object of meta data / info + virtual std::string Json() const = 0; ///< Generate JSON string of this object + virtual void SetJson(const std::string value) = 0; ///< Load JSON string into this object + virtual Json::Value JsonValue() const = 0; ///< Generate Json::Value for this object + virtual void SetJsonValue(const Json::Value root) = 0; ///< Load Json::Value into this object + Json::Value JsonInfo() const; ///< Generate JSON object of meta data / info /// Get the order that this effect should be executed. - int Order() { return order; } + int Order() const { return order; } /// Set the order that this effect should be executed. void Order(int new_order) { order = new_order; } diff --git a/include/EffectInfo.h b/include/EffectInfo.h index 1015b0da7..0e64327bb 100644 --- a/include/EffectInfo.h +++ b/include/EffectInfo.h @@ -51,7 +51,7 @@ namespace openshot /// JSON methods static std::string Json(); ///< Generate JSON string of this object - static Json::Value JsonValue(); ///< Generate Json::JsonValue for this object + static Json::Value JsonValue(); ///< Generate Json::Value for this object }; diff --git a/include/FFmpegReader.h b/include/FFmpegReader.h index 9faa86a35..e9cee5793 100644 --- a/include/FFmpegReader.h +++ b/include/FFmpegReader.h @@ -76,11 +76,11 @@ namespace openshot { * * @code * // Create a reader for a video - * FFmpegReader r("MyAwesomeVideo.webm"); + * openshot::FFmpegReader r("MyAwesomeVideo.webm"); * r.Open(); // Open the reader * * // Get frame number 1 from the video - * std::shared_ptr f = r.GetFrame(1); + * std::shared_ptr f = r.GetFrame(1); * * // Now that we have an openshot::Frame object, lets have some fun! * f->Display(); // Display the frame on the screen @@ -98,7 +98,7 @@ namespace openshot { AVFormatContext *pFormatCtx; int i, videoStream, audioStream; AVCodecContext *pCodecCtx, *aCodecCtx; -#if (LIBAVFORMAT_VERSION_MAJOR >= 57) +#if HAVE_HW_ACCEL AVBufferRef *hw_device_ctx = NULL; //PM #endif AVStream *pStream, *aStream; @@ -147,12 +147,11 @@ namespace openshot { int64_t current_video_frame; // can't reliably use PTS of video to determine this int hw_de_supported = 0; // Is set by FFmpegReader -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL AVPixelFormat hw_de_av_pix_fmt = AV_PIX_FMT_NONE; AVHWDeviceType hw_de_av_device_type = AV_HWDEVICE_TYPE_NONE; -#endif - int IsHardwareDecodeSupported(int codecid); +#endif /// Check for the correct frames per second value by scanning the 1st few seconds of video packets. void CheckFPS(); @@ -265,10 +264,10 @@ namespace openshot { std::string Name() { return "FFmpegReader"; }; /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Open File - which is called by the constructor automatically void Open(); diff --git a/include/FFmpegUtilities.h b/include/FFmpegUtilities.h index c673305eb..62d64df17 100644 --- a/include/FFmpegUtilities.h +++ b/include/FFmpegUtilities.h @@ -40,6 +40,10 @@ #ifndef IS_FFMPEG_3_2 #define IS_FFMPEG_3_2 (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 64, 101)) #endif + + #ifndef HAVE_HW_ACCEL + #define HAVE_HW_ACCEL (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 107, 100)) + #endif // Include the FFmpeg headers extern "C" { diff --git a/include/FFmpegWriter.h b/include/FFmpegWriter.h index dc3a2cf7b..1dfb21a93 100644 --- a/include/FFmpegWriter.h +++ b/include/FFmpegWriter.h @@ -75,15 +75,19 @@ namespace openshot { * @code SIMPLE EXAMPLE * * // Create a reader for a video - * FFmpegReader r("MyAwesomeVideo.webm"); - * r.Open(); // Open thetarget_ reader + * openshot::FFmpegReader r("MyAwesomeVideo.webm"); + * r.Open(); // Open the target reader * * // Create a writer (which will create a WebM video) - * FFmpegWriter w("/home/jonathan/NewVideo.webm"); + * openshot::FFmpegWriter w("/home/jonathan/NewVideo.webm"); * * // Set options - * w.SetAudioOptions(true, "libvorbis", 44100, 2, ChannelLayout::LAYOUT_STEREO, 128000); // Sample Rate: 44100, Channels: 2, Bitrate: 128000 - * w.SetVideoOptions(true, "libvpx", openshot::Fraction(24,1), 720, 480, openshot::Fraction(1,1), false, false, 300000); // FPS: 24, Size: 720x480, Pixel Ratio: 1/1, Bitrate: 300000 + * + * // Sample Rate: 44100, Channels: 2, Bitrate: 128000 + * w.SetAudioOptions(true, "libvorbis", 44100, 2, openshot::ChannelLayout::LAYOUT_STEREO, 128000); + * + * // FPS: 24, Size: 720x480, Pixel Ratio: 1/1, Bitrate: 300000 + * w.SetVideoOptions(true, "libvpx", openshot::Fraction(24,1), 720, 480, openshot::Fraction(1,1), false, false, 300000); * * // Open the writer * w.Open(); @@ -102,15 +106,19 @@ namespace openshot { * @code ADVANCED WRITER EXAMPLE * * // Create a reader for a video - * FFmpegReader r("MyAwesomeVideo.webm"); + * openshot::FFmpegReader r("MyAwesomeVideo.webm"); * r.Open(); // Open the reader * * // Create a writer (which will create a WebM video) - * FFmpegWriter w("/home/jonathan/NewVideo.webm"); + * openshot::FFmpegWriter w("/home/jonathan/NewVideo.webm"); * * // Set options - * w.SetAudioOptions(true, "libvorbis", 44100, 2, ChannelLayout::LAYOUT_STEREO, 128000); // Sample Rate: 44100, Channels: 2, Bitrate: 128000 - * w.SetVideoOptions(true, "libvpx", openshot::Fraction(24,1), 720, 480, openshot::Fraction(1,1), false, false, 300000); // FPS: 24, Size: 720x480, Pixel Ratio: 1/1, Bitrate: 300000 + * + * // Sample Rate: 44100, Channels: 2, Bitrate: 128000 + * w.SetAudioOptions(true, "libvorbis", 44100, 2, openshot::ChannelLayout::LAYOUT_STEREO, 128000); + * + * // FPS: 24, Size: 720x480, Pixel Ratio: 1/1, Bitrate: 300000 + * w.SetVideoOptions(true, "libvpx", openshot::Fraction(24,1), 720, 480, openshot::Fraction(1,1), false, false, 300000); * * // Prepare Streams (Optional method that must be called before any SetOption calls) * w.PrepareStreams(); @@ -285,8 +293,21 @@ namespace openshot { /// @param channels The number of audio channels needed in this file /// @param channel_layout The 'layout' of audio channels (i.e. mono, stereo, surround, etc...) /// @param bit_rate The audio bit rate used during encoding + /// + /// \note This is an overloaded function. void SetAudioOptions(bool has_audio, std::string codec, int sample_rate, int channels, openshot::ChannelLayout channel_layout, int bit_rate); + /// @brief Set audio export options. + /// + /// Enables the stream and configures a default 2-channel stereo layout. + /// + /// @param codec The codec used to encode the audio for this file + /// @param sample_rate The number of audio samples needed in this file + /// @param bit_rate The audio bit rate used during encoding + /// + /// \note This is an overloaded function. + void SetAudioOptions(std::string codec, int sample_rate, int bit_rate); + /// @brief Set the cache size /// @param new_size The number of frames to queue before writing to the file void SetCacheSize(int new_size) { cache_size = new_size; }; @@ -301,10 +322,27 @@ namespace openshot { /// @param interlaced Does this video need to be interlaced? /// @param top_field_first Which frame should be used as the top field? /// @param bit_rate The video bit rate used during encoding + /// + /// \note This is an overloaded function. void SetVideoOptions(bool has_video, std::string codec, openshot::Fraction fps, int width, int height, openshot::Fraction pixel_ratio, bool interlaced, bool top_field_first, int bit_rate); + /// @brief Set video export options. + /// + /// Enables the stream and configures non-interlaced video with a 1:1 pixel aspect ratio. + /// + /// @param codec The codec used to encode the images in this video + /// @param width The width in pixels of this video + /// @param height The height in pixels of this video + /// @param fps The number of frames per second + /// @param bit_rate The video bit rate used during encoding + /// + /// \note This is an overloaded function. + /// \warning Observe the argument order, which is consistent with the openshot::Timeline constructor, but differs from the other signature. + void SetVideoOptions(std::string codec, int width, int height, openshot::Fraction fps, int bit_rate); + /// @brief Set custom options (some codecs accept additional params). This must be called after the /// PrepareStreams() method, otherwise the streams have not been initialized yet. + /// /// @param stream The stream (openshot::StreamType) this option should apply to /// @param name The name of the option you want to set (i.e. qmin, qmax, etc...) /// @param value The new value of this option @@ -316,12 +354,16 @@ namespace openshot { /// @brief Add a frame to the stack waiting to be encoded. /// @param frame The openshot::Frame object to write to this image + /// + /// \note This is an overloaded function. void WriteFrame(std::shared_ptr frame); /// @brief Write a block of frames from a reader /// @param reader A openshot::ReaderBase object which will provide frames to be written /// @param start The starting frame number of the reader /// @param length The number of frames to write + /// + /// \note This is an overloaded function. void WriteFrame(openshot::ReaderBase *reader, int64_t start, int64_t length); /// @brief Write the file trailer (after all frames are written). This is called automatically diff --git a/include/Frame.h b/include/Frame.h index b9b989a50..f4ff54d44 100644 --- a/include/Frame.h +++ b/include/Frame.h @@ -31,15 +31,6 @@ #ifndef OPENSHOT_FRAME_H #define OPENSHOT_FRAME_H -/// Do not include the juce unittest headers, because it collides with unittest++ -#ifndef __JUCE_UNITTEST_JUCEHEADER__ - #define __JUCE_UNITTEST_JUCEHEADER__ -#endif -#ifndef _NDEBUG - // Define NO debug for JUCE on mac os - #define _NDEBUG -#endif - #include #include #include diff --git a/include/FrameMapper.h b/include/FrameMapper.h index f2b030a36..8be4ec1bd 100644 --- a/include/FrameMapper.h +++ b/include/FrameMapper.h @@ -199,10 +199,10 @@ namespace openshot std::string Name() { return "FrameMapper"; }; /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Open the internal reader void Open(); diff --git a/include/ImageReader.h b/include/ImageReader.h index 8da450eb7..bb19dd3dc 100644 --- a/include/ImageReader.h +++ b/include/ImageReader.h @@ -106,10 +106,10 @@ namespace openshot std::string Name() { return "ImageReader"; }; /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Open File - which is called by the constructor automatically void Open(); diff --git a/include/Json.h b/include/Json.h index 3a10ab744..0bdf6e2c3 100644 --- a/include/Json.h +++ b/include/Json.h @@ -31,6 +31,12 @@ #ifndef OPENSHOT_JSON_H #define OPENSHOT_JSON_H +#include #include "json/json.h" +#include "Exceptions.h" + +namespace openshot { + const Json::Value stringToJson(const std::string value); +} #endif diff --git a/include/KeyFrame.h b/include/KeyFrame.h index 6ed395530..ee58da8f0 100644 --- a/include/KeyFrame.h +++ b/include/KeyFrame.h @@ -133,9 +133,9 @@ namespace openshot { /// Get and Set JSON methods std::string Json() const; ///< Generate JSON string of this object - Json::Value JsonValue() const; ///< Generate Json::JsonValue for this object - void SetJson(std::string value); ///< Load JSON string into this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + Json::Value JsonValue() const; ///< Generate Json::Value for this object + void SetJson(const std::string value); ///< Load JSON string into this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Remove a point by matching a coordinate void RemovePoint(Point p); diff --git a/include/Point.h b/include/Point.h index 3fdcd9176..4941c5589 100644 --- a/include/Point.h +++ b/include/Point.h @@ -119,10 +119,10 @@ namespace openshot void Initialize_RightHandle(float x, float y); /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJson(std::string value); ///< Load JSON string into this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const; ///< Generate JSON string of this object + Json::Value JsonValue() const; ///< Generate Json::Value for this object + void SetJson(const std::string value); ///< Load JSON string into this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object }; diff --git a/include/Profiles.h b/include/Profiles.h index bc5fe3e01..3b5ebd0d1 100644 --- a/include/Profiles.h +++ b/include/Profiles.h @@ -90,10 +90,10 @@ namespace openshot Profile(std::string path); /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJson(std::string value); ///< Load JSON string into this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const; ///< Generate JSON string of this object + Json::Value JsonValue() const; ///< Generate Json::Value for this object + void SetJson(const std::string value); ///< Load JSON string into this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object }; } diff --git a/include/QtHtmlReader.h b/include/QtHtmlReader.h index c102038c4..d7052d494 100644 --- a/include/QtHtmlReader.h +++ b/include/QtHtmlReader.h @@ -131,10 +131,10 @@ namespace openshot std::string Name() { return "QtHtmlReader"; }; /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Open Reader - which is called by the constructor automatically void Open(); diff --git a/include/QtImageReader.h b/include/QtImageReader.h index 3153e7a39..3e456e394 100644 --- a/include/QtImageReader.h +++ b/include/QtImageReader.h @@ -104,10 +104,10 @@ namespace openshot std::string Name() { return "QtImageReader"; }; /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Open File - which is called by the constructor automatically void Open(); diff --git a/include/QtTextReader.h b/include/QtTextReader.h index e6a8c84b8..d226e920c 100644 --- a/include/QtTextReader.h +++ b/include/QtTextReader.h @@ -142,10 +142,10 @@ namespace openshot std::string Name() { return "QtTextReader"; }; /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Open Reader - which is called by the constructor automatically void Open(); diff --git a/include/ReaderBase.h b/include/ReaderBase.h index 267693388..dfb9873d4 100644 --- a/include/ReaderBase.h +++ b/include/ReaderBase.h @@ -140,10 +140,10 @@ namespace openshot virtual std::string Name() = 0; /// Get and Set JSON methods - virtual std::string Json() = 0; ///< Generate JSON string of this object - virtual void SetJson(std::string value) = 0; ///< Load JSON string into this object - virtual Json::Value JsonValue() = 0; ///< Generate Json::JsonValue for this object - virtual void SetJsonValue(Json::Value root) = 0; ///< Load Json::JsonValue into this object + virtual std::string Json() const = 0; ///< Generate JSON string of this object + virtual void SetJson(const std::string value) = 0; ///< Load JSON string into this object + virtual Json::Value JsonValue() const = 0; ///< Generate Json::Value for this object + virtual void SetJsonValue(const Json::Value root) = 0; ///< Load Json::Value into this object /// Open the reader (and start consuming resources, such as images or video files) virtual void Open() = 0; diff --git a/include/Settings.h b/include/Settings.h index 56a84fd7a..e82bf56bb 100644 --- a/include/Settings.h +++ b/include/Settings.h @@ -124,6 +124,10 @@ namespace openshot { /// The audio device name to use during playback std::string PLAYBACK_AUDIO_DEVICE_NAME = ""; + /// The current install path of OpenShot (needs to be set when using Timeline(path), since certain + /// paths depend on the location of OpenShot transitions and files) + std::string PATH_OPENSHOT_INSTALL = ""; + /// Create or get an instance of this logger singleton (invoke the class with this method) static Settings * Instance(); }; diff --git a/include/TextReader.h b/include/TextReader.h index 2d54fdc25..b6dbf5cd0 100644 --- a/include/TextReader.h +++ b/include/TextReader.h @@ -119,7 +119,7 @@ namespace openshot TextReader(int width, int height, int x_offset, int y_offset, GravityType gravity, std::string text, std::string font, double size, std::string text_color, std::string background_color); /// Draw a box under rendered text using the specified color. - /// @param text_background_color The background color behind the text + /// @param color The background color behind the text void SetTextBackgroundColor(std::string color); /// Close Reader @@ -142,10 +142,10 @@ namespace openshot std::string Name() { return "TextReader"; }; /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Open Reader - which is called by the constructor automatically void Open(); diff --git a/include/Timeline.h b/include/Timeline.h index 2ff03d919..56a895866 100644 --- a/include/Timeline.h +++ b/include/Timeline.h @@ -36,6 +36,7 @@ #include #include #include +#include #include "CacheBase.h" #include "CacheDisk.h" #include "CacheMemory.h" @@ -156,6 +157,7 @@ namespace openshot { CacheBase *final_cache; /// allocated_frame_mappers; ///< all the frame mappers we allocated and must free bool managed_cache; ///< Does this timeline instance manage the cache object + std::string path; ///< Optional path of loaded UTF-8 OpenShot JSON project file /// Process a new layer of video or audio void add_layer(std::shared_ptr new_frame, Clip* source_clip, int64_t clip_frame_number, int64_t timeline_frame_number, bool is_top_clip, float max_volume); @@ -209,6 +211,11 @@ namespace openshot { /// @param channel_layout The channel layout (i.e. mono, stereo, 3 point surround, etc...) Timeline(int width, int height, Fraction fps, int sample_rate, int channels, ChannelLayout channel_layout); + /// @brief Constructor for the timeline (which loads a JSON structure from a file path, and initializes a timeline) + /// @param projectPath The path of the UTF-8 *.osp project file (JSON contents). Contents will be loaded automatically. + /// @param convert_absolute_paths Should all paths be converted to absolute paths (based on the folder of the path provided) + Timeline(std::string projectPath, bool convert_absolute_paths); + virtual ~Timeline(); /// @brief Add an openshot::Clip to the timeline @@ -268,10 +275,10 @@ namespace openshot { std::string Name() { return "Timeline"; }; /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Set Max Image Size (used for performance optimization). Convenience function for setting /// Settings::Instance()->MAX_WIDTH and Settings::Instance()->MAX_HEIGHT. diff --git a/include/WriterBase.h b/include/WriterBase.h index 503be6bfb..1b55c72b6 100644 --- a/include/WriterBase.h +++ b/include/WriterBase.h @@ -107,10 +107,10 @@ namespace openshot virtual void WriteFrame(openshot::ReaderBase* reader, int64_t start, int64_t length) = 0; /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJson(std::string value); ///< Load JSON string into this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const; ///< Generate JSON string of this object + Json::Value JsonValue() const; ///< Generate Json::Value for this object + void SetJson(const std::string value); ///< Load JSON string into this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Display file information in the standard output stream (stdout) void DisplayInfo(); diff --git a/include/effects/Bars.h b/include/effects/Bars.h index 43a77a2ff..fa9df6f80 100644 --- a/include/effects/Bars.h +++ b/include/effects/Bars.h @@ -89,14 +89,14 @@ namespace openshot std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number); /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Get all properties for a specific frame (perfect for a UI to display the current state /// of all properties at any time) - std::string PropertiesJSON(int64_t requested_frame); + std::string PropertiesJSON(int64_t requested_frame) const override; }; } diff --git a/include/effects/Blur.h b/include/effects/Blur.h index 167e93032..c254b8cde 100644 --- a/include/effects/Blur.h +++ b/include/effects/Blur.h @@ -67,7 +67,6 @@ namespace openshot void init_effect_details(); /// Internal blur methods (inspired and credited to http://blog.ivank.net/fastest-gaussian-blur.html) - int* initBoxes(float sigma, int n); void boxBlurH(unsigned char *scl, unsigned char *tcl, int w, int h, int r); void boxBlurT(unsigned char *scl, unsigned char *tcl, int w, int h, int r); @@ -102,14 +101,14 @@ namespace openshot std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number); /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Get all properties for a specific frame (perfect for a UI to display the current state /// of all properties at any time) - std::string PropertiesJSON(int64_t requested_frame); + std::string PropertiesJSON(int64_t requested_frame) const override; }; } diff --git a/include/effects/Brightness.h b/include/effects/Brightness.h index 2a6ab9129..69f2cf343 100644 --- a/include/effects/Brightness.h +++ b/include/effects/Brightness.h @@ -89,14 +89,14 @@ namespace openshot std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number); /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Get all properties for a specific frame (perfect for a UI to display the current state /// of all properties at any time) - std::string PropertiesJSON(int64_t requested_frame); + std::string PropertiesJSON(int64_t requested_frame) const override; }; } diff --git a/include/effects/ChromaKey.h b/include/effects/ChromaKey.h index d0fa31d84..dbc81e9cf 100644 --- a/include/effects/ChromaKey.h +++ b/include/effects/ChromaKey.h @@ -86,13 +86,13 @@ namespace openshot std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number); /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object // Get all properties for a specific frame - std::string PropertiesJSON(int64_t requested_frame); + std::string PropertiesJSON(int64_t requested_frame) const override; }; } diff --git a/include/effects/ColorShift.h b/include/effects/ColorShift.h index e9c11d68e..ada2acd68 100644 --- a/include/effects/ColorShift.h +++ b/include/effects/ColorShift.h @@ -93,14 +93,14 @@ namespace openshot std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number); /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Get all properties for a specific frame (perfect for a UI to display the current state /// of all properties at any time) - std::string PropertiesJSON(int64_t requested_frame); + std::string PropertiesJSON(int64_t requested_frame) const override; }; } diff --git a/include/effects/Crop.h b/include/effects/Crop.h index 34cea89f5..2d4a2b2ee 100644 --- a/include/effects/Crop.h +++ b/include/effects/Crop.h @@ -88,14 +88,14 @@ namespace openshot std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number); /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Get all properties for a specific frame (perfect for a UI to display the current state /// of all properties at any time) - std::string PropertiesJSON(int64_t requested_frame); + std::string PropertiesJSON(int64_t requested_frame) const override; }; } diff --git a/include/effects/Deinterlace.h b/include/effects/Deinterlace.h index b411b0230..eff7b2fe7 100644 --- a/include/effects/Deinterlace.h +++ b/include/effects/Deinterlace.h @@ -82,13 +82,13 @@ namespace openshot std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number); /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object // Get all properties for a specific frame - std::string PropertiesJSON(int64_t requested_frame); + std::string PropertiesJSON(int64_t requested_frame) const override; }; } diff --git a/include/effects/Hue.h b/include/effects/Hue.h index 4670b6bff..9d86d5c2b 100644 --- a/include/effects/Hue.h +++ b/include/effects/Hue.h @@ -79,14 +79,14 @@ namespace openshot std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number); /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Get all properties for a specific frame (perfect for a UI to display the current state /// of all properties at any time) - std::string PropertiesJSON(int64_t requested_frame); + std::string PropertiesJSON(int64_t requested_frame) const override; }; } diff --git a/include/effects/Mask.h b/include/effects/Mask.h index dba3b2d89..390ffa361 100644 --- a/include/effects/Mask.h +++ b/include/effects/Mask.h @@ -101,14 +101,14 @@ namespace openshot std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number); /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Get all properties for a specific frame (perfect for a UI to display the current state /// of all properties at any time) - std::string PropertiesJSON(int64_t requested_frame); + std::string PropertiesJSON(int64_t requested_frame) const override; /// Get the reader object of the mask grayscale image ReaderBase* Reader() { return reader; }; diff --git a/include/effects/Negate.h b/include/effects/Negate.h index 3cbc7988d..6206a6607 100644 --- a/include/effects/Negate.h +++ b/include/effects/Negate.h @@ -70,13 +70,13 @@ namespace openshot std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number); /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object // Get all properties for a specific frame - std::string PropertiesJSON(int64_t requested_frame); + std::string PropertiesJSON(int64_t requested_frame) const override; }; } diff --git a/include/effects/Pixelate.h b/include/effects/Pixelate.h index 3146d456c..9348ce863 100644 --- a/include/effects/Pixelate.h +++ b/include/effects/Pixelate.h @@ -88,14 +88,14 @@ namespace openshot std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number); /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Get all properties for a specific frame (perfect for a UI to display the current state /// of all properties at any time) - std::string PropertiesJSON(int64_t requested_frame); + std::string PropertiesJSON(int64_t requested_frame) const override; }; } diff --git a/include/effects/Saturation.h b/include/effects/Saturation.h index de3cc7714..e8d0d9409 100644 --- a/include/effects/Saturation.h +++ b/include/effects/Saturation.h @@ -86,14 +86,14 @@ namespace openshot std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number); /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Get all properties for a specific frame (perfect for a UI to display the current state /// of all properties at any time) - std::string PropertiesJSON(int64_t requested_frame); + std::string PropertiesJSON(int64_t requested_frame) const override; }; } diff --git a/include/effects/Shift.h b/include/effects/Shift.h index 243e442eb..765da7555 100644 --- a/include/effects/Shift.h +++ b/include/effects/Shift.h @@ -82,14 +82,14 @@ namespace openshot std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number); /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Get all properties for a specific frame (perfect for a UI to display the current state /// of all properties at any time) - std::string PropertiesJSON(int64_t requested_frame); + std::string PropertiesJSON(int64_t requested_frame) const override; }; } diff --git a/include/effects/Wave.h b/include/effects/Wave.h index 62b08d1fa..ad516bde3 100644 --- a/include/effects/Wave.h +++ b/include/effects/Wave.h @@ -88,14 +88,14 @@ namespace openshot std::shared_ptr GetFrame(std::shared_ptr frame, int64_t frame_number); /// Get and Set JSON methods - std::string Json(); ///< Generate JSON string of this object - void SetJson(std::string value); ///< Load JSON string into this object - Json::Value JsonValue(); ///< Generate Json::JsonValue for this object - void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + std::string Json() const override; ///< Generate JSON string of this object + void SetJson(const std::string value); ///< Load JSON string into this object + Json::Value JsonValue() const override; ///< Generate Json::Value for this object + void SetJsonValue(const Json::Value root); ///< Load Json::Value into this object /// Get all properties for a specific frame (perfect for a UI to display the current state /// of all properties at any time) - std::string PropertiesJSON(int64_t requested_frame); + std::string PropertiesJSON(int64_t requested_frame) const override; }; } diff --git a/src/AudioBufferSource.cpp b/src/AudioBufferSource.cpp index 46b04916e..2f3d14ca3 100644 --- a/src/AudioBufferSource.cpp +++ b/src/AudioBufferSource.cpp @@ -43,7 +43,7 @@ AudioBufferSource::~AudioBufferSource() { // forget the AudioSampleBuffer. It still exists; we just don't know about it. buffer = NULL; -}; +} // Get the next block of audio samples void AudioBufferSource::getNextAudioBlock (const juce::AudioSourceChannelInfo& info) diff --git a/src/AudioReaderSource.cpp b/src/AudioReaderSource.cpp index 41c0b3f6b..c96d0bcc3 100644 --- a/src/AudioReaderSource.cpp +++ b/src/AudioReaderSource.cpp @@ -51,7 +51,7 @@ AudioReaderSource::~AudioReaderSource() // Clear and delete the buffer delete buffer; buffer = NULL; -}; +} // Get more samples from the reader void AudioReaderSource::GetMoreSamplesFromReader() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0de15ded5..a57780b0d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,31 +27,25 @@ # Collect and display summary of options/dependencies include(FeatureSummary) -################ OPTIONS ################## -# Optional build settings for libopenshot -option(USE_SYSTEM_JSONCPP "Use system installed JsonCpp, if found" ON) -option(DISABLE_BUNDLED_JSONCPP "Don't fall back to bundled JsonCpp" OFF) -option(ENABLE_IWYU "Enable 'Include What You Use' scanner (CMake 3.3+)" OFF) - -# Automatically process Qt classes with meta-object compiler -set(CMAKE_AUTOMOC True) +include(GNUInstallDirs) ################ WINDOWS ################## # Set some compiler options for Windows # required for libopenshot-audio headers -IF (WIN32) +if (WIN32) add_definitions( -DIGNORE_JUCE_HYPOT=1 ) - SET(CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -include cmath") -ENDIF(WIN32) -IF (APPLE) + set(CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -include cmath") +endif() + +if (APPLE) # If you still get errors compiling with GCC 4.8, mac headers need to be patched: http://hamelot.co.uk/programming/osx-gcc-dispatch_block_t-has-not-been-declared-invalid-typedef/ - SET_PROPERTY(GLOBAL PROPERTY JUCE_MAC "JUCE_MAC") - ADD_DEFINITIONS(-DNDEBUG) - SET(EXTENSION "mm") + set_property(GLOBAL PROPERTY JUCE_MAC "JUCE_MAC") + add_definitions(-DNDEBUG) + set(EXTENSION "mm") - SET(JUCE_PLATFORM_SPECIFIC_DIR build/macosx/platform_specific_code) - SET(JUCE_PLATFORM_SPECIFIC_LIBRARIES "-framework Carbon -framework Cocoa -framework CoreFoundation -framework CoreAudio -framework CoreMidi -framework IOKit -framework AGL -framework AudioToolbox -framework QuartzCore -lobjc -framework Accelerate") -ENDIF(APPLE) + set(JUCE_PLATFORM_SPECIFIC_DIR build/macosx/platform_specific_code) + set(JUCE_PLATFORM_SPECIFIC_LIBRARIES "-framework Carbon -framework Cocoa -framework CoreFoundation -framework CoreAudio -framework CoreMidi -framework IOKit -framework AGL -framework AudioToolbox -framework QuartzCore -lobjc -framework Accelerate") +endif() ################ IMAGE MAGICK ################## # Set the Quantum Depth that ImageMagick was built with (default to 16 bits) @@ -72,52 +66,40 @@ ELSE (OPENSHOT_IMAGEMAGICK_COMPATIBILITY) ENDIF (OPENSHOT_IMAGEMAGICK_COMPATIBILITY) # Find the ImageMagick++ library -FIND_PACKAGE(ImageMagick COMPONENTS Magick++ MagickWand MagickCore) -IF (ImageMagick_FOUND) +find_package(ImageMagick COMPONENTS Magick++ MagickWand MagickCore) +if (ImageMagick_FOUND) # Include ImageMagick++ headers (needed for compile) include_directories(${ImageMagick_INCLUDE_DIRS}) # define a global var (used in the C++) add_definitions( -DUSE_IMAGEMAGICK=1 ) - SET(CMAKE_SWIG_FLAGS "-DUSE_IMAGEMAGICK=1") + set(CMAKE_SWIG_FLAGS "-DUSE_IMAGEMAGICK=1") -ENDIF (ImageMagick_FOUND) +endif() ################# LIBOPENSHOT-AUDIO ################### # Find JUCE-based openshot Audio libraries -FIND_PACKAGE(OpenShotAudio 0.1.8 REQUIRED) +find_package(OpenShotAudio 0.2.0 REQUIRED) # Include Juce headers (needed for compile) include_directories(${LIBOPENSHOT_AUDIO_INCLUDE_DIRS}) ################# BLACKMAGIC DECKLINK ################### # Find BlackMagic DeckLinkAPI libraries -IF (ENABLE_BLACKMAGIC) - FIND_PACKAGE(BlackMagic) +if (ENABLE_BLACKMAGIC) - IF (BLACKMAGIC_FOUND) + find_package(BlackMagic) + + if (BLACKMAGIC_FOUND) # Include Blackmagic headers (needed for compile) include_directories(${BLACKMAGIC_INCLUDE_DIR}) # define a global var (used in the C++) add_definitions( -DUSE_BLACKMAGIC=1 ) - SET(CMAKE_SWIG_FLAGS "-DUSE_BLACKMAGIC=1") - - ENDIF (BLACKMAGIC_FOUND) -ENDIF (ENABLE_BLACKMAGIC) - -################### RESVG ##################### -# Find resvg library (used for rendering svg files) -FIND_PACKAGE(RESVG) - -# Include resvg headers (optional SVG library) -if (RESVG_FOUND) - include_directories(${RESVG_INCLUDE_DIRS}) + set(CMAKE_SWIG_FLAGS "-DUSE_BLACKMAGIC=1") + endif() - # define a global var (used in the C++) - add_definitions( -DUSE_RESVG=1 ) - SET(CMAKE_SWIG_FLAGS "-DUSE_RESVG=1") -endif(RESVG_FOUND) +endif() ############### PROFILING ################# #set(PROFILER "/usr/lib/libprofiler.so.0.3.2") @@ -169,6 +151,7 @@ set(OPENSHOT_SOURCES Fraction.cpp Frame.cpp FrameMapper.cpp + Json.cpp KeyFrame.cpp OpenShotVersion.cpp ZmqLogger.cpp @@ -209,30 +192,30 @@ set(QT_PLAYER_SOURCES Qt/VideoRenderer.cpp Qt/VideoRenderWidget.cpp) - -# Get list of headers -file(GLOB_RECURSE headers ${CMAKE_SOURCE_DIR}/include/*.h) +# Get list of MOC'able headers +file(GLOB_RECURSE OPENSHOT_QT_HEADERS ${CMAKE_SOURCE_DIR}/include/Qt/*.h) # Disable RPATH -SET(CMAKE_MACOSX_RPATH 0) +set(CMAKE_MACOSX_RPATH 0) ############### CREATE LIBRARY ################# # Create shared openshot library add_library(openshot SHARED) -target_sources(openshot - PRIVATE - ${OPENSHOT_SOURCES} ${EFFECTS_SOURCES} ${QT_PLAYER_SOURCES} - PUBLIC - ${headers}) +target_sources(openshot PRIVATE + ${OPENSHOT_SOURCES} + ${EFFECTS_SOURCES} + ${QT_PLAYER_SOURCES} + ${OPENSHOT_QT_HEADERS} + ) # Set SONAME and other library properties -set_target_properties(openshot - PROPERTIES - VERSION ${PROJECT_VERSION} - SOVERSION ${PROJECT_SO_VERSION} - INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib" - ) +set_target_properties(openshot PROPERTIES + AUTOMOC ON + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_SO_VERSION} + INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib" + ) # Add optional ImageMagic-dependent sources if(ImageMagick_FOUND) @@ -313,7 +296,7 @@ endforeach() ################### FFMPEG ##################### # Find FFmpeg libraries (used for video encoding / decoding) -FIND_PACKAGE(FFmpeg REQUIRED COMPONENTS avcodec avdevice avformat avutil swscale) +find_package(FFmpeg REQUIRED COMPONENTS avcodec avdevice avformat avutil swscale) foreach(ff_comp avcodec avdevice avformat avfilter avutil postproc swscale swresample avresample) if(TARGET FFmpeg::${ff_comp}) @@ -362,6 +345,26 @@ if (TARGET cppzmq) target_link_libraries(openshot PUBLIC cppzmq) endif() +################### RESVG ##################### +# Migrate some legacy variable names +if(DEFINED RESVGDIR AND NOT DEFINED RESVG_ROOT) + set(RESVG_ROOT ${RESVGDIR}) +endif() +if(DEFINED ENV{RESVGDIR} AND NOT DEFINED RESVG_ROOT) + set(RESVG_ROOT $ENV{RESVGDIR}) +endif() + +# Find resvg library (used for rendering svg files) +FIND_PACKAGE(RESVG) + +# Include resvg headers (optional SVG library) +if (TARGET RESVG::resvg) + #include_directories(${RESVG_INCLUDE_DIRS}) + target_link_libraries(openshot PUBLIC RESVG::resvg) + + target_compile_definitions(openshot PUBLIC "-DUSE_RESVG=1") + set(CMAKE_SWIG_FLAGS "-DUSE_RESVG=1") +endif() ############### LINK LIBRARY ################# # Link remaining dependency libraries @@ -373,10 +376,6 @@ if(ImageMagick_FOUND) target_link_libraries(openshot PUBLIC ${ImageMagick_LIBRARIES}) endif() -if(RESVG_FOUND) - target_link_libraries(openshot PUBLIC ${RESVG_LIBRARIES}) -endif() - if(BLACKMAGIC_FOUND) target_link_libraries(openshot PUBLIC ${BLACKMAGIC_LIBRARY_DIR}) endif() @@ -393,9 +392,9 @@ add_executable(openshot-example examples/Example.cpp) # Define path to test input files SET(TEST_MEDIA_PATH "${PROJECT_SOURCE_DIR}/src/examples/") -IF (WIN32) +if (WIN32) STRING(REPLACE "/" "\\\\" TEST_MEDIA_PATH TEST_MEDIA_PATH) -ENDIF(WIN32) +endif() target_compile_definitions(openshot-example PRIVATE -DTEST_MEDIA_PATH="${TEST_MEDIA_PATH}" ) @@ -413,48 +412,40 @@ add_executable(openshot-player Qt/demo/main.cpp) target_link_libraries(openshot-player openshot) ############### TEST BLACKMAGIC CAPTURE APP ################ -IF (BLACKMAGIC_FOUND) +if (BLACKMAGIC_FOUND) # Create test executable add_executable(openshot-blackmagic examples/ExampleBlackmagic.cpp) # Link test executable to the new library target_link_libraries(openshot-blackmagic openshot) -ENDIF (BLACKMAGIC_FOUND) +endif() ############### INCLUDE SWIG BINDINGS ################ add_subdirectory(bindings) -########### PRINT FEATURE SUMMARY ############## -feature_summary(WHAT ALL - INCLUDE_QUIET_PACKAGES - FATAL_ON_MISSING_REQUIRED_PACKAGES - DESCRIPTION "Displaying feature summary\n\nBuild configuration:") - ############### INSTALL HEADERS & LIBRARY ################ -set(LIB_INSTALL_DIR lib${LIB_SUFFIX}) # determine correct lib folder # Install primary library -INSTALL(TARGETS openshot - ARCHIVE DESTINATION ${LIB_INSTALL_DIR} - LIBRARY DESTINATION ${LIB_INSTALL_DIR} - RUNTIME DESTINATION ${LIB_INSTALL_DIR} - COMPONENT library ) +install(TARGETS openshot + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}) -INSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ - DESTINATION ${CMAKE_INSTALL_PREFIX}/include/libopenshot - FILES_MATCHING PATTERN "*.h") +install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libopenshot + FILES_MATCHING PATTERN "*.h") ############### CPACK PACKAGING ############## -IF(MINGW) - SET(CPACK_GENERATOR "NSIS") -ENDIF(MINGW) -IF(UNIX AND NOT APPLE) - SET(CPACK_GENERATOR "DEB") -ENDIF(UNIX AND NOT APPLE) -#IF(UNIX AND APPLE) -# SET(CPACK_GENERATOR "DragNDrop") -#ENDIF(UNIX AND APPLE) -SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "Jonathan Thomas") #required - -INCLUDE(CPack) +if(MINGW) + set(CPACK_GENERATOR "NSIS") +endif() +if(UNIX AND NOT APPLE) + set(CPACK_GENERATOR "DEB") +endif() +#if(UNIX AND APPLE) +# set(CPACK_GENERATOR "DragNDrop") +#endif() +set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Jonathan Thomas") #required + +include(CPack) diff --git a/src/CacheBase.cpp b/src/CacheBase.cpp index 8a7d541ae..bc57f3f45 100644 --- a/src/CacheBase.cpp +++ b/src/CacheBase.cpp @@ -37,13 +37,13 @@ using namespace openshot; CacheBase::CacheBase() : max_bytes(0) { // Init the critical section cacheCriticalSection = new CriticalSection(); -}; +} // Constructor that sets the max frames to cache CacheBase::CacheBase(int64_t max_bytes) : max_bytes(max_bytes) { // Init the critical section cacheCriticalSection = new CriticalSection(); -}; +} // Set maximum bytes to a different amount based on a ReaderInfo struct void CacheBase::SetMaxBytesFromInfo(int64_t number_of_frames, int width, int height, int sample_rate, int channels) @@ -53,7 +53,7 @@ void CacheBase::SetMaxBytesFromInfo(int64_t number_of_frames, int width, int hei SetMaxBytes(bytes); } -// Generate Json::JsonValue for this object +// Generate Json::Value for this object Json::Value CacheBase::JsonValue() { // Create root json object @@ -66,8 +66,8 @@ Json::Value CacheBase::JsonValue() { return root; } -// Load Json::JsonValue into this object -void CacheBase::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void CacheBase::SetJsonValue(const Json::Value root) { // Set data from Json (if key is found) if (!root["max_bytes"].isNull()) diff --git a/src/CacheDisk.cpp b/src/CacheDisk.cpp index 5dc677e56..9f67ce994 100644 --- a/src/CacheDisk.cpp +++ b/src/CacheDisk.cpp @@ -47,7 +47,7 @@ CacheDisk::CacheDisk(std::string cache_path, std::string format, float quality, // Init path directory InitPath(cache_path); -}; +} // Constructor that sets the max bytes to cache CacheDisk::CacheDisk(std::string cache_path, std::string format, float quality, float scale, int64_t max_bytes) : CacheBase(max_bytes) { @@ -62,7 +62,7 @@ CacheDisk::CacheDisk(std::string cache_path, std::string format, float quality, // Init path directory InitPath(cache_path); -}; +} // Initialize cache directory void CacheDisk::InitPath(std::string cache_path) { @@ -103,25 +103,19 @@ void CacheDisk::CalculateRanges() { // Increment range version range_version++; - std::vector::iterator itr_ordered; int64_t starting_frame = *ordered_frame_numbers.begin(); - int64_t ending_frame = *ordered_frame_numbers.begin(); + int64_t ending_frame = starting_frame; // Loop through all known frames (in sequential order) - for (itr_ordered = ordered_frame_numbers.begin(); itr_ordered != ordered_frame_numbers.end(); ++itr_ordered) { - int64_t frame_number = *itr_ordered; + for (const auto frame_number : ordered_frame_numbers) { if (frame_number - ending_frame > 1) { // End of range detected Json::Value range; // Add JSON object with start/end attributes // Use strings, since int64_ts are supported in JSON - std::stringstream start_str; - start_str << starting_frame; - std::stringstream end_str; - end_str << ending_frame; - range["start"] = start_str.str(); - range["end"] = end_str.str(); + range["start"] = std::to_string(starting_frame); + range["end"] = std::to_string(ending_frame); ranges.append(range); // Set new starting range @@ -137,12 +131,8 @@ void CacheDisk::CalculateRanges() { // Add JSON object with start/end attributes // Use strings, since int64_ts are supported in JSON - std::stringstream start_str; - start_str << starting_frame; - std::stringstream end_str; - end_str << ending_frame; - range["start"] = start_str.str(); - range["end"] = end_str.str(); + range["start"] = std::to_string(starting_frame); + range["end"] = std::to_string(ending_frame); ranges.append(range); // Cache range JSON as string @@ -471,7 +461,7 @@ std::string CacheDisk::Json() { return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object +// Generate Json::Value for this object Json::Value CacheDisk::JsonValue() { // Process range data (if anything has changed) @@ -488,41 +478,23 @@ Json::Value CacheDisk::JsonValue() { root["version"] = range_version_str.str(); // Parse and append range data (if any) - Json::Value ranges; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( json_ranges.c_str(), - json_ranges.c_str() + json_ranges.size(), &ranges, &errors ); - delete reader; - - if (success) + // Parse and append range data (if any) + try { + const Json::Value ranges = openshot::stringToJson(json_ranges); root["ranges"] = ranges; + } catch (...) { } // return JsonValue return root; } // Load JSON string into this object -void CacheDisk::SetJson(std::string value) { +void CacheDisk::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -533,8 +505,8 @@ void CacheDisk::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void CacheDisk::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void CacheDisk::SetJsonValue(const Json::Value root) { // Close timeline before we do anything (this also removes all open and closing clips) Clear(); diff --git a/src/CacheMemory.cpp b/src/CacheMemory.cpp index ff8963fdc..70feef03d 100644 --- a/src/CacheMemory.cpp +++ b/src/CacheMemory.cpp @@ -39,7 +39,7 @@ CacheMemory::CacheMemory() : CacheBase(0) { cache_type = "CacheMemory"; range_version = 0; needs_range_processing = false; -}; +} // Constructor that sets the max bytes to cache CacheMemory::CacheMemory(int64_t max_bytes) : CacheBase(max_bytes) { @@ -47,7 +47,7 @@ CacheMemory::CacheMemory(int64_t max_bytes) : CacheBase(max_bytes) { cache_type = "CacheMemory"; range_version = 0; needs_range_processing = false; -}; +} // Default destructor CacheMemory::~CacheMemory() @@ -92,12 +92,8 @@ void CacheMemory::CalculateRanges() { // Add JSON object with start/end attributes // Use strings, since int64_ts are supported in JSON - std::stringstream start_str; - start_str << starting_frame; - std::stringstream end_str; - end_str << ending_frame; - range["start"] = start_str.str(); - range["end"] = end_str.str(); + range["start"] = std::to_string(starting_frame); + range["end"] = std::to_string(ending_frame); ranges.append(range); // Set new starting range @@ -113,12 +109,8 @@ void CacheMemory::CalculateRanges() { // Add JSON object with start/end attributes // Use strings, since int64_ts are not supported in JSON - std::stringstream start_str; - start_str << starting_frame; - std::stringstream end_str; - end_str << ending_frame; - range["start"] = start_str.str(); - range["end"] = end_str.str(); + range["start"] = std::to_string(starting_frame); + range["end"] = std::to_string(ending_frame); ranges.append(range); // Cache range JSON as string @@ -327,7 +319,7 @@ std::string CacheMemory::Json() { return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object +// Generate Json::Value for this object Json::Value CacheMemory::JsonValue() { // Process range data (if anything has changed) @@ -337,45 +329,25 @@ Json::Value CacheMemory::JsonValue() { Json::Value root = CacheBase::JsonValue(); // get parent properties root["type"] = cache_type; - std::stringstream range_version_str; - range_version_str << range_version; - root["version"] = range_version_str.str(); + root["version"] = std::to_string(range_version); // Parse and append range data (if any) - Json::Value ranges; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( json_ranges.c_str(), - json_ranges.c_str() + json_ranges.size(), &ranges, &errors ); - delete reader; - - if (success) + try { + const Json::Value ranges = openshot::stringToJson(json_ranges); root["ranges"] = ranges; + } catch (...) { } // return JsonValue return root; } // Load JSON string into this object -void CacheMemory::SetJson(std::string value) { - - // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); +void CacheMemory::SetJson(const std::string value) { try { + // Parse string to Json::Value + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -386,8 +358,8 @@ void CacheMemory::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void CacheMemory::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void CacheMemory::SetJsonValue(const Json::Value root) { // Close timeline before we do anything (this also removes all open and closing clips) Clear(); diff --git a/src/ChunkReader.cpp b/src/ChunkReader.cpp index 7321cb268..c194ce33a 100644 --- a/src/ChunkReader.cpp +++ b/src/ChunkReader.cpp @@ -256,14 +256,14 @@ std::shared_ptr ChunkReader::GetFrame(int64_t requested_frame) } // Generate JSON string of this object -std::string ChunkReader::Json() { +std::string ChunkReader::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value ChunkReader::JsonValue() { +// Generate Json::Value for this object +Json::Value ChunkReader::JsonValue() const { // Create root json object Json::Value root = ReaderBase::JsonValue(); // get parent properties @@ -279,23 +279,11 @@ Json::Value ChunkReader::JsonValue() { } // Load JSON string into this object -void ChunkReader::SetJson(std::string value) { - - // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); +void ChunkReader::SetJson(const std::string value) { try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -306,8 +294,8 @@ void ChunkReader::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void ChunkReader::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void ChunkReader::SetJsonValue(const Json::Value root) { // Set parent data ReaderBase::SetJsonValue(root); diff --git a/src/Clip.cpp b/src/Clip.cpp index 45afd2aa5..1968bb3b1 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -38,6 +38,7 @@ #include "../include/QtImageReader.h" #include "../include/ChunkReader.h" #include "../include/DummyReader.h" +#include "../include/Timeline.h" using namespace openshot; @@ -159,7 +160,7 @@ Clip::Clip(std::string path) : resampler(NULL), audio_cache(NULL), reader(NULL), // Get file extension (and convert to lower case) std::string ext = get_file_extension(path); - transform(ext.begin(), ext.end(), ext.begin(), ::tolower); + std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); // Determine if common video formats if (ext=="avi" || ext=="mov" || ext=="mkv" || ext=="mpg" || ext=="mpeg" || ext=="mp3" || ext=="mp4" || ext=="mts" || @@ -172,6 +173,16 @@ Clip::Clip(std::string path) : resampler(NULL), audio_cache(NULL), reader(NULL), } catch(...) { } } + if (ext=="osp") + { + try + { + // Open common video format + reader = new Timeline(path, true); + + } catch(...) { } + } + // If no video found, try each reader if (!reader) @@ -270,7 +281,7 @@ void Clip::Close() } // Get end position of clip (trim end of video), which can be affected by the time curve. -float Clip::End() +float Clip::End() const { // if a time curve is present, use its length if (time.GetCount() > 1) @@ -319,12 +330,10 @@ std::shared_ptr Clip::GetFrame(int64_t requested_frame) // Now that we have re-mapped what frame number is needed, go and get the frame pointer std::shared_ptr original_frame; - #pragma omp critical (Clip_GetFrame) original_frame = GetOrCreateFrame(new_frame_number); // Create a new frame std::shared_ptr frame(new Frame(new_frame_number, 1, 1, "#000000", original_frame->GetAudioSamplesCount(), original_frame->GetAudioChannelsCount())); - #pragma omp critical (Clip_GetFrame) { frame->SampleRate(original_frame->SampleRate()); frame->ChannelsLayout(original_frame->ChannelsLayout()); @@ -645,14 +654,14 @@ std::shared_ptr Clip::GetOrCreateFrame(int64_t number) } // Generate JSON string of this object -std::string Clip::Json() { +std::string Clip::Json() const { // Return formatted string return JsonValue().toStyledString(); } // Get all properties for a specific frame -std::string Clip::PropertiesJSON(int64_t requested_frame) { +std::string Clip::PropertiesJSON(int64_t requested_frame) const { // Generate JSON properties list Json::Value root; @@ -739,8 +748,8 @@ std::string Clip::PropertiesJSON(int64_t requested_frame) { return root.toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value Clip::JsonValue() { +// Generate Json::Value for this object +Json::Value Clip::JsonValue() const { // Create root json object Json::Value root = ClipBase::JsonValue(); // get parent properties @@ -782,40 +791,27 @@ Json::Value Clip::JsonValue() { root["effects"] = Json::Value(Json::arrayValue); // loop through effects - std::list::iterator effect_itr; - for (effect_itr=effects.begin(); effect_itr != effects.end(); ++effect_itr) + for (auto existing_effect : effects) { - // Get clip object from the iterator - EffectBase *existing_effect = (*effect_itr); root["effects"].append(existing_effect->JsonValue()); } if (reader) root["reader"] = reader->JsonValue(); + else + root["reader"] = Json::Value(Json::objectValue); // return JsonValue return root; } // Load JSON string into this object -void Clip::SetJson(std::string value) { +void Clip::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -826,8 +822,8 @@ void Clip::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void Clip::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void Clip::SetJsonValue(const Json::Value root) { // Set parent data ClipBase::SetJsonValue(root); @@ -905,10 +901,7 @@ void Clip::SetJsonValue(Json::Value root) { effects.clear(); // loop through effects - for (int x = 0; x < root["effects"].size(); x++) { - // Get each effect - Json::Value existing_effect = root["effects"][x]; - + for (const auto existing_effect : root["effects"]) { // Create Effect EffectBase *e = NULL; @@ -982,6 +975,12 @@ void Clip::SetJsonValue(Json::Value root) { // Create new reader reader = new DummyReader(); reader->SetJsonValue(root["reader"]); + + } else if (type == "Timeline") { + + // Create new reader (always load from file again) + // This prevents FrameMappers from being loaded on accident + reader = new Timeline(root["reader"]["path"].asString(), true); } // mark as managed reader and set parent @@ -1025,12 +1024,8 @@ void Clip::RemoveEffect(EffectBase* effect) std::shared_ptr Clip::apply_effects(std::shared_ptr frame) { // Find Effects at this position and layer - std::list::iterator effect_itr; - for (effect_itr=effects.begin(); effect_itr != effects.end(); ++effect_itr) + for (auto effect : effects) { - // Get clip object from the iterator - EffectBase *effect = (*effect_itr); - // Apply the effect to this frame frame = effect->GetFrame(frame, frame->number); diff --git a/src/ClipBase.cpp b/src/ClipBase.cpp index d1370205b..a51c65735 100644 --- a/src/ClipBase.cpp +++ b/src/ClipBase.cpp @@ -32,8 +32,8 @@ using namespace openshot; -// Generate Json::JsonValue for this object -Json::Value ClipBase::JsonValue() { +// Generate Json::Value for this object +Json::Value ClipBase::JsonValue() const { // Create root json object Json::Value root; @@ -48,8 +48,8 @@ Json::Value ClipBase::JsonValue() { return root; } -// Load Json::JsonValue into this object -void ClipBase::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void ClipBase::SetJsonValue(const Json::Value root) { // Set data from Json (if key is found) if (!root["id"].isNull()) @@ -65,10 +65,10 @@ void ClipBase::SetJsonValue(Json::Value root) { } // Generate JSON for a property -Json::Value ClipBase::add_property_json(std::string name, float value, std::string type, std::string memo, Keyframe* keyframe, float min_value, float max_value, bool readonly, int64_t requested_frame) { +Json::Value ClipBase::add_property_json(std::string name, float value, std::string type, std::string memo, const Keyframe* keyframe, float min_value, float max_value, bool readonly, int64_t requested_frame) const { // Requested Point - Point requested_point(requested_frame, requested_frame); + const Point requested_point(requested_frame, requested_frame); // Create JSON Object Json::Value prop = Json::Value(Json::objectValue); @@ -101,7 +101,7 @@ Json::Value ClipBase::add_property_json(std::string name, float value, std::stri return prop; } -Json::Value ClipBase::add_property_choice_json(std::string name, int value, int selected_value) { +Json::Value ClipBase::add_property_choice_json(std::string name, int value, int selected_value) const { // Create choice Json::Value new_choice = Json::Value(Json::objectValue); diff --git a/src/Color.cpp b/src/Color.cpp index 927fc1624..705ece4a1 100644 --- a/src/Color.cpp +++ b/src/Color.cpp @@ -85,14 +85,14 @@ long Color::GetDistance(long R1, long G1, long B1, long R2, long G2, long B2) } // Generate JSON string of this object -std::string Color::Json() { +std::string Color::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value Color::JsonValue() { +// Generate Json::Value for this object +Json::Value Color::JsonValue() const { // Create root json object Json::Value root; @@ -106,24 +106,12 @@ Json::Value Color::JsonValue() { } // Load JSON string into this object -void Color::SetJson(std::string value) { +void Color::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -134,8 +122,8 @@ void Color::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void Color::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void Color::SetJsonValue(const Json::Value root) { // Set data from Json (if key is found) if (!root["red"].isNull()) diff --git a/src/Coordinate.cpp b/src/Coordinate.cpp index a0bdabcc3..f87fb7a0e 100644 --- a/src/Coordinate.cpp +++ b/src/Coordinate.cpp @@ -45,14 +45,14 @@ Coordinate::Coordinate(double x, double y) : // Generate JSON string of this object -std::string Coordinate::Json() { +std::string Coordinate::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value Coordinate::JsonValue() { +// Generate Json::Value for this object +Json::Value Coordinate::JsonValue() const { // Create root json object Json::Value root; @@ -69,24 +69,12 @@ Json::Value Coordinate::JsonValue() { } // Load JSON string into this object -void Coordinate::SetJson(std::string value) { +void Coordinate::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -97,8 +85,8 @@ void Coordinate::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void Coordinate::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void Coordinate::SetJsonValue(const Json::Value root) { // Set data from Json (if key is found) if (!root["X"].isNull()) diff --git a/src/DecklinkReader.cpp b/src/DecklinkReader.cpp index 35ed18578..14c2f87ca 100644 --- a/src/DecklinkReader.cpp +++ b/src/DecklinkReader.cpp @@ -246,14 +246,14 @@ std::shared_ptr DecklinkReader::GetFrame(int64_t requested_frame) // Generate JSON string of this object -std::string DecklinkReader::Json() { +std::string DecklinkReader::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value DecklinkReader::JsonValue() { +// Generate Json::Value for this object +Json::Value DecklinkReader::JsonValue() const { // Create root json object Json::Value root = ReaderBase::JsonValue(); // get parent properties @@ -264,24 +264,12 @@ Json::Value DecklinkReader::JsonValue() { } // Load JSON string into this object -void DecklinkReader::SetJson(std::string value) { +void DecklinkReader::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -292,8 +280,8 @@ void DecklinkReader::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void DecklinkReader::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void DecklinkReader::SetJsonValue(const Json::Value root) { // Set parent data ReaderBase::SetJsonValue(root); diff --git a/src/DummyReader.cpp b/src/DummyReader.cpp index 997a020ae..8fd98bcbc 100644 --- a/src/DummyReader.cpp +++ b/src/DummyReader.cpp @@ -124,14 +124,14 @@ std::shared_ptr DummyReader::GetFrame(int64_t requested_frame) } // Generate JSON string of this object -std::string DummyReader::Json() { +std::string DummyReader::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value DummyReader::JsonValue() { +// Generate Json::Value for this object +Json::Value DummyReader::JsonValue() const { // Create root json object Json::Value root = ReaderBase::JsonValue(); // get parent properties @@ -142,24 +142,12 @@ Json::Value DummyReader::JsonValue() { } // Load JSON string into this object -void DummyReader::SetJson(std::string value) { +void DummyReader::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -170,8 +158,8 @@ void DummyReader::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void DummyReader::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void DummyReader::SetJsonValue(const Json::Value root) { // Set parent data ReaderBase::SetJsonValue(root); diff --git a/src/EffectBase.cpp b/src/EffectBase.cpp index 30e837652..05ed97c2a 100644 --- a/src/EffectBase.cpp +++ b/src/EffectBase.cpp @@ -74,20 +74,19 @@ int EffectBase::constrain(int color_value) } // Generate JSON string of this object -std::string EffectBase::Json() { +std::string EffectBase::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value EffectBase::JsonValue() { +// Generate Json::Value for this object +Json::Value EffectBase::JsonValue() const { // Create root json object Json::Value root = ClipBase::JsonValue(); // get parent properties root["name"] = info.name; root["class_name"] = info.class_name; - root["short_name"] = info.short_name; root["description"] = info.description; root["has_video"] = info.has_video; root["has_audio"] = info.has_audio; @@ -98,24 +97,12 @@ Json::Value EffectBase::JsonValue() { } // Load JSON string into this object -void EffectBase::SetJson(std::string value) { +void EffectBase::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -126,8 +113,8 @@ void EffectBase::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void EffectBase::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void EffectBase::SetJsonValue(const Json::Value root) { // Set parent data ClipBase::SetJsonValue(root); @@ -137,14 +124,13 @@ void EffectBase::SetJsonValue(Json::Value root) { Order(root["order"].asInt()); } -// Generate Json::JsonValue for this object -Json::Value EffectBase::JsonInfo() { +// Generate Json::Value for this object +Json::Value EffectBase::JsonInfo() const { // Create root json object Json::Value root; root["name"] = info.name; root["class_name"] = info.class_name; - root["short_name"] = info.short_name; root["description"] = info.description; root["has_video"] = info.has_video; root["has_audio"] = info.has_audio; diff --git a/src/EffectInfo.cpp b/src/EffectInfo.cpp index 8fb8a4fe4..6829f4eb5 100644 --- a/src/EffectInfo.cpp +++ b/src/EffectInfo.cpp @@ -56,7 +56,7 @@ EffectBase* EffectInfo::CreateEffect(std::string effect_type) { else if (effect_type == "ChromaKey") return new ChromaKey(); - else if (effect_type == "Color Shift") + else if (effect_type == "ColorShift") return new ColorShift(); else if (effect_type == "Crop") @@ -88,7 +88,7 @@ EffectBase* EffectInfo::CreateEffect(std::string effect_type) { return NULL; } -// Generate Json::JsonValue for this object +// Generate Json::Value for this object Json::Value EffectInfo::JsonValue() { // Create root json object diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp index e8ac4af91..2b03994be 100644 --- a/src/FFmpegReader.cpp +++ b/src/FFmpegReader.cpp @@ -35,13 +35,13 @@ #define ENABLE_VAAPI 0 -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL #pragma message "You are compiling with experimental hardware decode" #else #pragma message "You are compiling only with software decode" #endif -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL #define MAX_SUPPORTED_WIDTH 1950 #define MAX_SUPPORTED_HEIGHT 1100 @@ -71,14 +71,14 @@ typedef struct VAAPIDecodeContext { enum AVPixelFormat surface_format; int surface_count; } VAAPIDecodeContext; -#endif -#endif +#endif // ENABLE_VAAPI +#endif // HAVE_HW_ACCEL using namespace openshot; int hw_de_on = 0; -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL AVPixelFormat hw_de_av_pix_fmt_global = AV_PIX_FMT_NONE; AVHWDeviceType hw_de_av_device_type_global = AV_HWDEVICE_TYPE_NONE; #endif @@ -153,7 +153,7 @@ bool AudioLocation::is_near(AudioLocation location, int samples_per_frame, int64 return false; } -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL // Get hardware pix format static enum AVPixelFormat get_hw_dec_format(AVCodecContext *ctx, const enum AVPixelFormat *pix_fmts) @@ -191,10 +191,10 @@ static enum AVPixelFormat get_hw_dec_format(AVCodecContext *ctx, const enum AVPi #if defined(__APPLE__) // Apple pix formats case AV_PIX_FMT_VIDEOTOOLBOX: - hw_de_av_pix_fmt_global = AV_PIX_FMT_VIDEOTOOLBOX; - hw_de_av_device_type_global = AV_HWDEVICE_TYPE_VIDEOTOOLBOX; - return *p; - break; + hw_de_av_pix_fmt_global = AV_PIX_FMT_VIDEOTOOLBOX; + hw_de_av_device_type_global = AV_HWDEVICE_TYPE_VIDEOTOOLBOX; + return *p; + break; #endif // Cross-platform pix formats case AV_PIX_FMT_CUDA: @@ -207,6 +207,9 @@ static enum AVPixelFormat get_hw_dec_format(AVCodecContext *ctx, const enum AVPi hw_de_av_device_type_global = AV_HWDEVICE_TYPE_QSV; return *p; break; + default: + // This is only here to silence unused-enum warnings + break; } } ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::get_hw_dec_format (Unable to decode this file using hardware decode)"); @@ -231,7 +234,7 @@ int FFmpegReader::IsHardwareDecodeSupported(int codecid) } return ret; } -#endif +#endif // HAVE_HW_ACCEL void FFmpegReader::Open() { // Open reader if not already open @@ -284,7 +287,7 @@ void FFmpegReader::Open() { // If hw accel is selected but hardware cannot handle repeat with software decoding do { pCodecCtx = AV_GET_CODEC_CONTEXT(pStream, pCodec); -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL if (hw_de_on && (retry_decode_open==2)) { // Up to here no decision is made if hardware or software decode hw_de_supported = IsHardwareDecodeSupported(pCodecCtx->codec_id); @@ -301,7 +304,7 @@ void FFmpegReader::Open() { // Init options av_dict_set(&opts, "strict", "experimental", 0); -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL if (hw_de_on && hw_de_supported) { // Open Hardware Acceleration int i_decoder_hw = 0; @@ -430,13 +433,13 @@ void FFmpegReader::Open() { throw InvalidCodec("Hardware device create failed.", path); } } -#endif +#endif // HAVE_HW_ACCEL // Open video codec if (avcodec_open2(pCodecCtx, pCodec, &opts) < 0) throw InvalidCodec("A video codec was found, but could not be opened.", path); -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL if (hw_de_on && hw_de_supported) { AVHWFramesConstraints *constraints = NULL; void *hwconfig = NULL; @@ -446,7 +449,7 @@ void FFmpegReader::Open() { #if ENABLE_VAAPI ((AVVAAPIHWConfig *)hwconfig)->config_id = ((VAAPIDecodeContext *)(pCodecCtx->priv_data))->va_config; constraints = av_hwdevice_get_hwframe_constraints(hw_device_ctx,hwconfig); -#endif +#endif // ENABLE_VAAPI if (constraints) { if (pCodecCtx->coded_width < constraints->min_width || pCodecCtx->coded_height < constraints->min_height || @@ -503,7 +506,7 @@ void FFmpegReader::Open() { } #else retry_decode_open = 0; -#endif +#endif // HAVE_HW_ACCEL } while (retry_decode_open); // retry_decode_open // Free options av_dict_free(&opts); @@ -589,14 +592,14 @@ void FFmpegReader::Close() { if (info.has_video) { avcodec_flush_buffers(pCodecCtx); AV_FREE_CONTEXT(pCodecCtx); -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL if (hw_de_on) { if (hw_device_ctx) { av_buffer_unref(&hw_device_ctx); hw_device_ctx = NULL; } } -#endif +#endif // HAVE_HW_ACCEL } if (info.has_audio) { avcodec_flush_buffers(aCodecCtx); @@ -1097,19 +1100,22 @@ bool FFmpegReader::GetAVFrame() { ret = avcodec_send_packet(pCodecCtx, packet); + #if HAVE_HW_ACCEL // Get the format from the variables set in get_hw_dec_format hw_de_av_pix_fmt = hw_de_av_pix_fmt_global; hw_de_av_device_type = hw_de_av_device_type_global; - + #endif // HAVE_HW_ACCEL if (ret < 0 || ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::GetAVFrame (Packet not sent)"); } else { AVFrame *next_frame2; + #if HAVE_HW_ACCEL if (hw_de_on && hw_de_supported) { next_frame2 = AV_ALLOCATE_FRAME(); } else + #endif // HAVE_HW_ACCEL { next_frame2 = next_frame; } @@ -1122,6 +1128,7 @@ bool FFmpegReader::GetAVFrame() { if (ret != 0) { ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::GetAVFrame (invalid return frame received)"); } + #if HAVE_HW_ACCEL if (hw_de_on && hw_de_supported) { int err; if (next_frame2->format == hw_de_av_pix_fmt) { @@ -1135,6 +1142,7 @@ bool FFmpegReader::GetAVFrame() { } } else + #endif // HAVE_HW_ACCEL { // No hardware acceleration used -> no copy from GPU memory needed next_frame = next_frame2; } @@ -1148,9 +1156,11 @@ bool FFmpegReader::GetAVFrame() { (AVPixelFormat)(pStream->codecpar->format), info.width, info.height); } } + #if HAVE_HW_ACCEL if (hw_de_on && hw_de_supported) { AV_FREE_FRAME(&next_frame2); } + #endif // HAVE_HW_ACCEL } #else avcodec_decode_video2(pCodecCtx, next_frame, &frameFinished, packet); @@ -1166,7 +1176,7 @@ bool FFmpegReader::GetAVFrame() { av_picture_copy((AVPicture *) pFrame, (AVPicture *) next_frame, pCodecCtx->pix_fmt, info.width, info.height); } -#endif +#endif // IS_FFMPEG_3_2 } // deallocate the frame @@ -2083,7 +2093,7 @@ bool FFmpegReader::CheckMissingFrame(int64_t requested_frame) { // CPU time looking for missing images for all the audio-only frames. if (checked_frames[requested_frame] > 8 && !missing_video_frames.count(requested_frame) && !processing_audio_frames.count(requested_frame) && processed_audio_frames.count(requested_frame) && - last_frame && last_video_frame->has_image_data && aCodecId == AV_CODEC_ID_MP3 && (vCodecId == AV_CODEC_ID_MJPEGB || vCodecId == AV_CODEC_ID_MJPEG)) { + last_frame && last_video_frame && last_video_frame->has_image_data && aCodecId == AV_CODEC_ID_MP3 && (vCodecId == AV_CODEC_ID_MJPEGB || vCodecId == AV_CODEC_ID_MJPEG)) { missing_video_frames.insert(std::pair(requested_frame, last_video_frame->number)); missing_video_frames_source.insert(std::pair(last_video_frame->number, requested_frame)); missing_frames.Add(last_video_frame); @@ -2424,14 +2434,14 @@ int64_t FFmpegReader::GetSmallestAudioFrame() { } // Generate JSON string of this object -std::string FFmpegReader::Json() { +std::string FFmpegReader::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value FFmpegReader::JsonValue() { +// Generate Json::Value for this object +Json::Value FFmpegReader::JsonValue() const { // Create root json object Json::Value root = ReaderBase::JsonValue(); // get parent properties @@ -2443,23 +2453,11 @@ Json::Value FFmpegReader::JsonValue() { } // Load JSON string into this object -void FFmpegReader::SetJson(std::string value) { +void FFmpegReader::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse(value.c_str(), value.c_str() + value.size(), - &root, &errors); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -2469,8 +2467,8 @@ void FFmpegReader::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void FFmpegReader::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void FFmpegReader::SetJsonValue(const Json::Value root) { // Set parent data ReaderBase::SetJsonValue(root); diff --git a/src/FFmpegWriter.cpp b/src/FFmpegWriter.cpp index 645c0cca4..e6a1d180a 100644 --- a/src/FFmpegWriter.cpp +++ b/src/FFmpegWriter.cpp @@ -35,7 +35,7 @@ using namespace openshot; -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL #pragma message "You are compiling with experimental hardware encode" #else #pragma message "You are compiling only with software encode" @@ -44,7 +44,7 @@ using namespace openshot; // Multiplexer parameters temporary storage AVDictionary *mux_dict = NULL; -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL int hw_en_on = 1; // Is set in UI int hw_en_supported = 0; // Is set by FFmpegWriter AVPixelFormat hw_en_av_pix_fmt = AV_PIX_FMT_NONE; @@ -81,7 +81,7 @@ static int set_hwframe_ctx(AVCodecContext *ctx, AVBufferRef *hw_device_ctx, int6 av_buffer_unref(&hw_frames_ref); return err; } -#endif +#endif // HAVE_HW_ACCEL FFmpegWriter::FFmpegWriter(std::string path) : path(path), fmt(NULL), oc(NULL), audio_st(NULL), video_st(NULL), audio_pts(0), video_pts(0), samples(NULL), @@ -171,7 +171,7 @@ void FFmpegWriter::SetVideoOptions(bool has_video, std::string codec, Fraction f if (codec.length() > 0) { AVCodec *new_codec; // Check if the codec selected is a hardware accelerated codec -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL #if defined(__linux__) if (strstr(codec.c_str(), "_vaapi") != NULL) { new_codec = avcodec_find_encoder_by_name(codec.c_str()); @@ -225,7 +225,7 @@ void FFmpegWriter::SetVideoOptions(bool has_video, std::string codec, Fraction f #endif //__linux__ #else // not ffmpeg 3 new_codec = avcodec_find_encoder_by_name(codec.c_str()); -#endif //IS_FFMPEG_3_2 +#endif // HAVE_HW_ACCEL if (new_codec == NULL) throw InvalidCodec("A valid video codec could not be found for this file.", path); else { @@ -277,6 +277,14 @@ void FFmpegWriter::SetVideoOptions(bool has_video, std::string codec, Fraction f info.has_video = has_video; } +// Set video export options (overloaded function) +void FFmpegWriter::SetVideoOptions(std::string codec, int width, int height, Fraction fps, int bit_rate) { + // Call full signature with some default parameters + FFmpegWriter::SetVideoOptions(true, codec, fps, width, height, + openshot::Fraction(1, 1), false, true, bit_rate); +} + + // Set audio export options void FFmpegWriter::SetAudioOptions(bool has_audio, std::string codec, int sample_rate, int channels, ChannelLayout channel_layout, int bit_rate) { // Set audio options @@ -312,6 +320,15 @@ void FFmpegWriter::SetAudioOptions(bool has_audio, std::string codec, int sample info.has_audio = has_audio; } + +// Set audio export options (overloaded function) +void FFmpegWriter::SetAudioOptions(std::string codec, int sample_rate, int bit_rate) { + // Call full signature with some default parameters + FFmpegWriter::SetAudioOptions(true, codec, sample_rate, 2, + openshot::LAYOUT_STEREO, bit_rate); +} + + // Set custom options (some codecs accept additional params) void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string value) { // Declare codec context @@ -392,11 +409,11 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va // This might be better in an extra methods as more options // and way to set quality are possible #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 39, 101) - #if IS_FFMPEG_3_2 + #if HAVE_HW_ACCEL if (hw_en_on) { av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value),63), 0); // 0-63 } else - #endif + #endif // HAVE_HW_ACCEL { switch (c->codec_id) { #if (LIBAVCODEC_VERSION_MAJOR >= 58) @@ -423,7 +440,7 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va av_opt_set(c->priv_data, "preset", "veryslow", 0); } break; - case AV_CODEC_ID_H265 : + case AV_CODEC_ID_HEVC : av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value), 51), 0); // 0-51 if (std::stoi(value) == 0) { av_opt_set(c->priv_data, "preset", "veryslow", 0); @@ -442,7 +459,7 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va // This might be better in an extra methods as more options // and way to set quality are possible #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 39, 101) -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL if (hw_en_on) { double mbs = 15000000.0; if (info.video_bit_rate > 0) { @@ -455,7 +472,7 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va } c->bit_rate = (int)(mbs); } else -#endif +#endif // HAVE_HW_ACCEL { switch (c->codec_id) { #if (LIBAVCODEC_VERSION_MAJOR >= 58) @@ -482,7 +499,7 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va av_opt_set(c->priv_data, "preset", "veryslow", 0); } break; - case AV_CODEC_ID_H265 : + case AV_CODEC_ID_HEVC : av_opt_set_int(c->priv_data, "crf", std::min(std::stoi(value), 51), 0); // 0-51 if (std::stoi(value) == 0) { av_opt_set(c->priv_data, "preset", "veryslow", 0); @@ -615,7 +632,7 @@ void FFmpegWriter::WriteFrame(std::shared_ptr frame) { ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::WriteFrame", "frame->number", frame->number, "spooled_video_frames.size()", spooled_video_frames.size(), "spooled_audio_frames.size()", spooled_audio_frames.size(), "cache_size", cache_size, "is_writing", is_writing); // Write the frames once it reaches the correct cache size - if (spooled_video_frames.size() == cache_size || spooled_audio_frames.size() == cache_size) { + if ((int)spooled_video_frames.size() == cache_size || (int)spooled_audio_frames.size() == cache_size) { // Is writer currently writing? if (!is_writing) // Write frames to video file @@ -955,16 +972,14 @@ void FFmpegWriter::flush_encoders() { // Close the video codec void FFmpegWriter::close_video(AVFormatContext *oc, AVStream *st) { -#if IS_FFMPEG_3_2 - // #if defined(__linux__) - if (hw_en_on && hw_en_supported) { - if (hw_device_ctx) { - av_buffer_unref(&hw_device_ctx); - hw_device_ctx = NULL; - } +#if HAVE_HW_ACCEL + if (hw_en_on && hw_en_supported) { + if (hw_device_ctx) { + av_buffer_unref(&hw_device_ctx); + hw_device_ctx = NULL; } - // #endif -#endif + } +#endif // HAVE_HW_ACCEL } // Close the audio codec @@ -1083,7 +1098,7 @@ AVStream *FFmpegWriter::add_audio_stream() { // Set a valid number of channels (or throw error) - int channel_layout = info.channel_layout; + const uint64_t channel_layout = info.channel_layout; if (codec->channel_layouts) { int i; for (i = 0; codec->channel_layouts[i] != 0; i++) @@ -1162,7 +1177,7 @@ AVStream *FFmpegWriter::add_video_stream() { case AV_CODEC_ID_AV1 : #endif case AV_CODEC_ID_VP9 : - case AV_CODEC_ID_H265 : + case AV_CODEC_ID_HEVC : #endif case AV_CODEC_ID_VP8 : case AV_CODEC_ID_H264 : @@ -1200,7 +1215,8 @@ AVStream *FFmpegWriter::add_video_stream() { identically 1. */ c->time_base.num = info.video_timebase.num; c->time_base.den = info.video_timebase.den; -#if LIBAVFORMAT_VERSION_MAJOR >= 56 +// AVCodecContext->framerate was added in FFmpeg 2.2 +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(56, 26, 0) c->framerate = av_inv_q(c->time_base); #endif st->avg_frame_rate = av_inv_q(c->time_base); @@ -1341,7 +1357,7 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) { // Set number of threads equal to number of processors (not to exceed 16) video_codec->thread_count = std::min(FF_NUM_PROCESSORS, 16); -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL if (hw_en_on && hw_en_supported) { //char *dev_hw = NULL; char adapter[256]; @@ -1384,7 +1400,7 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) { throw InvalidCodec("Could not create hwdevice", path); } } -#endif +#endif // HAVE_HW_ACCEL /* find the video encoder */ codec = avcodec_find_encoder_by_name(info.vcodec.c_str()); @@ -1401,7 +1417,7 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) { AVDictionary *opts = NULL; av_dict_set(&opts, "strict", "experimental", 0); -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL if (hw_en_on && hw_en_supported) { video_codec->pix_fmt = hw_en_av_pix_fmt; @@ -1417,7 +1433,7 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) { // unless "qp" was set for CQP, switch to VBR RC mode av_opt_set(video_codec->priv_data, "rc_mode", "VBR", 0); - // In the current state (ffmpeg-4.2-4 libva-mesa-driver-19.1.5-1) to use VBR, + // In the current state (ffmpeg-4.2-4 libva-mesa-driver-19.1.5-1) to use VBR, // one has to specify both bit_rate and maxrate, otherwise a small low quality file is generated on Intel iGPU). video_codec->rc_max_rate = video_codec->bit_rate; } @@ -1450,7 +1466,7 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) { "width", info.width, "height", info.height, av_err2str(err), -1); } } -#endif +#endif // HAVE_HW_ACCEL /* open the codec */ if (avcodec_open2(video_codec, codec, &opts) < 0) @@ -1482,7 +1498,8 @@ void FFmpegWriter::write_audio_packets(bool is_final) { ChannelLayout channel_layout_in_frame = LAYOUT_MONO; // default channel layout // Create a new array (to hold all S16 audio samples, for the current queued frames - int16_t *all_queued_samples = (int16_t *) av_malloc((sizeof(int16_t) * (queued_audio_frames.size() * AVCODEC_MAX_AUDIO_FRAME_SIZE))); + unsigned int all_queued_samples_size = sizeof(int16_t) * (queued_audio_frames.size() * AVCODEC_MAX_AUDIO_FRAME_SIZE); + int16_t *all_queued_samples = (int16_t *) av_malloc(all_queued_samples_size); int16_t *all_resampled_samples = NULL; int16_t *final_samples_planar = NULL; int16_t *final_samples = NULL; @@ -1542,8 +1559,10 @@ void FFmpegWriter::write_audio_packets(bool is_final) { audio_frame->nb_samples = total_frame_samples / channels_in_frame; // Fill input frame with sample data - avcodec_fill_audio_frame(audio_frame, channels_in_frame, AV_SAMPLE_FMT_S16, (uint8_t *) all_queued_samples, - audio_encoder_buffer_size, 0); + int error_code = avcodec_fill_audio_frame(audio_frame, channels_in_frame, AV_SAMPLE_FMT_S16, (uint8_t *) all_queued_samples, all_queued_samples_size, 0); + if (error_code < 0) { + ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_audio_packets ERROR [" + (std::string) av_err2str(error_code) + "]", "error_code", error_code); + } // Do not convert audio to planar format (yet). We need to keep everything interleaved at this point. switch (audio_codec->sample_fmt) { @@ -1563,15 +1582,16 @@ void FFmpegWriter::write_audio_packets(bool is_final) { output_sample_fmt = AV_SAMPLE_FMT_U8; break; } + default: { + // This is only here to silence unused-enum warnings + break; + } } // Update total samples & input frame size (due to bigger or smaller data types) total_frame_samples *= (float(info.sample_rate) / sample_rate_in_frame); // adjust for different byte sizes total_frame_samples *= (float(info.channels) / channels_in_frame); // adjust for different # of channels - // Set remaining samples - remaining_frame_samples = total_frame_samples; - // Create output frame (and allocate arrays) AVFrame *audio_converted = AV_ALLOCATE_FRAME(); AV_RESET_FRAME(audio_converted); @@ -1604,6 +1624,9 @@ void FFmpegWriter::write_audio_packets(bool is_final) { audio_frame->linesize[0], // input plane size, in bytes (0 if unknown) audio_frame->nb_samples); // number of input samples to convert + // Set remaining samples + remaining_frame_samples = nb_samples * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16); + // Create a new array (to hold all resampled S16 audio samples) all_resampled_samples = (int16_t *) av_malloc( sizeof(int16_t) * nb_samples * info.channels * (av_get_bytes_per_sample(output_sample_fmt) / av_get_bytes_per_sample(AV_SAMPLE_FMT_S16))); @@ -1892,14 +1915,17 @@ void FFmpegWriter::process_video_packet(std::shared_ptr frame) { frame_source = allocate_avframe(PIX_FMT_RGBA, source_image_width, source_image_height, &bytes_source, (uint8_t *) pixels); #if IS_FFMPEG_3_2 AVFrame *frame_final; + #if HAVE_HW_ACCEL if (hw_en_on && hw_en_supported) { frame_final = allocate_avframe(AV_PIX_FMT_NV12, info.width, info.height, &bytes_final, NULL); - } else { + } else + #endif // HAVE_HW_ACCEL + { frame_final = allocate_avframe((AVPixelFormat)(video_st->codecpar->format), info.width, info.height, &bytes_final, NULL); } #else AVFrame *frame_final = allocate_avframe(video_codec->pix_fmt, info.width, info.height, &bytes_final, NULL); -#endif +#endif // IS_FFMPEG_3_2 // Fill with data AV_COPY_PICTURE_DATA(frame_source, (uint8_t *) pixels, PIX_FMT_RGBA, source_image_width, source_image_height); @@ -1969,7 +1995,7 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra // Assign the initial AVFrame PTS from the frame counter frame_final->pts = write_video_count; -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL if (hw_en_on && hw_en_supported) { if (!(hw_frame = av_frame_alloc())) { fprintf(stderr, "Error code: av_hwframe_alloc\n"); @@ -1986,7 +2012,7 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra } av_frame_copy_props(hw_frame, frame_final); } -#endif +#endif // HAVE_HW_ACCEL /* encode the image */ int got_packet_ptr = 0; int error_code = 0; @@ -1995,9 +2021,12 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra int frameFinished = 0; int ret; + #if HAVE_HW_ACCEL if (hw_en_on && hw_en_supported) { ret = avcodec_send_frame(video_codec, hw_frame); //hw_frame!!! - } else { + } else + #endif // HAVE_HW_ACCEL + { ret = avcodec_send_frame(video_codec, frame_final); } error_code = ret; @@ -2054,8 +2083,8 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra // got data back (so encode this frame) got_packet_ptr = 1; } -#endif -#endif +#endif // LIBAVFORMAT_VERSION_MAJOR >= 54 +#endif // IS_FFMPEG_3_2 /* if zero size, it means the image was buffered */ if (error_code == 0 && got_packet_ptr) { @@ -2087,14 +2116,14 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra // Deallocate packet AV_FREE_PACKET(&pkt); -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL if (hw_en_on && hw_en_supported) { if (hw_frame) { av_frame_free(&hw_frame); hw_frame = NULL; } } -#endif +#endif // HAVE_HW_ACCEL } // Success @@ -2117,11 +2146,11 @@ void FFmpegWriter::InitScalers(int source_width, int source_height) { // Init software rescalers vector (many of them, one for each thread) for (int x = 0; x < num_of_rescalers; x++) { // Init the software scaler from FFMpeg -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL if (hw_en_on && hw_en_supported) { img_convert_ctx = sws_getContext(source_width, source_height, PIX_FMT_RGBA, info.width, info.height, AV_PIX_FMT_NV12, scale_mode, NULL, NULL, NULL); } else -#endif +#endif // HAVE_HW_ACCEL { img_convert_ctx = sws_getContext(source_width, source_height, PIX_FMT_RGBA, info.width, info.height, AV_GET_CODEC_PIXEL_FORMAT(video_st, video_st->codec), scale_mode, NULL, NULL, NULL); diff --git a/src/Frame.cpp b/src/Frame.cpp index 40183422c..9577a7f9e 100644 --- a/src/Frame.cpp +++ b/src/Frame.cpp @@ -43,7 +43,7 @@ Frame::Frame() : number(1), pixel_ratio(1,1), channels(2), width(1), height(1), // initialize the audio samples to zero (silence) audio->clear(); -}; +} // Constructor - image only (48kHz audio silence) Frame::Frame(int64_t number, int width, int height, std::string color) @@ -56,7 +56,7 @@ Frame::Frame(int64_t number, int width, int height, std::string color) // initialize the audio samples to zero (silence) audio->clear(); -}; +} // Constructor - audio only (300x200 blank image) Frame::Frame(int64_t number, int samples, int channels) : @@ -69,7 +69,7 @@ Frame::Frame(int64_t number, int samples, int channels) : // initialize the audio samples to zero (silence) audio->clear(); -}; +} // Constructor - image & audio Frame::Frame(int64_t number, int width, int height, std::string color, int samples, int channels) @@ -82,7 +82,7 @@ Frame::Frame(int64_t number, int width, int height, std::string color, int sampl // initialize the audio samples to zero (silence) audio->clear(); -}; +} // Copy constructor @@ -109,11 +109,12 @@ void Frame::DeepCopy(const Frame& other) width = other.width; height = other.height; channel_layout = other.channel_layout; - has_audio_data = other.has_image_data; + has_audio_data = other.has_audio_data; has_image_data = other.has_image_data; sample_rate = other.sample_rate; pixel_ratio = Fraction(other.pixel_ratio.num, other.pixel_ratio.den); color = other.color; + max_audio_sample = other.max_audio_sample; if (other.image) image = std::shared_ptr(new QImage(*(other.image))); @@ -580,7 +581,7 @@ void Frame::Save(std::string path, float scale, std::string format, int quality) std::shared_ptr previewImage = GetImage(); // scale image if needed - if (abs(scale) > 1.001 || abs(scale) < 0.999) + if (fabs(scale) > 1.001 || fabs(scale) < 0.999) { int new_width = width; int new_height = height; @@ -817,16 +818,23 @@ void Frame::AddImage(std::shared_ptr new_image, bool only_odd_lines) // Ignore image of different sizes or formats bool ret=false; #pragma omp critical (AddImage) - if (image == new_image || image->size() != image->size() || image->format() != image->format()) - ret=true; - if (ret) + { + if (image == new_image || image->size() != new_image->size()) { + ret = true; + } + else if (new_image->format() != image->format()) { + new_image = std::shared_ptr(new QImage(new_image->convertToFormat(image->format()))); + } + } + if (ret) { return; - + } + // Get the frame's image const GenericScopedLock lock(addingImageSection); #pragma omp critical (AddImage) { - const unsigned char *pixels = image->constBits(); + unsigned char *pixels = image->bits(); const unsigned char *new_pixels = new_image->constBits(); // Loop through the scanlines of the image (even or odd) @@ -835,13 +843,13 @@ void Frame::AddImage(std::shared_ptr new_image, bool only_odd_lines) start = 1; for (int row = start; row < image->height(); row += 2) { - memcpy((unsigned char *) pixels, new_pixels + (row * image->bytesPerLine()), image->bytesPerLine()); - new_pixels += image->bytesPerLine(); + int offset = row * image->bytesPerLine(); + memcpy(pixels + offset, new_pixels + offset, image->bytesPerLine()); } // Update height and width - width = image->width(); height = image->height(); + width = image->width(); has_image_data = true; } } diff --git a/src/FrameMapper.cpp b/src/FrameMapper.cpp index fe12a780b..7c4d04bb4 100644 --- a/src/FrameMapper.cpp +++ b/src/FrameMapper.cpp @@ -235,7 +235,7 @@ void FrameMapper::Init() int64_t start_samples_frame = 1; int start_samples_position = 0; - for (int64_t field = 1; field <= fields.size(); field++) + for (std::vector::size_type field = 1; field <= fields.size(); field++) { // Get the current field Field f = fields[field - 1]; @@ -337,7 +337,7 @@ MappedFrame FrameMapper::GetMappedFrame(int64_t TargetFrameNumber) // frame too small, return error throw OutOfBoundsFrame("An invalid frame was requested.", TargetFrameNumber, frames.size()); - else if (TargetFrameNumber > frames.size()) + else if (TargetFrameNumber > (int64_t)frames.size()) // frame too large, set to end frame TargetFrameNumber = frames.size(); @@ -675,14 +675,14 @@ void FrameMapper::Close() // Generate JSON string of this object -std::string FrameMapper::Json() { +std::string FrameMapper::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value FrameMapper::JsonValue() { +// Generate Json::Value for this object +Json::Value FrameMapper::JsonValue() const { // Create root json object Json::Value root = ReaderBase::JsonValue(); // get parent properties @@ -693,24 +693,12 @@ Json::Value FrameMapper::JsonValue() { } // Load JSON string into this object -void FrameMapper::SetJson(std::string value) { +void FrameMapper::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -721,8 +709,8 @@ void FrameMapper::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void FrameMapper::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void FrameMapper::SetJsonValue(const Json::Value root) { // Set parent data ReaderBase::SetJsonValue(root); diff --git a/src/ImageReader.cpp b/src/ImageReader.cpp index ad871f1f7..9ce3a70ff 100644 --- a/src/ImageReader.cpp +++ b/src/ImageReader.cpp @@ -136,14 +136,14 @@ std::shared_ptr ImageReader::GetFrame(int64_t requested_frame) } // Generate JSON string of this object -std::string ImageReader::Json() { +std::string ImageReader::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value ImageReader::JsonValue() { +// Generate Json::Value for this object +Json::Value ImageReader::JsonValue() const { // Create root json object Json::Value root = ReaderBase::JsonValue(); // get parent properties @@ -155,24 +155,12 @@ Json::Value ImageReader::JsonValue() { } // Load JSON string into this object -void ImageReader::SetJson(std::string value) { +void ImageReader::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -183,8 +171,8 @@ void ImageReader::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void ImageReader::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void ImageReader::SetJsonValue(const Json::Value root) { // Set parent data ReaderBase::SetJsonValue(root); diff --git a/src/Json.cpp b/src/Json.cpp new file mode 100644 index 000000000..0c83d9d62 --- /dev/null +++ b/src/Json.cpp @@ -0,0 +1,50 @@ +/** + * @file + * @brief Helper functions for Json parsing + * @author FeRD (Frank Dana) + * + * @ref License + */ + +/* LICENSE + * + * Copyright (c) 2008-2019 OpenShot Studios, LLC + * . This file is part of + * OpenShot Library (libopenshot), an open-source project dedicated to + * delivering high quality video editing and animation solutions to the + * world. For more information visit . + * + * OpenShot Library (libopenshot) is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * OpenShot Library (libopenshot) is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenShot Library. If not, see . + */ + +#include "../include/Json.h" + +const Json::Value openshot::stringToJson(const std::string value) { + + // Parse JSON string into JSON objects + Json::Value root; + Json::CharReaderBuilder rbuilder; + Json::CharReader* reader(rbuilder.newCharReader()); + + std::string errors; + bool success = reader->parse( value.c_str(), value.c_str() + value.size(), + &root, &errors ); + delete reader; + + if (!success) + // Raise exception + throw openshot::InvalidJSON("JSON could not be parsed (or is invalid)"); + + return root; +} diff --git a/src/KeyFrame.cpp b/src/KeyFrame.cpp index d16348a2e..57e424cf7 100644 --- a/src/KeyFrame.cpp +++ b/src/KeyFrame.cpp @@ -71,7 +71,7 @@ namespace { } double const x = p0.X * B[0] + p1.X * B[1] + p2.X * B[2] + p3.X * B[3]; double const y = p0.Y * B[0] + p1.Y * B[1] + p2.Y * B[2] + p3.Y * B[3]; - if (abs(target - x) < allowed_error) { + if (fabs(target - x) < allowed_error) { return y; } if (x > target) { @@ -169,7 +169,7 @@ void Keyframe::AddPoint(double x, double y, InterpolationType interpolate) // Get the index of a point by matching a coordinate int64_t Keyframe::FindIndex(Point p) const { // loop through points, and find a matching coordinate - for (int64_t x = 0; x < Points.size(); x++) { + for (std::vector::size_type x = 0; x < Points.size(); x++) { // Get each point Point existing_point = Points[x]; @@ -325,17 +325,15 @@ std::string Keyframe::Json() const { return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object +// Generate Json::Value for this object Json::Value Keyframe::JsonValue() const { // Create root json object Json::Value root; root["Points"] = Json::Value(Json::arrayValue); - // loop through points, and find a matching coordinate - for (int x = 0; x < Points.size(); x++) { - // Get each point - Point existing_point = Points[x]; + // loop through points + for (const auto existing_point : Points) { root["Points"].append(existing_point.JsonValue()); } @@ -344,24 +342,12 @@ Json::Value Keyframe::JsonValue() const { } // Load JSON string into this object -void Keyframe::SetJson(std::string value) { +void Keyframe::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -372,17 +358,14 @@ void Keyframe::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void Keyframe::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void Keyframe::SetJsonValue(const Json::Value root) { // Clear existing points Points.clear(); if (!root["Points"].isNull()) // loop through points - for (int64_t x = 0; x < root["Points"].size(); x++) { - // Get each point - Json::Value existing_point = root["Points"][(Json::UInt) x]; - + for (const auto existing_point : root["Points"]) { // Create Point Point p; @@ -509,7 +492,7 @@ double Keyframe::GetDelta(int64_t index) const { // Get a point at a specific index Point const & Keyframe::GetPoint(int64_t index) const { // Is index a valid point? - if (index >= 0 && index < Points.size()) + if (index >= 0 && index < (int64_t)Points.size()) return Points[index]; else // Invalid index @@ -532,7 +515,7 @@ int64_t Keyframe::GetCount() const { // Remove a point by matching a coordinate void Keyframe::RemovePoint(Point p) { // loop through points, and find a matching coordinate - for (int64_t x = 0; x < Points.size(); x++) { + for (std::vector::size_type x = 0; x < Points.size(); x++) { // Get each point Point existing_point = Points[x]; @@ -551,7 +534,7 @@ void Keyframe::RemovePoint(Point p) { // Remove a point by index void Keyframe::RemovePoint(int64_t index) { // Is index a valid point? - if (index >= 0 && index < Points.size()) + if (index >= 0 && index < (int64_t)Points.size()) { // Remove a specific point by index Points.erase(Points.begin() + index); @@ -581,7 +564,7 @@ void Keyframe::PrintValues() const { cout << fixed << setprecision(4); cout << "Frame Number (X)\tValue (Y)\tIs Increasing\tRepeat Numerator\tRepeat Denominator\tDelta (Y Difference)\n"; - for (uint64_t i = 1; i < GetLength(); ++i) { + for (int64_t i = 1; i < GetLength(); ++i) { cout << i << "\t" << GetValue(i) << "\t" << IsIncreasing(i) << "\t" ; cout << GetRepeatFraction(i).num << "\t" << GetRepeatFraction(i).den << "\t" << GetDelta(i) << "\n"; } @@ -597,7 +580,7 @@ void Keyframe::ScalePoints(double scale) // TODO: What if scale < 0? // Loop through each point (skipping the 1st point) - for (int64_t point_index = 1; point_index < Points.size(); point_index++) { + for (std::vector::size_type point_index = 1; point_index < Points.size(); point_index++) { // Scale X value Points[point_index].co.X = round(Points[point_index].co.X * scale); } @@ -605,7 +588,7 @@ void Keyframe::ScalePoints(double scale) // Flip all the points in this openshot::Keyframe (useful for reversing an effect or transition, etc...) void Keyframe::FlipPoints() { - for (int64_t point_index = 0, reverse_index = Points.size() - 1; point_index < reverse_index; point_index++, reverse_index--) { + for (std::vector::size_type point_index = 0, reverse_index = Points.size() - 1; point_index < reverse_index; point_index++, reverse_index--) { // Flip the points using std::swap; swap(Points[point_index].co.Y, Points[reverse_index].co.Y); diff --git a/src/Point.cpp b/src/Point.cpp index 52e7ffbe0..136799778 100644 --- a/src/Point.cpp +++ b/src/Point.cpp @@ -108,14 +108,14 @@ void Point::Initialize_RightHandle(float x, float y) { } // Generate JSON string of this object -std::string Point::Json() { +std::string Point::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value Point::JsonValue() { +// Generate Json::Value for this object +Json::Value Point::JsonValue() const { // Create root json object Json::Value root; @@ -132,24 +132,12 @@ Json::Value Point::JsonValue() { } // Load JSON string into this object -void Point::SetJson(std::string value) { +void Point::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -160,8 +148,8 @@ void Point::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void Point::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void Point::SetJsonValue(const Json::Value root) { if (!root["co"].isNull()) co.SetJsonValue(root["co"]); // update coordinate diff --git a/src/Profiles.cpp b/src/Profiles.cpp index e7c547717..5351520e9 100644 --- a/src/Profiles.cpp +++ b/src/Profiles.cpp @@ -133,14 +133,14 @@ Profile::Profile(std::string path) { } // Generate JSON string of this object -std::string Profile::Json() { +std::string Profile::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value Profile::JsonValue() { +// Generate Json::Value for this object +Json::Value Profile::JsonValue() const { // Create root json object Json::Value root; @@ -163,24 +163,12 @@ Json::Value Profile::JsonValue() { } // Load JSON string into this object -void Profile::SetJson(std::string value) { +void Profile::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -191,8 +179,8 @@ void Profile::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void Profile::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void Profile::SetJsonValue(const Json::Value root) { if (!root["height"].isNull()) info.height = root["height"].asInt(); diff --git a/src/Qt/VideoCacheThread.cpp b/src/Qt/VideoCacheThread.cpp index 46b6f030a..6cff46ba8 100644 --- a/src/Qt/VideoCacheThread.cpp +++ b/src/Qt/VideoCacheThread.cpp @@ -29,13 +29,14 @@ */ #include "../../include/Qt/VideoCacheThread.h" +#include namespace openshot { // Constructor VideoCacheThread::VideoCacheThread() : Thread("video-cache"), speed(1), is_playing(false), position(1) - , reader(NULL), max_frames(OPEN_MP_NUM_PROCESSORS * 2), current_display_frame(1) + , reader(NULL), max_frames(std::min(OPEN_MP_NUM_PROCESSORS * 8, 64)), current_display_frame(1) { } diff --git a/src/QtHtmlReader.cpp b/src/QtHtmlReader.cpp index cfdde9f39..6b502fbd4 100644 --- a/src/QtHtmlReader.cpp +++ b/src/QtHtmlReader.cpp @@ -180,14 +180,14 @@ std::shared_ptr QtHtmlReader::GetFrame(int64_t requested_frame) } // Generate JSON string of this object -std::string QtHtmlReader::Json() { +std::string QtHtmlReader::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value QtHtmlReader::JsonValue() { +// Generate Json::Value for this object +Json::Value QtHtmlReader::JsonValue() const { // Create root json object Json::Value root = ReaderBase::JsonValue(); // get parent properties @@ -206,24 +206,12 @@ Json::Value QtHtmlReader::JsonValue() { } // Load JSON string into this object -void QtHtmlReader::SetJson(std::string value) { +void QtHtmlReader::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -234,8 +222,8 @@ void QtHtmlReader::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void QtHtmlReader::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void QtHtmlReader::SetJsonValue(const Json::Value root) { // Set parent data ReaderBase::SetJsonValue(root); diff --git a/src/QtImageReader.cpp b/src/QtImageReader.cpp index 7163b0979..864af23db 100644 --- a/src/QtImageReader.cpp +++ b/src/QtImageReader.cpp @@ -71,7 +71,7 @@ void QtImageReader::Open() if (!is_open) { bool success = true; - image = std::shared_ptr(new QImage()); + bool loaded = false; #if USE_RESVG == 1 // If defined and found in CMake, utilize the libresvg for parsing @@ -80,38 +80,32 @@ void QtImageReader::Open() if (path.toLower().endsWith(".svg") || path.toLower().endsWith(".svgz")) { ResvgRenderer renderer(path); - if (!renderer.isValid()) { - // Attempt to open file (old method using Qt5 limited SVG parsing) - success = image->load(path); - if (success) { - image = std::shared_ptr(new QImage(image->convertToFormat(QImage::Format_RGBA8888))); - } - } else { - - image = std::shared_ptr(new QImage(renderer.defaultSize(), QImage::Format_RGBA8888)); + if (renderer.isValid()) { + + image = std::shared_ptr(new QImage(renderer.defaultSize(), QImage::Format_ARGB32_Premultiplied)); image->fill(Qt::transparent); QPainter p(image.get()); renderer.render(&p); p.end(); + loaded = true; } + } +#endif - } else { - // Attempt to open file (old method) + if (!loaded) { + // Attempt to open file using Qt's build in image processing capabilities + image = std::shared_ptr(new QImage()); success = image->load(path); - if (success) - image = std::shared_ptr(new QImage(image->convertToFormat(QImage::Format_RGBA8888))); } -#else - // Attempt to open file using Qt's build in image processing capabilities - success = image->load(path); - if (success) - image = std::shared_ptr(new QImage(image->convertToFormat(QImage::Format_RGBA8888))); -#endif - if (!success) + if (!success) { // raise exception throw InvalidFile("File could not be opened.", path.toStdString()); + } + + // Convert to proper format + image = std::shared_ptr(new QImage(image->convertToFormat(QImage::Format_RGBA8888))); // Update image properties info.has_audio = false; @@ -224,11 +218,14 @@ std::shared_ptr QtImageReader::GetFrame(int64_t requested_frame) // Scale image smaller (or use a previous scaled image) if (!cached_image || (max_size.width() != max_width || max_size.height() != max_height)) { + + bool rendered = false; #if USE_RESVG == 1 // If defined and found in CMake, utilize the libresvg for parsing // SVG files and rasterizing them to QImages. // Only use resvg for files ending in '.svg' or '.svgz' if (path.toLower().endsWith(".svg") || path.toLower().endsWith(".svgz")) { + ResvgRenderer renderer(path); if (renderer.isValid()) { // Scale SVG size to keep aspect ratio, and fill the max_size as best as possible @@ -236,30 +233,25 @@ std::shared_ptr QtImageReader::GetFrame(int64_t requested_frame) svg_size.scale(max_width, max_height, Qt::KeepAspectRatio); // Create empty QImage - cached_image = std::shared_ptr(new QImage(QSize(svg_size.width(), svg_size.height()), QImage::Format_RGBA8888)); + cached_image = std::shared_ptr(new QImage(QSize(svg_size.width(), svg_size.height()), QImage::Format_ARGB32_Premultiplied)); cached_image->fill(Qt::transparent); // Render SVG into QImage QPainter p(cached_image.get()); renderer.render(&p); p.end(); - } else { - // Resize current rasterized SVG (since we failed to parse original SVG file with resvg) - cached_image = std::shared_ptr(new QImage(image->scaled(max_width, max_height, Qt::KeepAspectRatio, Qt::SmoothTransformation))); - cached_image = std::shared_ptr(new QImage(cached_image->convertToFormat(QImage::Format_RGBA8888))); + rendered = true; } - } else { + } +#endif + + if (!rendered) { // We need to resize the original image to a smaller image (for performance reasons) // Only do this once, to prevent tons of unneeded scaling operations cached_image = std::shared_ptr(new QImage(image->scaled(max_width, max_height, Qt::KeepAspectRatio, Qt::SmoothTransformation))); - cached_image = std::shared_ptr(new QImage(cached_image->convertToFormat(QImage::Format_RGBA8888))); } -#else - // We need to resize the original image to a smaller image (for performance reasons) - // Only do this once, to prevent tons of unneeded scaling operations - cached_image = std::shared_ptr(new QImage(image->scaled(max_width, max_height, Qt::KeepAspectRatio, Qt::SmoothTransformation))); + cached_image = std::shared_ptr(new QImage(cached_image->convertToFormat(QImage::Format_RGBA8888))); -#endif // Set max size (to later determine if max_size is changed) max_size.setWidth(max_width); @@ -277,14 +269,14 @@ std::shared_ptr QtImageReader::GetFrame(int64_t requested_frame) } // Generate JSON string of this object -std::string QtImageReader::Json() { +std::string QtImageReader::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value QtImageReader::JsonValue() { +// Generate Json::Value for this object +Json::Value QtImageReader::JsonValue() const { // Create root json object Json::Value root = ReaderBase::JsonValue(); // get parent properties @@ -296,24 +288,12 @@ Json::Value QtImageReader::JsonValue() { } // Load JSON string into this object -void QtImageReader::SetJson(std::string value) { +void QtImageReader::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -324,8 +304,8 @@ void QtImageReader::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void QtImageReader::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void QtImageReader::SetJsonValue(const Json::Value root) { // Set parent data ReaderBase::SetJsonValue(root); diff --git a/src/QtTextReader.cpp b/src/QtTextReader.cpp index ee0c598a4..d91d164e6 100644 --- a/src/QtTextReader.cpp +++ b/src/QtTextReader.cpp @@ -197,14 +197,14 @@ std::shared_ptr QtTextReader::GetFrame(int64_t requested_frame) } // Generate JSON string of this object -std::string QtTextReader::Json() { +std::string QtTextReader::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value QtTextReader::JsonValue() { +// Generate Json::Value for this object +Json::Value QtTextReader::JsonValue() const { // Create root json object Json::Value root = ReaderBase::JsonValue(); // get parent properties @@ -225,24 +225,12 @@ Json::Value QtTextReader::JsonValue() { } // Load JSON string into this object -void QtTextReader::SetJson(std::string value) { +void QtTextReader::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -253,8 +241,8 @@ void QtTextReader::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void QtTextReader::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void QtTextReader::SetJsonValue(const Json::Value root) { // Set parent data ReaderBase::SetJsonValue(root); diff --git a/src/ReaderBase.cpp b/src/ReaderBase.cpp index ccd271f48..474dc624e 100644 --- a/src/ReaderBase.cpp +++ b/src/ReaderBase.cpp @@ -108,13 +108,12 @@ void ReaderBase::DisplayInfo() { std::cout << "----------------------------" << std::endl; // Iterate through metadata - std::map::iterator it; - for (it = info.metadata.begin(); it != info.metadata.end(); it++) - std::cout << "--> " << it->first << ": " << it->second << std::endl; + for (auto it : info.metadata) + std::cout << "--> " << it.first << ": " << it.second << std::endl; } -// Generate Json::JsonValue for this object -Json::Value ReaderBase::JsonValue() { +// Generate Json::Value for this object +Json::Value ReaderBase::JsonValue() const { // Create root json object Json::Value root; @@ -160,16 +159,16 @@ Json::Value ReaderBase::JsonValue() { // Append metadata map root["metadata"] = Json::Value(Json::objectValue); - std::map::iterator it; - for (it = info.metadata.begin(); it != info.metadata.end(); it++) - root["metadata"][it->first] = it->second; + + for (const auto it : info.metadata) + root["metadata"][it.first] = it.second; // return JsonValue return root; } -// Load Json::JsonValue into this object -void ReaderBase::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void ReaderBase::SetJsonValue(const Json::Value root) { // Set data from Json (if key is found) if (!root["has_video"].isNull()) @@ -244,7 +243,7 @@ void ReaderBase::SetJsonValue(Json::Value root) { info.audio_timebase.den = root["audio_timebase"]["den"].asInt(); } if (!root["metadata"].isNull() && root["metadata"].isObject()) { - for( Json::Value::iterator itr = root["metadata"].begin() ; itr != root["metadata"].end() ; itr++ ) { + for( Json::Value::const_iterator itr = root["metadata"].begin() ; itr != root["metadata"].end() ; itr++ ) { std::string key = itr.key().asString(); info.metadata[key] = root["metadata"][key].asString(); } diff --git a/src/TextReader.cpp b/src/TextReader.cpp index 1983b104f..e317700c9 100644 --- a/src/TextReader.cpp +++ b/src/TextReader.cpp @@ -187,14 +187,14 @@ std::shared_ptr TextReader::GetFrame(int64_t requested_frame) } // Generate JSON string of this object -std::string TextReader::Json() { +std::string TextReader::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value TextReader::JsonValue() { +// Generate Json::Value for this object +Json::Value TextReader::JsonValue() const { // Create root json object Json::Value root = ReaderBase::JsonValue(); // get parent properties @@ -216,24 +216,10 @@ Json::Value TextReader::JsonValue() { } // Load JSON string into this object -void TextReader::SetJson(std::string value) { - - // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - +void TextReader::SetJson(const std::string value) { try { + Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -244,8 +230,8 @@ void TextReader::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void TextReader::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void TextReader::SetJsonValue(const Json::Value root) { // Set parent data ReaderBase::SetJsonValue(root); diff --git a/src/Timeline.cpp b/src/Timeline.cpp index 86ef2911f..61ce31e33 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -34,7 +34,7 @@ using namespace openshot; // Default Constructor for the timeline (which sets the canvas width and height) Timeline::Timeline(int width, int height, Fraction fps, int sample_rate, int channels, ChannelLayout channel_layout) : - is_open(false), auto_map_clips(true), managed_cache(true) + is_open(false), auto_map_clips(true), managed_cache(true), path("") { // Create CrashHandler and Attach (incase of errors) CrashHandler::Instance(); @@ -64,6 +64,8 @@ Timeline::Timeline(int width, int height, Fraction fps, int sample_rate, int cha info.display_ratio = openshot::Fraction(width, height); info.display_ratio.Reduce(); info.pixel_ratio = openshot::Fraction(1, 1); + info.acodec = "openshot::timeline"; + info.vcodec = "openshot::timeline"; // Init max image size SetMaxSize(info.width, info.height); @@ -73,21 +75,149 @@ Timeline::Timeline(int width, int height, Fraction fps, int sample_rate, int cha final_cache->SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * 2, info.width, info.height, info.sample_rate, info.channels); } +// Constructor for the timeline (which loads a JSON structure from a file path, and initializes a timeline) +Timeline::Timeline(std::string projectPath, bool convert_absolute_paths) : + is_open(false), auto_map_clips(true), managed_cache(true), path(projectPath) { + + // Create CrashHandler and Attach (incase of errors) + CrashHandler::Instance(); + + // Init final cache as NULL (will be created after loading json) + final_cache = NULL; + + // Init viewport size (curve based, because it can be animated) + viewport_scale = Keyframe(100.0); + viewport_x = Keyframe(0.0); + viewport_y = Keyframe(0.0); + + // Init background color + color.red = Keyframe(0.0); + color.green = Keyframe(0.0); + color.blue = Keyframe(0.0); + + // Check if path exists + QFileInfo filePath(QString::fromStdString(path)); + if (!filePath.exists()) { + throw InvalidFile("File could not be opened.", path); + } + + // Check OpenShot Install Path exists + Settings *s = Settings::Instance(); + QDir openshotPath(QString::fromStdString(s->PATH_OPENSHOT_INSTALL)); + if (!openshotPath.exists()) { + throw InvalidFile("PATH_OPENSHOT_INSTALL could not be found.", s->PATH_OPENSHOT_INSTALL); + } + QDir openshotTransPath(openshotPath.filePath("transitions")); + if (!openshotTransPath.exists()) { + throw InvalidFile("PATH_OPENSHOT_INSTALL/transitions could not be found.", openshotTransPath.path().toStdString()); + } + + // Determine asset path + QString asset_name = filePath.baseName().left(30) + "_assets"; + QDir asset_folder(filePath.dir().filePath(asset_name)); + if (!asset_folder.exists()) { + // Create directory if needed + asset_folder.mkpath("."); + } + + // Load UTF-8 project file into QString + QFile projectFile(QString::fromStdString(path)); + projectFile.open(QFile::ReadOnly); + QString projectContents = QString::fromUtf8(projectFile.readAll()); + + // Convert all relative paths into absolute paths (if requested) + if (convert_absolute_paths) { + + // Find all "image" or "path" references in JSON (using regex). Must loop through match results + // due to our path matching needs, which are not possible with the QString::replace() function. + QRegularExpression allPathsRegex(QStringLiteral("\"(image|path)\":.*?\"(.*?)\"")); + std::vector matchedPositions; + QRegularExpressionMatchIterator i = allPathsRegex.globalMatch(projectContents); + while (i.hasNext()) { + QRegularExpressionMatch match = i.next(); + if (match.hasMatch()) { + // Push all match objects into a vector (so we can reverse them later) + matchedPositions.push_back(match); + } + } + + // Reverse the matches (bottom of file to top, so our replacements don't break our match positions) + std::vector::reverse_iterator itr; + for (itr = matchedPositions.rbegin(); itr != matchedPositions.rend(); itr++) { + QRegularExpressionMatch match = *itr; + QString relativeKey = match.captured(1); // image or path + QString relativePath = match.captured(2); // relative file path + QString absolutePath = ""; + + // Find absolute path of all path, image (including special replacements of @assets and @transitions) + if (relativePath.startsWith("@assets")) { + absolutePath = QFileInfo(asset_folder.absoluteFilePath(relativePath.replace("@assets", "."))).canonicalFilePath(); + } else if (relativePath.startsWith("@transitions")) { + absolutePath = QFileInfo(openshotTransPath.absoluteFilePath(relativePath.replace("@transitions", "."))).canonicalFilePath(); + } else { + absolutePath = QFileInfo(filePath.absoluteDir().absoluteFilePath(relativePath)).canonicalFilePath(); + } + + // Replace path in JSON content, if an absolute path was successfully found + if (!absolutePath.isEmpty()) { + projectContents.replace(match.capturedStart(0), match.capturedLength(0), "\"" + relativeKey + "\": \"" + absolutePath + "\""); + } + } + // Clear matches + matchedPositions.clear(); + } + + // Set JSON of project + SetJson(projectContents.toStdString()); + + // Calculate valid duration and set has_audio and has_video + // based on content inside this Timeline's clips. + float calculated_duration = 0.0; + for (auto clip : clips) + { + float clip_last_frame = clip->Position() + clip->Duration(); + if (clip_last_frame > calculated_duration) + calculated_duration = clip_last_frame; + if (clip->Reader() && clip->Reader()->info.has_audio) + info.has_audio = true; + if (clip->Reader() && clip->Reader()->info.has_video) + info.has_video = true; + + } + info.video_length = calculated_duration * info.fps.ToFloat(); + info.duration = calculated_duration; + + // Init FileInfo settings + info.acodec = "openshot::timeline"; + info.vcodec = "openshot::timeline"; + info.video_timebase = info.fps.Reciprocal(); + info.has_video = true; + info.has_audio = true; + + // Init max image size + SetMaxSize(info.width, info.height); + + // Init cache + final_cache = new CacheMemory(); + final_cache->SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * 2, info.width, info.height, info.sample_rate, info.channels); +} + Timeline::~Timeline() { if (is_open) // Auto Close if not already Close(); // Free all allocated frame mappers - std::set::iterator frame_mapper_itr; - for (frame_mapper_itr = allocated_frame_mappers.begin(); frame_mapper_itr != allocated_frame_mappers.end(); ++frame_mapper_itr) { - // Get frame mapper object from the iterator - FrameMapper *frame_mapper = (*frame_mapper_itr); - frame_mapper->Reader(NULL); - frame_mapper->Close(); - delete frame_mapper; + std::set::iterator it; + for (it = allocated_frame_mappers.begin(); it != allocated_frame_mappers.end(); ) { + // Dereference and clean up FrameMapper object + FrameMapper *mapper = (*it); + mapper->Reader(NULL); + mapper->Close(); + delete mapper; + // Remove reference and proceed to next element + it = allocated_frame_mappers.erase(it); } - allocated_frame_mappers.clear(); // Destroy previous cache (if managed by timeline) if (managed_cache && final_cache) { @@ -169,12 +299,8 @@ void Timeline::ApplyMapperToClips() ClearAllCache(); // Loop through all clips - std::list::iterator clip_itr; - for (clip_itr=clips.begin(); clip_itr != clips.end(); ++clip_itr) + for (auto clip : clips) { - // Get clip object from the iterator - Clip *clip = (*clip_itr); - // Apply framemapper (or update existing framemapper) apply_mapper_to_clip(clip); } @@ -197,12 +323,8 @@ std::shared_ptr Timeline::apply_effects(std::shared_ptr frame, int ZmqLogger::Instance()->AppendDebugMethod("Timeline::apply_effects", "frame->number", frame->number, "timeline_frame_number", timeline_frame_number, "layer", layer); // Find Effects at this position and layer - std::list::iterator effect_itr; - for (effect_itr=effects.begin(); effect_itr != effects.end(); ++effect_itr) + for (auto effect : effects) { - // Get effect object from the iterator - EffectBase *effect = (*effect_itr); - // Does clip intersect the current requested time long effect_start_position = round(effect->Position() * info.fps.ToDouble()) + 1; long effect_end_position = round((effect->Position() + (effect->Duration())) * info.fps.ToDouble()) + 1; @@ -379,8 +501,9 @@ void Timeline::add_layer(std::shared_ptr new_frame, Clip* source_clip, in } - // Skip out if only an audio frame - if (!source_clip->Waveform() && !source_clip->Reader()->info.has_video) + // Skip out if video was disabled or only an audio frame (no visualisation in use) + if (source_clip->has_video.GetInt(clip_frame_number) == 0 || + (!source_clip->Waveform() && !source_clip->Reader()->info.has_video)) // Skip the rest of the image processing for performance reasons return; @@ -466,34 +589,37 @@ void Timeline::add_layer(std::shared_ptr new_frame, Clip* source_clip, in float crop_h = source_clip->crop_height.GetValue(clip_frame_number); switch(source_clip->crop_gravity) { - case (GRAVITY_TOP): - crop_x += 0.5; - break; - case (GRAVITY_TOP_RIGHT): - crop_x += 1.0; - break; - case (GRAVITY_LEFT): - crop_y += 0.5; - break; - case (GRAVITY_CENTER): - crop_x += 0.5; - crop_y += 0.5; - break; - case (GRAVITY_RIGHT): - crop_x += 1.0; - crop_y += 0.5; - break; - case (GRAVITY_BOTTOM_LEFT): - crop_y += 1.0; - break; - case (GRAVITY_BOTTOM): - crop_x += 0.5; - crop_y += 1.0; - break; - case (GRAVITY_BOTTOM_RIGHT): - crop_x += 1.0; - crop_y += 1.0; - break; + case (GRAVITY_TOP_LEFT): + // This is only here to prevent unused-enum warnings + break; + case (GRAVITY_TOP): + crop_x += 0.5; + break; + case (GRAVITY_TOP_RIGHT): + crop_x += 1.0; + break; + case (GRAVITY_LEFT): + crop_y += 0.5; + break; + case (GRAVITY_CENTER): + crop_x += 0.5; + crop_y += 0.5; + break; + case (GRAVITY_RIGHT): + crop_x += 1.0; + crop_y += 0.5; + break; + case (GRAVITY_BOTTOM_LEFT): + crop_y += 1.0; + break; + case (GRAVITY_BOTTOM): + crop_x += 0.5; + crop_y += 1.0; + break; + case (GRAVITY_BOTTOM_RIGHT): + crop_x += 1.0; + crop_y += 1.0; + break; } @@ -509,6 +635,9 @@ void Timeline::add_layer(std::shared_ptr new_frame, Clip* source_clip, in switch (source_clip->gravity) { + case (GRAVITY_TOP_LEFT): + // This is only here to prevent unused-enum warnings + break; case (GRAVITY_TOP): x = (Settings::Instance()->MAX_WIDTH - scaled_source_width) / 2.0; // center break; @@ -611,6 +740,10 @@ void Timeline::add_layer(std::shared_ptr new_frame, Clip* source_clip, in std::stringstream frame_number_str; switch (source_clip->display) { + case (FRAME_DISPLAY_NONE): + // This is only here to prevent unused-enum warnings + break; + case (FRAME_DISPLAY_CLIP): frame_number_str << clip_frame_number; break; @@ -692,12 +825,8 @@ void Timeline::Close() ZmqLogger::Instance()->AppendDebugMethod("Timeline::Close"); // Close all open clips - std::list::iterator clip_itr; - for (clip_itr=clips.begin(); clip_itr != clips.end(); ++clip_itr) + for (auto clip : clips) { - // Get clip object from the iterator - Clip *clip = (*clip_itr); - // Open or Close this clip, based on if it's intersecting or not update_open_clips(clip, false); } @@ -706,7 +835,8 @@ void Timeline::Close() is_open = false; // Clear cache - final_cache->Clear(); + if (final_cache) + final_cache->Clear(); } // Open the reader (and start consuming resources) @@ -780,10 +910,8 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame) for (int64_t frame_number = requested_frame; frame_number < requested_frame + minimum_frames; frame_number++) { // Loop through clips - for (int clip_index = 0; clip_index < nearby_clips.size(); clip_index++) + for (auto clip : nearby_clips) { - // Get clip object from the iterator - Clip *clip = nearby_clips[clip_index]; long clip_start_position = round(clip->Position() * info.fps.ToDouble()) + 1; long clip_end_position = round((clip->Position() + clip->Duration()) * info.fps.ToDouble()) + 1; @@ -832,10 +960,8 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame) ZmqLogger::Instance()->AppendDebugMethod("Timeline::GetFrame (Loop through clips)", "frame_number", frame_number, "clips.size()", clips.size(), "nearby_clips.size()", nearby_clips.size()); // Find Clips near this time - for (int clip_index = 0; clip_index < nearby_clips.size(); clip_index++) + for (auto clip : nearby_clips) { - // Get clip object from the iterator - Clip *clip = nearby_clips[clip_index]; long clip_start_position = round(clip->Position() * info.fps.ToDouble()) + 1; long clip_end_position = round((clip->Position() + clip->Duration()) * info.fps.ToDouble()) + 1; @@ -850,9 +976,8 @@ std::shared_ptr Timeline::GetFrame(int64_t requested_frame) // Determine if clip is "top" clip on this layer (only happens when multiple clips are overlapping) bool is_top_clip = true; float max_volume = 0.0; - for (int top_clip_index = 0; top_clip_index < nearby_clips.size(); top_clip_index++) + for (auto nearby_clip : nearby_clips) { - Clip *nearby_clip = nearby_clips[top_clip_index]; long nearby_clip_start_position = round(nearby_clip->Position() * info.fps.ToDouble()) + 1; long nearby_clip_end_position = round((nearby_clip->Position() + nearby_clip->Duration()) * info.fps.ToDouble()) + 1; long nearby_clip_start_frame = (nearby_clip->Start() * info.fps.ToDouble()) + 1; @@ -927,12 +1052,8 @@ std::vector Timeline::find_intersecting_clips(int64_t requested_frame, in sort_clips(); // Find Clips at this time - std::list::iterator clip_itr; - for (clip_itr=clips.begin(); clip_itr != clips.end(); ++clip_itr) + for (auto clip : clips) { - // Get clip object from the iterator - Clip *clip = (*clip_itr); - // Does clip intersect the current requested time long clip_start_position = round(clip->Position() * info.fps.ToDouble()) + 1; long clip_end_position = round((clip->Position() + clip->Duration()) * info.fps.ToDouble()) + 1; @@ -977,14 +1098,14 @@ void Timeline::SetCache(CacheBase* new_cache) { } // Generate JSON string of this object -std::string Timeline::Json() { +std::string Timeline::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value Timeline::JsonValue() { +// Generate Json::Value for this object +Json::Value Timeline::JsonValue() const { // Create root json object Json::Value root = ReaderBase::JsonValue(); // get parent properties @@ -993,16 +1114,14 @@ Json::Value Timeline::JsonValue() { root["viewport_x"] = viewport_x.JsonValue(); root["viewport_y"] = viewport_y.JsonValue(); root["color"] = color.JsonValue(); + root["path"] = path; // Add array of clips root["clips"] = Json::Value(Json::arrayValue); // Find Clips at this time - std::list::iterator clip_itr; - for (clip_itr=clips.begin(); clip_itr != clips.end(); ++clip_itr) + for (const auto existing_clip : clips) { - // Get clip object from the iterator - Clip *existing_clip = (*clip_itr); root["clips"].append(existing_clip->JsonValue()); } @@ -1010,11 +1129,8 @@ Json::Value Timeline::JsonValue() { root["effects"] = Json::Value(Json::arrayValue); // loop through effects - std::list::iterator effect_itr; - for (effect_itr=effects.begin(); effect_itr != effects.end(); ++effect_itr) + for (const auto existing_effect: effects) { - // Get clip object from the iterator - EffectBase *existing_effect = (*effect_itr); root["effects"].append(existing_effect->JsonValue()); } @@ -1023,27 +1139,15 @@ Json::Value Timeline::JsonValue() { } // Load JSON string into this object -void Timeline::SetJson(std::string value) { +void Timeline::SetJson(const std::string value) { // Get lock (prevent getting frames while this happens) const GenericScopedLock lock(getFrameCriticalSection); // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -1054,8 +1158,8 @@ void Timeline::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void Timeline::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void Timeline::SetJsonValue(const Json::Value root) { // Close timeline before we do anything (this also removes all open and closing clips) bool was_open = is_open; @@ -1064,15 +1168,16 @@ void Timeline::SetJsonValue(Json::Value root) { // Set parent data ReaderBase::SetJsonValue(root); + // Set data from Json (if key is found) + if (!root["path"].isNull()) + path = root["path"].asString(); + if (!root["clips"].isNull()) { // Clear existing clips clips.clear(); // loop through clips - for (int x = 0; x < root["clips"].size(); x++) { - // Get each clip - Json::Value existing_clip = root["clips"][x]; - + for (const Json::Value existing_clip : root["clips"]) { // Create Clip Clip *c = new Clip(); @@ -1089,10 +1194,7 @@ void Timeline::SetJsonValue(Json::Value root) { effects.clear(); // loop through effects - for (int x = 0; x < root["effects"].size(); x++) { - // Get each effect - Json::Value existing_effect = root["effects"][x]; - + for (const Json::Value existing_effect :root["effects"]) { // Create Effect EffectBase *e = NULL; @@ -1128,33 +1230,19 @@ void Timeline::ApplyJsonDiff(std::string value) { const GenericScopedLock lock(getFrameCriticalSection); // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success || !root.isArray()) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)."); - try { + const Json::Value root = openshot::stringToJson(value); // Process the JSON change array, loop through each item - for (int x = 0; x < root.size(); x++) { - // Get each change - Json::Value change = root[x]; - std::string root_key = change["key"][(uint)0].asString(); + for (const Json::Value change : root) { + std::string change_key = change["key"][(uint)0].asString(); // Process each type of change - if (root_key == "clips") + if (change_key == "clips") // Apply to CLIPS apply_json_to_clips(change); - else if (root_key == "effects") + else if (change_key == "effects") // Apply to EFFECTS apply_json_to_effects(change); @@ -1180,10 +1268,8 @@ void Timeline::apply_json_to_clips(Json::Value change) { Clip *existing_clip = NULL; // Find id of clip (if any) - for (int x = 0; x < change["key"].size(); x++) { + for (auto key_part : change["key"]) { // Get each change - Json::Value key_part = change["key"][x]; - if (key_part.isObject()) { // Check for id if (!key_part["id"].isNull()) { @@ -1191,11 +1277,8 @@ void Timeline::apply_json_to_clips(Json::Value change) { clip_id = key_part["id"].asString(); // Find matching clip in timeline (if any) - std::list::iterator clip_itr; - for (clip_itr=clips.begin(); clip_itr != clips.end(); ++clip_itr) + for (auto c : clips) { - // Get clip object from the iterator - Clip *c = (*clip_itr); if (c->Id() == clip_id) { existing_clip = c; break; // clip found, exit loop @@ -1222,11 +1305,8 @@ void Timeline::apply_json_to_clips(Json::Value change) { // Find matching effect in timeline (if any) std::list effect_list = existing_clip->Effects(); - std::list::iterator effect_itr; - for (effect_itr=effect_list.begin(); effect_itr != effect_list.end(); ++effect_itr) + for (auto e : effect_list) { - // Get effect object from the iterator - EffectBase *e = (*effect_itr); if (e->Id() == effect_id) { // Apply the change to the effect directly apply_json_to_effects(change, e); @@ -1308,9 +1388,7 @@ void Timeline::apply_json_to_effects(Json::Value change) { EffectBase *existing_effect = NULL; // Find id of an effect (if any) - for (int x = 0; x < change["key"].size(); x++) { - // Get each change - Json::Value key_part = change["key"][x]; + for (auto key_part : change["key"]) { if (key_part.isObject()) { // Check for id @@ -1320,11 +1398,8 @@ void Timeline::apply_json_to_effects(Json::Value change) { std::string effect_id = key_part["id"].asString(); // Find matching effect in timeline (if any) - std::list::iterator effect_itr; - for (effect_itr=effects.begin(); effect_itr != effects.end(); ++effect_itr) + for (auto e : effects) { - // Get effect object from the iterator - EffectBase *e = (*effect_itr); if (e->Id() == effect_id) { existing_effect = e; break; // effect found, exit loop @@ -1533,12 +1608,8 @@ void Timeline::ClearAllCache() { final_cache->Clear(); // Loop through all clips - std::list::iterator clip_itr; - for (clip_itr=clips.begin(); clip_itr != clips.end(); ++clip_itr) + for (auto clip : clips) { - // Get clip object from the iterator - Clip *clip = (*clip_itr); - // Clear cache on clip clip->Reader()->GetCache()->Clear(); diff --git a/src/WriterBase.cpp b/src/WriterBase.cpp index 72b86b611..388219179 100644 --- a/src/WriterBase.cpp +++ b/src/WriterBase.cpp @@ -139,14 +139,14 @@ void WriterBase::DisplayInfo() { } // Generate JSON string of this object -std::string WriterBase::Json() { +std::string WriterBase::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value WriterBase::JsonValue() { +// Generate Json::Value for this object +Json::Value WriterBase::JsonValue() const { // Create root json object Json::Value root; @@ -195,24 +195,12 @@ Json::Value WriterBase::JsonValue() { } // Load JSON string into this object -void WriterBase::SetJson(std::string value) { +void WriterBase::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -223,8 +211,8 @@ void WriterBase::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void WriterBase::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void WriterBase::SetJsonValue(const Json::Value root) { // Set data from Json (if key is found) if (!root["has_video"].isNull()) diff --git a/src/effects/Bars.cpp b/src/effects/Bars.cpp index fcb684e3f..3f9aac344 100644 --- a/src/effects/Bars.cpp +++ b/src/effects/Bars.cpp @@ -114,14 +114,14 @@ std::shared_ptr Bars::GetFrame(std::shared_ptr frame, int64_t fram } // Generate JSON string of this object -std::string Bars::Json() { +std::string Bars::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value Bars::JsonValue() { +// Generate Json::Value for this object +Json::Value Bars::JsonValue() const { // Create root json object Json::Value root = EffectBase::JsonValue(); // get parent properties @@ -137,24 +137,12 @@ Json::Value Bars::JsonValue() { } // Load JSON string into this object -void Bars::SetJson(std::string value) { +void Bars::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -165,8 +153,8 @@ void Bars::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void Bars::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void Bars::SetJsonValue(const Json::Value root) { // Set parent data EffectBase::SetJsonValue(root); @@ -185,7 +173,7 @@ void Bars::SetJsonValue(Json::Value root) { } // Get all properties for a specific frame -std::string Bars::PropertiesJSON(int64_t requested_frame) { +std::string Bars::PropertiesJSON(int64_t requested_frame) const { // Generate JSON properties list Json::Value root; diff --git a/src/effects/Blur.cpp b/src/effects/Blur.cpp index e0315fda4..6ddba88e6 100644 --- a/src/effects/Blur.cpp +++ b/src/effects/Blur.cpp @@ -74,192 +74,109 @@ std::shared_ptr Blur::GetFrame(std::shared_ptr frame, int64_t fram float sigma_value = sigma.GetValue(frame_number); int iteration_value = iterations.GetInt(frame_number); + int w = frame_image->width(); + int h = frame_image->height(); - // Declare arrays for each color channel - unsigned char *red = new unsigned char[frame_image->width() * frame_image->height()](); - unsigned char *green = new unsigned char[frame_image->width() * frame_image->height()](); - unsigned char *blue = new unsigned char[frame_image->width() * frame_image->height()](); - unsigned char *alpha = new unsigned char[frame_image->width() * frame_image->height()](); - // Create empty target RGBA arrays (for the results of our blur) - unsigned char *blur_red = new unsigned char[frame_image->width() * frame_image->height()](); - unsigned char *blur_green = new unsigned char[frame_image->width() * frame_image->height()](); - unsigned char *blur_blue = new unsigned char[frame_image->width() * frame_image->height()](); - unsigned char *blur_alpha = new unsigned char[frame_image->width() * frame_image->height()](); - - // Loop through pixels and split RGBA channels into separate arrays - unsigned char *pixels = (unsigned char *) frame_image->bits(); - for (int pixel = 0, byte_index=0; pixel < frame_image->width() * frame_image->height(); pixel++, byte_index+=4) - { - // Get the RGBA values from each pixel - unsigned char R = pixels[byte_index]; - unsigned char G = pixels[byte_index + 1]; - unsigned char B = pixels[byte_index + 2]; - unsigned char A = pixels[byte_index + 3]; - - // Split channels into their own arrays - red[pixel] = R; - green[pixel] = G; - blue[pixel] = B; - alpha[pixel] = A; - } - - // Init target RGBA arrays - for (int i = 0; i < (frame_image->width() * frame_image->height()); i++) blur_red[i] = red[i]; - for (int i = 0; i < (frame_image->width() * frame_image->height()); i++) blur_green[i] = green[i]; - for (int i = 0; i < (frame_image->width() * frame_image->height()); i++) blur_blue[i] = blue[i]; - for (int i = 0; i < (frame_image->width() * frame_image->height()); i++) blur_alpha[i] = alpha[i]; + // Grab two copies of the image pixel data + QImage image_copy = frame_image->copy(); + std::shared_ptr frame_image_2 = std::make_shared(image_copy); // Loop through each iteration - for (int iteration = 0; iteration < iteration_value; iteration++) + for (int iteration = 0; iteration < iteration_value; ++iteration) { // HORIZONTAL BLUR (if any) if (horizontal_radius_value > 0.0) { - // Init boxes for computing blur - int *bxs = initBoxes(sigma_value, horizontal_radius_value); - // Apply horizontal blur to target RGBA channels - boxBlurH(red, blur_red, frame_image->width(), frame_image->height(), horizontal_radius_value); - boxBlurH(green, blur_green, frame_image->width(), frame_image->height(), horizontal_radius_value); - boxBlurH(blue, blur_blue, frame_image->width(), frame_image->height(), horizontal_radius_value); - boxBlurH(alpha, blur_alpha, frame_image->width(), frame_image->height(), horizontal_radius_value); - - // Remove boxes - delete[] bxs; - - // Copy blur_ back to for vertical blur or next iteration - for (int i = 0; i < (frame_image->width() * frame_image->height()); i++) red[i] = blur_red[i]; - for (int i = 0; i < (frame_image->width() * frame_image->height()); i++) green[i] = blur_green[i]; - for (int i = 0; i < (frame_image->width() * frame_image->height()); i++) blue[i] = blur_blue[i]; - for (int i = 0; i < (frame_image->width() * frame_image->height()); i++) alpha[i] = blur_alpha[i]; + boxBlurH(frame_image->bits(), frame_image_2->bits(), w, h, horizontal_radius_value); + + // Swap output image back to input + frame_image.swap(frame_image_2); } // VERTICAL BLUR (if any) if (vertical_radius_value > 0.0) { - // Init boxes for computing blur - int *bxs = initBoxes(sigma_value, vertical_radius_value); - // Apply vertical blur to target RGBA channels - boxBlurT(red, blur_red, frame_image->width(), frame_image->height(), vertical_radius_value); - boxBlurT(green, blur_green, frame_image->width(), frame_image->height(), vertical_radius_value); - boxBlurT(blue, blur_blue, frame_image->width(), frame_image->height(), vertical_radius_value); - boxBlurT(alpha, blur_alpha, frame_image->width(), frame_image->height(), vertical_radius_value); - - // Remove boxes - delete[] bxs; - - // Copy blur_ back to for vertical blur or next iteration - for (int i = 0; i < (frame_image->width() * frame_image->height()); i++) red[i] = blur_red[i]; - for (int i = 0; i < (frame_image->width() * frame_image->height()); i++) green[i] = blur_green[i]; - for (int i = 0; i < (frame_image->width() * frame_image->height()); i++) blue[i] = blur_blue[i]; - for (int i = 0; i < (frame_image->width() * frame_image->height()); i++) alpha[i] = blur_alpha[i]; - } - } + boxBlurT(frame_image->bits(), frame_image_2->bits(), w, h, vertical_radius_value); - // Copy RGBA channels back to original image - for (int pixel = 0, byte_index=0; pixel < frame_image->width() * frame_image->height(); pixel++, byte_index+=4) - { - // Get the RGB values from the pixel - unsigned char R = blur_red[pixel]; - unsigned char G = blur_green[pixel]; - unsigned char B = blur_blue[pixel]; - unsigned char A = blur_alpha[pixel]; - - // Split channels into their own arrays - pixels[byte_index] = R; - pixels[byte_index + 1] = G; - pixels[byte_index + 2] = B; - pixels[byte_index + 3] = A; + // Swap output image back to input + frame_image.swap(frame_image_2); + } } - // Delete channel arrays - delete[] red; - delete[] green; - delete[] blue; - delete[] alpha; - delete[] blur_red; - delete[] blur_green; - delete[] blur_blue; - delete[] blur_alpha; - // return the modified frame return frame; } // Credit: http://blog.ivank.net/fastest-gaussian-blur.html (MIT License) -int* Blur::initBoxes(float sigma, int n) // standard deviation, number of boxes -{ - float wIdeal = sqrt((12.0 * sigma * sigma / n) + 1.0); // Ideal averaging filter width - int wl = floor(wIdeal); - if (wl % 2 == 0) wl--; - int wu = wl + 2; - - float mIdeal = (12.0 * sigma * sigma - n * wl * wl - 4 * n * wl - 3 * n) / (-4.0 * wl - 4); - int m = round(mIdeal); - - int *sizes = new int[n](); - for (int i = 0; i < n; i++) sizes[i] = i < m ? wl : wu; - return sizes; -} - -// Credit: http://blog.ivank.net/fastest-gaussian-blur.html (MIT License) +// Modified to process all four channels in a pixel array void Blur::boxBlurH(unsigned char *scl, unsigned char *tcl, int w, int h, int r) { float iarr = 1.0 / (r + r + 1); - for (int i = 0; i < h; i++) { - int ti = i * w, li = ti, ri = ti + r; - int fv = scl[ti], lv = scl[ti + w - 1], val = (r + 1) * fv; - for (int j = 0; j < r; j++) val += scl[ti + j]; - for (int j = 0; j <= r; j++) { - val += scl[ri++] - fv; - tcl[ti++] = round(val * iarr); - } - for (int j = r + 1; j < w - r; j++) { - val += scl[ri++] - scl[li++]; - tcl[ti++] = round(val * iarr); - } - for (int j = w - r; j < w; j++) { - val += lv - scl[li++]; - tcl[ti++] = round(val * iarr); + + #pragma omp parallel for shared (scl, tcl) + for (int i = 0; i < h; ++i) { + for (int ch = 0; ch < 4; ++ch) { + int ti = i * w, li = ti, ri = ti + r; + int fv = scl[ti * 4 + ch], lv = scl[(ti + w - 1) * 4 + ch], val = (r + 1) * fv; + for (int j = 0; j < r; ++j) { + val += scl[(ti + j) * 4 + ch]; + } + for (int j = 0; j <= r; ++j) { + val += scl[ri++ * 4 + ch] - fv; + tcl[ti++ * 4 + ch] = round(val * iarr); + } + for (int j = r + 1; j < w - r; ++j) { + val += scl[ri++ * 4 + ch] - scl[li++ * 4 + ch]; + tcl[ti++ * 4 + ch] = round(val * iarr); + } + for (int j = w - r; j < w; ++j) { + val += lv - scl[li++ * 4 + ch]; + tcl[ti++ * 4 + ch] = round(val * iarr); + } } } } void Blur::boxBlurT(unsigned char *scl, unsigned char *tcl, int w, int h, int r) { float iarr = 1.0 / (r + r + 1); + + #pragma omp parallel for shared (scl, tcl) for (int i = 0; i < w; i++) { - int ti = i, li = ti, ri = ti + r * w; - int fv = scl[ti], lv = scl[ti + w * (h - 1)], val = (r + 1) * fv; - for (int j = 0; j < r; j++) val += scl[ti + j * w]; - for (int j = 0; j <= r; j++) { - val += scl[ri] - fv; - tcl[ti] = round(val * iarr); - ri += w; - ti += w; - } - for (int j = r + 1; j < h - r; j++) { - val += scl[ri] - scl[li]; - tcl[ti] = round(val * iarr); - li += w; - ri += w; - ti += w; - } - for (int j = h - r; j < h; j++) { - val += lv - scl[li]; - tcl[ti] = round(val * iarr); - li += w; - ti += w; + for (int ch = 0; ch < 4; ++ch) { + int ti = i, li = ti, ri = ti + r * w; + int fv = scl[ti * 4 + ch], lv = scl[(ti + w * (h - 1)) * 4 + ch], val = (r + 1) * fv; + for (int j = 0; j < r; j++) val += scl[(ti + j * w) * 4 + ch]; + for (int j = 0; j <= r; j++) { + val += scl[ri * 4 + ch] - fv; + tcl[ti * 4 + ch] = round(val * iarr); + ri += w; + ti += w; + } + for (int j = r + 1; j < h - r; j++) { + val += scl[ri * 4 + ch] - scl[li * 4 + ch]; + tcl[ti * 4 + ch] = round(val * iarr); + li += w; + ri += w; + ti += w; + } + for (int j = h - r; j < h; j++) { + val += lv - scl[li * 4 + ch]; + tcl[ti * 4 + ch] = round(val * iarr); + li += w; + ti += w; + } } } } // Generate JSON string of this object -std::string Blur::Json() { +std::string Blur::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value Blur::JsonValue() { +// Generate Json::Value for this object +Json::Value Blur::JsonValue() const { // Create root json object Json::Value root = EffectBase::JsonValue(); // get parent properties @@ -274,24 +191,12 @@ Json::Value Blur::JsonValue() { } // Load JSON string into this object -void Blur::SetJson(std::string value) { +void Blur::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -302,8 +207,8 @@ void Blur::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void Blur::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void Blur::SetJsonValue(const Json::Value root) { // Set parent data EffectBase::SetJsonValue(root); @@ -320,7 +225,7 @@ void Blur::SetJsonValue(Json::Value root) { } // Get all properties for a specific frame -std::string Blur::PropertiesJSON(int64_t requested_frame) { +std::string Blur::PropertiesJSON(int64_t requested_frame) const { // Generate JSON properties list Json::Value root; diff --git a/src/effects/Brightness.cpp b/src/effects/Brightness.cpp index b8113b876..321a19a78 100644 --- a/src/effects/Brightness.cpp +++ b/src/effects/Brightness.cpp @@ -72,35 +72,24 @@ std::shared_ptr Brightness::GetFrame(std::shared_ptr frame, int64_ // Loop through pixels unsigned char *pixels = (unsigned char *) frame_image->bits(); - for (int pixel = 0, byte_index=0; pixel < frame_image->width() * frame_image->height(); pixel++, byte_index+=4) - { - // Get the RGB values from the pixel - int R = pixels[byte_index]; - int G = pixels[byte_index + 1]; - int B = pixels[byte_index + 2]; - int A = pixels[byte_index + 3]; + int pixel_count = frame_image->width() * frame_image->height(); - // Adjust the contrast + #pragma omp parallel for + for (int pixel = 0; pixel < pixel_count; ++pixel) + { + // Compute contrast adjustment factor float factor = (259 * (contrast_value + 255)) / (255 * (259 - contrast_value)); - R = constrain((factor * (R - 128)) + 128); - G = constrain((factor * (G - 128)) + 128); - B = constrain((factor * (B - 128)) + 128); - - // Adjust the brightness - R += (255 * brightness_value); - G += (255 * brightness_value); - B += (255 * brightness_value); - - // Constrain the value from 0 to 255 - R = constrain(R); - G = constrain(G); - B = constrain(B); - - // Set all pixels to new value - pixels[byte_index] = R; - pixels[byte_index + 1] = G; - pixels[byte_index + 2] = B; - pixels[byte_index + 3] = A; // leave the alpha value alone + + // Get RGB pixels from image and apply constrained contrast adjustment + int R = constrain((factor * (pixels[pixel * 4] - 128)) + 128); + int G = constrain((factor * (pixels[pixel * 4 + 1] - 128)) + 128); + int B = constrain((factor * (pixels[pixel * 4 + 2] - 128)) + 128); + // (Don't modify Alpha value) + + // Adjust brightness and write constrained values back to image + pixels[pixel * 4] = constrain(R + (255 * brightness_value)); + pixels[pixel * 4 + 1] = constrain(G + (255 * brightness_value)); + pixels[pixel * 4 + 2] = constrain(B + (255 * brightness_value)); } // return the modified frame @@ -108,14 +97,14 @@ std::shared_ptr Brightness::GetFrame(std::shared_ptr frame, int64_ } // Generate JSON string of this object -std::string Brightness::Json() { +std::string Brightness::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value Brightness::JsonValue() { +// Generate Json::Value for this object +Json::Value Brightness::JsonValue() const { // Create root json object Json::Value root = EffectBase::JsonValue(); // get parent properties @@ -128,24 +117,12 @@ Json::Value Brightness::JsonValue() { } // Load JSON string into this object -void Brightness::SetJson(std::string value) { +void Brightness::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -156,8 +133,8 @@ void Brightness::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void Brightness::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void Brightness::SetJsonValue(const Json::Value root) { // Set parent data EffectBase::SetJsonValue(root); @@ -170,7 +147,7 @@ void Brightness::SetJsonValue(Json::Value root) { } // Get all properties for a specific frame -std::string Brightness::PropertiesJSON(int64_t requested_frame) { +std::string Brightness::PropertiesJSON(int64_t requested_frame) const { // Generate JSON properties list Json::Value root; diff --git a/src/effects/ChromaKey.cpp b/src/effects/ChromaKey.cpp index 30c4dfce8..dbb56a693 100644 --- a/src/effects/ChromaKey.cpp +++ b/src/effects/ChromaKey.cpp @@ -101,14 +101,14 @@ std::shared_ptr ChromaKey::GetFrame(std::shared_ptr frame, int64_t } // Generate JSON string of this object -std::string ChromaKey::Json() { +std::string ChromaKey::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value ChromaKey::JsonValue() { +// Generate Json::Value for this object +Json::Value ChromaKey::JsonValue() const { // Create root json object Json::Value root = EffectBase::JsonValue(); // get parent properties @@ -121,24 +121,12 @@ Json::Value ChromaKey::JsonValue() { } // Load JSON string into this object -void ChromaKey::SetJson(std::string value) { +void ChromaKey::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -149,8 +137,8 @@ void ChromaKey::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void ChromaKey::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void ChromaKey::SetJsonValue(const Json::Value root) { // Set parent data EffectBase::SetJsonValue(root); @@ -163,7 +151,7 @@ void ChromaKey::SetJsonValue(Json::Value root) { } // Get all properties for a specific frame -std::string ChromaKey::PropertiesJSON(int64_t requested_frame) { +std::string ChromaKey::PropertiesJSON(int64_t requested_frame) const { // Generate JSON properties list Json::Value root; diff --git a/src/effects/ColorShift.cpp b/src/effects/ColorShift.cpp index ea8ae136e..7ec62e14c 100644 --- a/src/effects/ColorShift.cpp +++ b/src/effects/ColorShift.cpp @@ -33,7 +33,7 @@ using namespace openshot; /// Blank constructor, useful when using Json to load the effect properties -ColorShift::ColorShift() : red_x(-0.05), red_y(0.0), green_x(0.05), green_y(0.0), blue_x(0.0), blue_y(0.0), alpha_x(0.0), alpha_y(0.0) { +ColorShift::ColorShift() : red_x(0.0), red_y(0.0), green_x(0.0), green_y(0.0), blue_x(0.0), blue_y(0.0), alpha_x(0.0), alpha_y(0.0) { // Init effect properties init_effect_details(); } @@ -53,7 +53,7 @@ void ColorShift::init_effect_details() InitEffectInfo(); /// Set the effect info - info.class_name = "Color Shift"; + info.class_name = "ColorShift"; info.name = "Color Shift"; info.description = "Shift the colors of an image up, down, left, and right (with infinite wrapping)."; info.has_audio = false; @@ -75,25 +75,24 @@ std::shared_ptr ColorShift::GetFrame(std::shared_ptr frame, int64_ // Get the current shift amount, and clamp to range (-1 to 1 range) // Red Keyframes float red_x_shift = red_x.GetValue(frame_number); - int red_x_shift_limit = round(frame_image_width * fmod(abs(red_x_shift), 1.0)); + int red_x_shift_limit = round(frame_image_width * fmod(fabs(red_x_shift), 1.0)); float red_y_shift = red_y.GetValue(frame_number); - int red_y_shift_limit = round(frame_image_height * fmod(abs(red_y_shift), 1.0)); + int red_y_shift_limit = round(frame_image_height * fmod(fabs(red_y_shift), 1.0)); // Green Keyframes float green_x_shift = green_x.GetValue(frame_number); - int green_x_shift_limit = round(frame_image_width * fmod(abs(green_x_shift), 1.0)); + int green_x_shift_limit = round(frame_image_width * fmod(fabs(green_x_shift), 1.0)); float green_y_shift = green_y.GetValue(frame_number); - int green_y_shift_limit = round(frame_image_height * fmod(abs(green_y_shift), 1.0)); + int green_y_shift_limit = round(frame_image_height * fmod(fabs(green_y_shift), 1.0)); // Blue Keyframes float blue_x_shift = blue_x.GetValue(frame_number); - int blue_x_shift_limit = round(frame_image_width * fmod(abs(blue_x_shift), 1.0)); + int blue_x_shift_limit = round(frame_image_width * fmod(fabs(blue_x_shift), 1.0)); float blue_y_shift = blue_y.GetValue(frame_number); - int blue_y_shift_limit = round(frame_image_height * fmod(abs(blue_y_shift), 1.0)); + int blue_y_shift_limit = round(frame_image_height * fmod(fabs(blue_y_shift), 1.0)); // Alpha Keyframes float alpha_x_shift = alpha_x.GetValue(frame_number); - int alpha_x_shift_limit = round(frame_image_width * fmod(abs(alpha_x_shift), 1.0)); + int alpha_x_shift_limit = round(frame_image_width * fmod(fabs(alpha_x_shift), 1.0)); float alpha_y_shift = alpha_y.GetValue(frame_number); - int alpha_y_shift_limit = round(frame_image_height * fmod(abs(alpha_y_shift), 1.0)); - + int alpha_y_shift_limit = round(frame_image_height * fmod(fabs(alpha_y_shift), 1.0)); // Make temp copy of pixels unsigned char *temp_image = new unsigned char[frame_image_width * frame_image_height * 4](); @@ -130,7 +129,6 @@ std::shared_ptr ColorShift::GetFrame(std::shared_ptr frame, int64_ blue_starting_row_index = starting_row_index; alpha_starting_row_index = starting_row_index; - red_pixel_offset = 0; green_pixel_offset = 0; blue_pixel_offset = 0; @@ -194,14 +192,14 @@ std::shared_ptr ColorShift::GetFrame(std::shared_ptr frame, int64_ } // Generate JSON string of this object -std::string ColorShift::Json() { +std::string ColorShift::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value ColorShift::JsonValue() { +// Generate Json::Value for this object +Json::Value ColorShift::JsonValue() const { // Create root json object Json::Value root = EffectBase::JsonValue(); // get parent properties @@ -220,24 +218,12 @@ Json::Value ColorShift::JsonValue() { } // Load JSON string into this object -void ColorShift::SetJson(std::string value) { +void ColorShift::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -248,8 +234,8 @@ void ColorShift::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void ColorShift::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void ColorShift::SetJsonValue(const Json::Value root) { // Set parent data EffectBase::SetJsonValue(root); @@ -274,7 +260,7 @@ void ColorShift::SetJsonValue(Json::Value root) { } // Get all properties for a specific frame -std::string ColorShift::PropertiesJSON(int64_t requested_frame) { +std::string ColorShift::PropertiesJSON(int64_t requested_frame) const { // Generate JSON properties list Json::Value root; diff --git a/src/effects/Crop.cpp b/src/effects/Crop.cpp index 8a4afa5e1..b1c3d38d9 100644 --- a/src/effects/Crop.cpp +++ b/src/effects/Crop.cpp @@ -114,14 +114,14 @@ std::shared_ptr Crop::GetFrame(std::shared_ptr frame, int64_t fram } // Generate JSON string of this object -std::string Crop::Json() { +std::string Crop::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value Crop::JsonValue() { +// Generate Json::Value for this object +Json::Value Crop::JsonValue() const { // Create root json object Json::Value root = EffectBase::JsonValue(); // get parent properties @@ -136,24 +136,12 @@ Json::Value Crop::JsonValue() { } // Load JSON string into this object -void Crop::SetJson(std::string value) { +void Crop::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -164,8 +152,8 @@ void Crop::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void Crop::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void Crop::SetJsonValue(const Json::Value root) { // Set parent data EffectBase::SetJsonValue(root); @@ -182,7 +170,7 @@ void Crop::SetJsonValue(Json::Value root) { } // Get all properties for a specific frame -std::string Crop::PropertiesJSON(int64_t requested_frame) { +std::string Crop::PropertiesJSON(int64_t requested_frame) const { // Generate JSON properties list Json::Value root; diff --git a/src/effects/Deinterlace.cpp b/src/effects/Deinterlace.cpp index a78af9314..39b3316a5 100644 --- a/src/effects/Deinterlace.cpp +++ b/src/effects/Deinterlace.cpp @@ -96,14 +96,14 @@ std::shared_ptr Deinterlace::GetFrame(std::shared_ptr frame, int64 } // Generate JSON string of this object -std::string Deinterlace::Json() { +std::string Deinterlace::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value Deinterlace::JsonValue() { +// Generate Json::Value for this object +Json::Value Deinterlace::JsonValue() const { // Create root json object Json::Value root = EffectBase::JsonValue(); // get parent properties @@ -115,24 +115,12 @@ Json::Value Deinterlace::JsonValue() { } // Load JSON string into this object -void Deinterlace::SetJson(std::string value) { +void Deinterlace::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -143,8 +131,8 @@ void Deinterlace::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void Deinterlace::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void Deinterlace::SetJsonValue(const Json::Value root) { // Set parent data EffectBase::SetJsonValue(root); @@ -155,7 +143,7 @@ void Deinterlace::SetJsonValue(Json::Value root) { } // Get all properties for a specific frame -std::string Deinterlace::PropertiesJSON(int64_t requested_frame) { +std::string Deinterlace::PropertiesJSON(int64_t requested_frame) const { // Generate JSON properties list Json::Value root; diff --git a/src/effects/Hue.cpp b/src/effects/Hue.cpp index 4083d0356..eb4808e31 100644 --- a/src/effects/Hue.cpp +++ b/src/effects/Hue.cpp @@ -66,36 +66,35 @@ std::shared_ptr Hue::GetFrame(std::shared_ptr frame, int64_t frame // Get the frame's image std::shared_ptr frame_image = frame->GetImage(); + int pixel_count = frame_image->width() * frame_image->height(); + // Get the current hue percentage shift amount, and convert to degrees double degrees = 360.0 * hue.GetValue(frame_number); float cosA = cos(degrees*3.14159265f/180); float sinA = sin(degrees*3.14159265f/180); // Calculate a rotation matrix for the RGB colorspace (based on the current hue shift keyframe value) - float matrix[3][3] = {{cosA + (1.0f - cosA) / 3.0f, 1.0f/3.0f * (1.0f - cosA) - sqrtf(1.0f/3.0f) * sinA, 1.0f/3.0f * (1.0f - cosA) + sqrtf(1.0f/3.0f) * sinA}, - {1.0f/3.0f * (1.0f - cosA) + sqrtf(1.0f/3.0f) * sinA, cosA + 1.0f/3.0f*(1.0f - cosA), 1.0f/3.0f * (1.0f - cosA) - sqrtf(1.0f/3.0f) * sinA}, - {1.0f/3.0f * (1.0f - cosA) - sqrtf(1.0f/3.0f) * sinA, 1.0f/3.0f * (1.0f - cosA) + sqrtf(1.0f/3.0f) * sinA, cosA + 1.0f/3.0f * (1.0f - cosA)}}; + float matrix[3] = { + cosA + (1.0f - cosA) / 3.0f, + 1.0f/3.0f * (1.0f - cosA) - sqrtf(1.0f/3.0f) * sinA, + 1.0f/3.0f * (1.0f - cosA) + sqrtf(1.0f/3.0f) * sinA + }; // Loop through pixels unsigned char *pixels = (unsigned char *) frame_image->bits(); - for (int pixel = 0, byte_index=0; pixel < frame_image->width() * frame_image->height(); pixel++, byte_index+=4) + + #pragma omp parallel for shared (pixels) + for (int pixel = 0; pixel < pixel_count; ++pixel) { - // Get the RGB values from the pixel - int R = pixels[byte_index]; - int G = pixels[byte_index + 1]; - int B = pixels[byte_index + 2]; - int A = pixels[byte_index + 3]; + // Get the RGB values from the pixel (ignore the alpha channel) + int R = pixels[pixel * 4]; + int G = pixels[pixel * 4 + 1]; + int B = pixels[pixel * 4 + 2]; // Multiply each color by the hue rotation matrix - float rx = constrain(R * matrix[0][0] + G * matrix[0][1] + B * matrix[0][2]); - float gx = constrain(R * matrix[1][0] + G * matrix[1][1] + B * matrix[1][2]); - float bx = constrain(R * matrix[2][0] + G * matrix[2][1] + B * matrix[2][2]); - - // Set all pixels to new value - pixels[byte_index] = rx; - pixels[byte_index + 1] = gx; - pixels[byte_index + 2] = bx; - pixels[byte_index + 3] = A; // leave the alpha value alone + pixels[pixel * 4] = constrain(R * matrix[0] + G * matrix[1] + B * matrix[2]); + pixels[pixel * 4 + 1] = constrain(R * matrix[2] + G * matrix[0] + B * matrix[1]); + pixels[pixel * 4 + 2] = constrain(R * matrix[1] + G * matrix[2] + B * matrix[0]); } // return the modified frame @@ -103,14 +102,14 @@ std::shared_ptr Hue::GetFrame(std::shared_ptr frame, int64_t frame } // Generate JSON string of this object -std::string Hue::Json() { +std::string Hue::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value Hue::JsonValue() { +// Generate Json::Value for this object +Json::Value Hue::JsonValue() const { // Create root json object Json::Value root = EffectBase::JsonValue(); // get parent properties @@ -122,24 +121,12 @@ Json::Value Hue::JsonValue() { } // Load JSON string into this object -void Hue::SetJson(std::string value) { +void Hue::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -150,8 +137,8 @@ void Hue::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void Hue::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void Hue::SetJsonValue(const Json::Value root) { // Set parent data EffectBase::SetJsonValue(root); @@ -162,7 +149,7 @@ void Hue::SetJsonValue(Json::Value root) { } // Get all properties for a specific frame -std::string Hue::PropertiesJSON(int64_t requested_frame) { +std::string Hue::PropertiesJSON(int64_t requested_frame) const { // Generate JSON properties list Json::Value root; diff --git a/src/effects/Mask.cpp b/src/effects/Mask.cpp index b804c2148..11c37f053 100644 --- a/src/effects/Mask.cpp +++ b/src/effects/Mask.cpp @@ -150,14 +150,14 @@ std::shared_ptr Mask::GetFrame(std::shared_ptr frame, int64_t fram } // Generate JSON string of this object -std::string Mask::Json() { +std::string Mask::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value Mask::JsonValue() { +// Generate Json::Value for this object +Json::Value Mask::JsonValue() const { // Create root json object Json::Value root = EffectBase::JsonValue(); // get parent properties @@ -175,24 +175,12 @@ Json::Value Mask::JsonValue() { } // Load JSON string into this object -void Mask::SetJson(std::string value) { +void Mask::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -203,8 +191,8 @@ void Mask::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void Mask::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void Mask::SetJsonValue(const Json::Value root) { // Set parent data EffectBase::SetJsonValue(root); @@ -271,7 +259,7 @@ void Mask::SetJsonValue(Json::Value root) { } // Get all properties for a specific frame -std::string Mask::PropertiesJSON(int64_t requested_frame) { +std::string Mask::PropertiesJSON(int64_t requested_frame) const { // Generate JSON properties list Json::Value root; diff --git a/src/effects/Negate.cpp b/src/effects/Negate.cpp index cce594984..18a5d194c 100644 --- a/src/effects/Negate.cpp +++ b/src/effects/Negate.cpp @@ -58,14 +58,14 @@ std::shared_ptr Negate::GetFrame(std::shared_ptr frame, int64_t fr } // Generate JSON string of this object -std::string Negate::Json() { +std::string Negate::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value Negate::JsonValue() { +// Generate Json::Value for this object +Json::Value Negate::JsonValue() const { // Create root json object Json::Value root = EffectBase::JsonValue(); // get parent properties @@ -76,24 +76,12 @@ Json::Value Negate::JsonValue() { } // Load JSON string into this object -void Negate::SetJson(std::string value) { +void Negate::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -104,8 +92,8 @@ void Negate::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void Negate::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void Negate::SetJsonValue(const Json::Value root) { // Set parent data EffectBase::SetJsonValue(root); @@ -113,7 +101,7 @@ void Negate::SetJsonValue(Json::Value root) { } // Get all properties for a specific frame -std::string Negate::PropertiesJSON(int64_t requested_frame) { +std::string Negate::PropertiesJSON(int64_t requested_frame) const { // Generate JSON properties list Json::Value root; diff --git a/src/effects/Pixelate.cpp b/src/effects/Pixelate.cpp index a57a186f1..c993915c5 100644 --- a/src/effects/Pixelate.cpp +++ b/src/effects/Pixelate.cpp @@ -75,34 +75,20 @@ std::shared_ptr Pixelate::GetFrame(std::shared_ptr frame, int64_t double bottom_value = bottom.GetValue(frame_number); if (pixelization_value > 0.0) { - // Resize frame image smaller (based on pixelization value) - std::shared_ptr smaller_frame_image = std::shared_ptr(new QImage(frame_image->scaledToWidth(std::max(frame_image->width() * pixelization_value, 2.0), Qt::SmoothTransformation))); - - // Resize image back to original size (with no smoothing to create pixelated image) - std::shared_ptr pixelated_image = std::shared_ptr(new QImage(smaller_frame_image->scaledToWidth(frame_image->width(), Qt::FastTransformation).convertToFormat(QImage::Format_RGBA8888))); - - // Get pixel array pointer - unsigned char *pixels = (unsigned char *) frame_image->bits(); - unsigned char *pixelated_pixels = (unsigned char *) pixelated_image->bits(); - - // Get pixels sizes of all margins - int top_bar_height = top_value * frame_image->height(); - int bottom_bar_height = bottom_value * frame_image->height(); - int left_bar_width = left_value * frame_image->width(); - int right_bar_width = right_value * frame_image->width(); - - // Loop through rows - for (int row = 0; row < frame_image->height(); row++) { - - // Copy pixelated pixels into original frame image (where needed) - if ((row >= top_bar_height) && (row <= frame_image->height() - bottom_bar_height)) { - memcpy(&pixels[(row * frame_image->width() + left_bar_width) * 4], &pixelated_pixels[(row * frame_image->width() + left_bar_width) * 4], sizeof(char) * (frame_image->width() - left_bar_width - right_bar_width) * 4); - } - } - - // Cleanup temp images - smaller_frame_image.reset(); - pixelated_image.reset(); + int w = frame_image->width(); + int h = frame_image->height(); + + // Define area we're working on in terms of a QRect with QMargins applied + QRect area(QPoint(0,0), frame_image->size()); + area = area.marginsRemoved({int(left_value * w), int(top_value * h), int(right_value * w), int(bottom_value * h)}); + + // Copy and scale pixels in area to be pixelated + auto frame_scaled = frame_image->copy(area).scaledToWidth(area.width() * pixelization_value, Qt::SmoothTransformation); + + // Draw pixelated image back over original + QPainter painter(frame_image.get()); + painter.drawImage(area, frame_scaled); + painter.end(); } // return the modified frame @@ -110,14 +96,14 @@ std::shared_ptr Pixelate::GetFrame(std::shared_ptr frame, int64_t } // Generate JSON string of this object -std::string Pixelate::Json() { +std::string Pixelate::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value Pixelate::JsonValue() { +// Generate Json::Value for this object +Json::Value Pixelate::JsonValue() const { // Create root json object Json::Value root = EffectBase::JsonValue(); // get parent properties @@ -133,24 +119,12 @@ Json::Value Pixelate::JsonValue() { } // Load JSON string into this object -void Pixelate::SetJson(std::string value) { +void Pixelate::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -161,8 +135,8 @@ void Pixelate::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void Pixelate::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void Pixelate::SetJsonValue(const Json::Value root) { // Set parent data EffectBase::SetJsonValue(root); @@ -181,7 +155,7 @@ void Pixelate::SetJsonValue(Json::Value root) { } // Get all properties for a specific frame -std::string Pixelate::PropertiesJSON(int64_t requested_frame) { +std::string Pixelate::PropertiesJSON(int64_t requested_frame) const { // Generate JSON properties list Json::Value root; diff --git a/src/effects/Saturation.cpp b/src/effects/Saturation.cpp index 06bcb02c2..d5c295eb2 100644 --- a/src/effects/Saturation.cpp +++ b/src/effects/Saturation.cpp @@ -69,44 +69,36 @@ std::shared_ptr Saturation::GetFrame(std::shared_ptr frame, int64_ if (!frame_image) return frame; + int pixel_count = frame_image->width() * frame_image->height(); + // Get keyframe values for this frame float saturation_value = saturation.GetValue(frame_number); // Constants used for color saturation formula - double pR = .299; - double pG = .587; - double pB = .114; + const double pR = .299; + const double pG = .587; + const double pB = .114; // Loop through pixels unsigned char *pixels = (unsigned char *) frame_image->bits(); - for (int pixel = 0, byte_index=0; pixel < frame_image->width() * frame_image->height(); pixel++, byte_index+=4) + + #pragma omp parallel for shared (pixels) + for (int pixel = 0; pixel < pixel_count; ++pixel) { // Get the RGB values from the pixel - int R = pixels[byte_index]; - int G = pixels[byte_index + 1]; - int B = pixels[byte_index + 2]; - int A = pixels[byte_index + 3]; + int R = pixels[pixel * 4]; + int G = pixels[pixel * 4 + 1]; + int B = pixels[pixel * 4 + 2]; // Calculate the saturation multiplier double p = sqrt( (R * R * pR) + - (G * G * pG) + - (B * B * pB) ); - - // Adjust the saturation - R = p + (R - p) * saturation_value; - G = p + (G - p) * saturation_value; - B = p + (B - p) * saturation_value; - - // Constrain the value from 0 to 255 - R = constrain(R); - G = constrain(G); - B = constrain(B); - - // Set all pixels to new value - pixels[byte_index] = R; - pixels[byte_index + 1] = G; - pixels[byte_index + 2] = B; - pixels[byte_index + 3] = A; // leave the alpha value alone + (G * G * pG) + + (B * B * pB) ); + + // Apply adjusted and constrained saturation + pixels[pixel * 4] = constrain(p + (R - p) * saturation_value); + pixels[pixel * 4 + 1] = constrain(p + (G - p) * saturation_value); + pixels[pixel * 4 + 2] = constrain(p + (B - p) * saturation_value); } // return the modified frame @@ -114,14 +106,14 @@ std::shared_ptr Saturation::GetFrame(std::shared_ptr frame, int64_ } // Generate JSON string of this object -std::string Saturation::Json() { +std::string Saturation::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value Saturation::JsonValue() { +// Generate Json::Value for this object +Json::Value Saturation::JsonValue() const { // Create root json object Json::Value root = EffectBase::JsonValue(); // get parent properties @@ -133,24 +125,12 @@ Json::Value Saturation::JsonValue() { } // Load JSON string into this object -void Saturation::SetJson(std::string value) { +void Saturation::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -161,8 +141,8 @@ void Saturation::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void Saturation::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void Saturation::SetJsonValue(const Json::Value root) { // Set parent data EffectBase::SetJsonValue(root); @@ -173,7 +153,7 @@ void Saturation::SetJsonValue(Json::Value root) { } // Get all properties for a specific frame -std::string Saturation::PropertiesJSON(int64_t requested_frame) { +std::string Saturation::PropertiesJSON(int64_t requested_frame) const { // Generate JSON properties list Json::Value root; diff --git a/src/effects/Shift.cpp b/src/effects/Shift.cpp index d0908fd88..ee5815912 100644 --- a/src/effects/Shift.cpp +++ b/src/effects/Shift.cpp @@ -69,9 +69,9 @@ std::shared_ptr Shift::GetFrame(std::shared_ptr frame, int64_t fra // Get the current shift amount, and clamp to range (-1 to 1 range) double x_shift = x.GetValue(frame_number); - double x_shift_limit = fmod(abs(x_shift), 1.0); + double x_shift_limit = fmod(fabs(x_shift), 1.0); double y_shift = y.GetValue(frame_number); - double y_shift_limit = fmod(abs(y_shift), 1.0); + double y_shift_limit = fmod(fabs(y_shift), 1.0); // Declare temp arrays to hold pixels while we move things around unsigned char *temp_row = new unsigned char[frame_image->width() * 4](); @@ -133,14 +133,14 @@ std::shared_ptr Shift::GetFrame(std::shared_ptr frame, int64_t fra } // Generate JSON string of this object -std::string Shift::Json() { +std::string Shift::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value Shift::JsonValue() { +// Generate Json::Value for this object +Json::Value Shift::JsonValue() const { // Create root json object Json::Value root = EffectBase::JsonValue(); // get parent properties @@ -153,24 +153,12 @@ Json::Value Shift::JsonValue() { } // Load JSON string into this object -void Shift::SetJson(std::string value) { +void Shift::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -181,8 +169,8 @@ void Shift::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void Shift::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void Shift::SetJsonValue(const Json::Value root) { // Set parent data EffectBase::SetJsonValue(root); @@ -195,7 +183,7 @@ void Shift::SetJsonValue(Json::Value root) { } // Get all properties for a specific frame -std::string Shift::PropertiesJSON(int64_t requested_frame) { +std::string Shift::PropertiesJSON(int64_t requested_frame) const { // Generate JSON properties list Json::Value root; diff --git a/src/effects/Wave.cpp b/src/effects/Wave.cpp index 2139e4ac8..499fc9588 100644 --- a/src/effects/Wave.cpp +++ b/src/effects/Wave.cpp @@ -68,15 +68,13 @@ std::shared_ptr Wave::GetFrame(std::shared_ptr frame, int64_t fram // Get the frame's image std::shared_ptr frame_image = frame->GetImage(); - // Get pixels for frame image + // Get original pixels for frame image, and also make a copy for editing + const unsigned char *original_pixels = (unsigned char *) frame_image->constBits(); unsigned char *pixels = (unsigned char *) frame_image->bits(); - - // Make temp copy of pixels before we start changing them - unsigned char *temp_image = new unsigned char[frame_image->width() * frame_image->height() * 4](); - memcpy(temp_image, pixels, sizeof(char) * frame_image->width() * frame_image->height() * 4); + int pixel_count = frame_image->width() * frame_image->height(); // Get current keyframe values - double time = frame_number;//abs(((frame_number + 255) % 510) - 255); + double time = frame_number; double wavelength_value = wavelength.GetValue(frame_number); double amplitude_value = amplitude.GetValue(frame_number); double multiplier_value = multiplier.GetValue(frame_number); @@ -84,43 +82,41 @@ std::shared_ptr Wave::GetFrame(std::shared_ptr frame, int64_t fram double speed_y_value = speed_y.GetValue(frame_number); // Loop through pixels - for (int pixel = 0, byte_index=0; pixel < frame_image->width() * frame_image->height(); pixel++, byte_index+=4) + #pragma omp parallel for + for (int pixel = 0; pixel < pixel_count; ++pixel) { - // Calculate X and Y pixel coordinates + // Calculate pixel Y value int Y = pixel / frame_image->width(); // Calculate wave pixel offsets - float noiseVal = (100 + Y * 0.001) * multiplier_value; // Time and time multiplier (to make the wave move) - float noiseAmp = noiseVal * amplitude_value; // Apply amplitude / height of the wave - float waveformVal = sin((Y * wavelength_value) + (time * speed_y_value)); // Waveform algorithm on y-axis - float waveVal = (waveformVal + shift_x_value) * noiseAmp; // Shifts pixels on the x-axis + float noiseVal = (100 + Y * 0.001) * multiplier_value; // Time and time multiplier (to make the wave move) + float noiseAmp = noiseVal * amplitude_value; // Apply amplitude / height of the wave + float waveformVal = sin((Y * wavelength_value) + (time * speed_y_value)); // Waveform algorithm on y-axis + float waveVal = (waveformVal + shift_x_value) * noiseAmp; // Shifts pixels on the x-axis - int source_X = round(pixel + waveVal) * 4; - if (source_X < 0) - source_X = 0; - if (source_X > frame_image->width() * frame_image->height() * 4 * sizeof(char)) - source_X = (frame_image->width() * frame_image->height() * 4 * sizeof(char)) - (sizeof(char) * 4); + long unsigned int source_px = round(pixel + waveVal); + if (source_px < 0) + source_px = 0; + if (source_px >= pixel_count) + source_px = pixel_count - 1; // Calculate source array location, and target array location, and copy the 4 color values - memcpy(&pixels[byte_index], &temp_image[source_X], sizeof(char) * 4); + memcpy(&pixels[pixel * 4], &original_pixels[source_px * 4], sizeof(char) * 4); } - // Delete arrays - delete[] temp_image; - // return the modified frame return frame; } // Generate JSON string of this object -std::string Wave::Json() { +std::string Wave::Json() const { // Return formatted string return JsonValue().toStyledString(); } -// Generate Json::JsonValue for this object -Json::Value Wave::JsonValue() { +// Generate Json::Value for this object +Json::Value Wave::JsonValue() const { // Create root json object Json::Value root = EffectBase::JsonValue(); // get parent properties @@ -136,24 +132,12 @@ Json::Value Wave::JsonValue() { } // Load JSON string into this object -void Wave::SetJson(std::string value) { +void Wave::SetJson(const std::string value) { // Parse JSON string into JSON objects - Json::Value root; - Json::CharReaderBuilder rbuilder; - Json::CharReader* reader(rbuilder.newCharReader()); - - std::string errors; - bool success = reader->parse( value.c_str(), - value.c_str() + value.size(), &root, &errors ); - delete reader; - - if (!success) - // Raise exception - throw InvalidJSON("JSON could not be parsed (or is invalid)"); - try { + const Json::Value root = openshot::stringToJson(value); // Set all values that match SetJsonValue(root); } @@ -164,8 +148,8 @@ void Wave::SetJson(std::string value) { } } -// Load Json::JsonValue into this object -void Wave::SetJsonValue(Json::Value root) { +// Load Json::Value into this object +void Wave::SetJsonValue(const Json::Value root) { // Set parent data EffectBase::SetJsonValue(root); @@ -184,7 +168,7 @@ void Wave::SetJsonValue(Json::Value root) { } // Get all properties for a specific frame -std::string Wave::PropertiesJSON(int64_t requested_frame) { +std::string Wave::PropertiesJSON(int64_t requested_frame) const { // Generate JSON properties list Json::Value root; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a104d766a..480dfb3d3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -24,117 +24,116 @@ # along with OpenShot Library. If not, see . ################################################################################ -SET(TEST_MEDIA_PATH "${PROJECT_SOURCE_DIR}/src/examples/") +# Test media path, used by unit tests for input data +file(TO_NATIVE_PATH "${PROJECT_SOURCE_DIR}/src/examples/" TEST_MEDIA_PATH) +add_definitions( -DTEST_MEDIA_PATH="${TEST_MEDIA_PATH}" ) ################ WINDOWS ################## # Set some compiler options for Windows # required for libopenshot-audio headers -IF (WIN32) - STRING(REPLACE "/" "\\\\" TEST_MEDIA_PATH TEST_MEDIA_PATH) +if(WIN32) add_definitions( -DIGNORE_JUCE_HYPOT=1 ) - SET(CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -include cmath") -ENDIF(WIN32) - -add_definitions( -DTEST_MEDIA_PATH="${TEST_MEDIA_PATH}" ) + set(CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -include cmath") +endif() ################### UNITTEST++ ##################### # Find UnitTest++ libraries (used for unit testing) -FIND_PACKAGE(UnitTest++ REQUIRED) +find_package(UnitTest++) + +if (NOT UnitTest++_FOUND) + set(TESTS_ENABLED OFF PARENT_SCOPE) + return() +endif() # Include UnitTest++ headers (needed for compile) -include_directories(${UNITTEST++_INCLUDE_DIR}) +include_directories(${UnitTest++_INCLUDE_DIRS}) + +set_package_properties(UnitTest++ PROPERTIES + TYPE RECOMMENDED + PURPOSE "Unit testing framework") ################ IMAGE MAGICK ################## # Set the Quantum Depth that ImageMagick was built with (default to 16 bits) -IF (MAGICKCORE_QUANTUM_DEPTH) +if(MAGICKCORE_QUANTUM_DEPTH) add_definitions( -DMAGICKCORE_QUANTUM_DEPTH=${MAGICKCORE_QUANTUM_DEPTH} ) -ELSE (MAGICKCORE_QUANTUM_DEPTH) +else() add_definitions( -DMAGICKCORE_QUANTUM_DEPTH=16 ) -ENDIF (MAGICKCORE_QUANTUM_DEPTH) -IF (MAGICKCORE_HDRI_ENABLE) +endif() + +if(MAGICKCORE_HDRI_ENABLE) add_definitions( -DMAGICKCORE_HDRI_ENABLE=${MAGICKCORE_HDRI_ENABLE} ) -ELSE (MAGICKCORE_HDRI_ENABLE) +else() add_definitions( -DMAGICKCORE_HDRI_ENABLE=0 ) -ENDIF (MAGICKCORE_HDRI_ENABLE) -IF (OPENSHOT_IMAGEMAGICK_COMPATIBILITY) +endif() + +if(OPENSHOT_IMAGEMAGICK_COMPATIBILITY) add_definitions( -DOPENSHOT_IMAGEMAGICK_COMPATIBILITY=${OPENSHOT_IMAGEMAGICK_COMPATIBILITY} ) -ELSE (OPENSHOT_IMAGEMAGICK_COMPATIBILITY) +else() add_definitions( -DOPENSHOT_IMAGEMAGICK_COMPATIBILITY=0 ) -ENDIF (OPENSHOT_IMAGEMAGICK_COMPATIBILITY) +endif() # Find the ImageMagick++ library -FIND_PACKAGE(ImageMagick COMPONENTS Magick++ MagickWand MagickCore) -IF (ImageMagick_FOUND) +find_package(ImageMagick COMPONENTS Magick++ MagickWand MagickCore) +if(ImageMagick_FOUND) # Include ImageMagick++ headers (needed for compile) include_directories(${ImageMagick_INCLUDE_DIRS}) # define a global var (used in the C++) add_definitions( -DUSE_IMAGEMAGICK=1 ) - SET(CMAKE_SWIG_FLAGS "-DUSE_IMAGEMAGICK=1") - -ENDIF (ImageMagick_FOUND) + set(CMAKE_SWIG_FLAGS "-DUSE_IMAGEMAGICK=1") +endif() ################# LIBOPENSHOT-AUDIO ################### # Find JUCE-based openshot Audio libraries -FIND_PACKAGE(OpenShotAudio 0.1.8 REQUIRED) +find_package(OpenShotAudio 0.2.0 REQUIRED) # Include Juce headers (needed for compile) include_directories(${LIBOPENSHOT_AUDIO_INCLUDE_DIRS}) ################# BLACKMAGIC DECKLINK ################### -IF (ENABLE_BLACKMAGIC) +if(ENABLE_BLACKMAGIC) # Find BlackMagic DeckLinkAPI libraries - FIND_PACKAGE(BlackMagic) + find_package(BlackMagic) - IF (BLACKMAGIC_FOUND) + if(BLACKMAGIC_FOUND) # Include Blackmagic headers (needed for compile) include_directories(${BLACKMAGIC_INCLUDE_DIR}) - ENDIF (BLACKMAGIC_FOUND) -ENDIF (ENABLE_BLACKMAGIC) - - -################### RESVG ##################### -# Find resvg library (used for rendering svg files) -FIND_PACKAGE(RESVG) - -# Include resvg headers (optional SVG library) -if (RESVG_FOUND) - include_directories(${RESVG_INCLUDE_DIRS}) -endif(RESVG_FOUND) + endif() +endif() ############### SET TEST SOURCE FILES ################# -SET ( OPENSHOT_TEST_FILES - Cache_Tests.cpp - Clip_Tests.cpp - Color_Tests.cpp - Coordinate_Tests.cpp - ReaderBase_Tests.cpp - ImageWriter_Tests.cpp - FFmpegReader_Tests.cpp - FFmpegWriter_Tests.cpp - Fraction_Tests.cpp - FrameMapper_Tests.cpp - KeyFrame_Tests.cpp - Point_Tests.cpp - Settings_Tests.cpp - Timeline_Tests.cpp ) +set(OPENSHOT_TEST_FILES + Cache_Tests.cpp + Clip_Tests.cpp + Color_Tests.cpp + Coordinate_Tests.cpp + ReaderBase_Tests.cpp + ImageWriter_Tests.cpp + FFmpegReader_Tests.cpp + FFmpegWriter_Tests.cpp + Fraction_Tests.cpp + Frame_Tests.cpp + FrameMapper_Tests.cpp + KeyFrame_Tests.cpp + Point_Tests.cpp + Settings_Tests.cpp + Timeline_Tests.cpp ) ################ TESTER EXECUTABLE ################# # Create unit test executable (openshot-test) message (STATUS "Tests enabled, test executable will be built as tests/openshot-test") add_executable(openshot-test - tests.cpp - ${OPENSHOT_TEST_FILES} ) + tests.cpp + ${OPENSHOT_TEST_FILES} ) # Link libraries to the new executable -target_link_libraries(openshot-test openshot ${UNITTEST++_LIBRARY}) +target_link_libraries(openshot-test openshot ${UnitTest++_LIBRARIES}) ##### RUNNING TESTS (make os_test / make test) ##### # Hook up the 'make os_test' target to the 'openshot-test' executable -ADD_CUSTOM_TARGET(os_test COMMAND openshot-test) -list(APPEND OS_TEST_CMDS "'make os_test'") +add_custom_target(os_test COMMAND openshot-test) # Also hook up 'make test', if possible # This requires CMake 3.11+, where the CMP0037 policy @@ -146,8 +145,8 @@ endif() if (CMAKE_VERSION VERSION_GREATER 3.11) message(STATUS "Cmake 3.11+ detected, enabling 'test' target") add_custom_target(test COMMAND openshot-test) - list(APPEND OS_TEST_CMDS " or " "'make test'") + set(TEST_TARGET_NAME "test") +else() + set(TEST_TARGET_NAME "os_test") endif() - -string(CONCAT t ${OS_TEST_CMDS}) -message("\nTo run unit tests, use: ${t}") +add_feature_info("Testrunner" ENABLE_TESTS "Run unit tests with 'make ${TEST_TARGET_NAME}'") diff --git a/tests/Cache_Tests.cpp b/tests/Cache_Tests.cpp index ea5b45ce4..ddf698f54 100644 --- a/tests/Cache_Tests.cpp +++ b/tests/Cache_Tests.cpp @@ -395,31 +395,31 @@ TEST(CacheDisk_JSON) // Add some frames (out of order) std::shared_ptr f3(new Frame(3, 1280, 720, "Blue", 500, 2)); c.Add(f3); - CHECK_EQUAL(1, c.JsonValue()["ranges"].size()); + CHECK_EQUAL(1, (int)c.JsonValue()["ranges"].size()); CHECK_EQUAL("1", c.JsonValue()["version"].asString()); // Add some frames (out of order) std::shared_ptr f1(new Frame(1, 1280, 720, "Blue", 500, 2)); c.Add(f1); - CHECK_EQUAL(2, c.JsonValue()["ranges"].size()); + CHECK_EQUAL(2, (int)c.JsonValue()["ranges"].size()); CHECK_EQUAL("2", c.JsonValue()["version"].asString()); // Add some frames (out of order) std::shared_ptr f2(new Frame(2, 1280, 720, "Blue", 500, 2)); c.Add(f2); - CHECK_EQUAL(1, c.JsonValue()["ranges"].size()); + CHECK_EQUAL(1, (int)c.JsonValue()["ranges"].size()); CHECK_EQUAL("3", c.JsonValue()["version"].asString()); // Add some frames (out of order) std::shared_ptr f5(new Frame(5, 1280, 720, "Blue", 500, 2)); c.Add(f5); - CHECK_EQUAL(2, c.JsonValue()["ranges"].size()); + CHECK_EQUAL(2, (int)c.JsonValue()["ranges"].size()); CHECK_EQUAL("4", c.JsonValue()["version"].asString()); // Add some frames (out of order) std::shared_ptr f4(new Frame(4, 1280, 720, "Blue", 500, 2)); c.Add(f4); - CHECK_EQUAL(1, c.JsonValue()["ranges"].size()); + CHECK_EQUAL(1, (int)c.JsonValue()["ranges"].size()); CHECK_EQUAL("5", c.JsonValue()["version"].asString()); // Delete cache directory @@ -435,31 +435,31 @@ TEST(CacheMemory_JSON) // Add some frames (out of order) std::shared_ptr f3(new Frame(3, 1280, 720, "Blue", 500, 2)); c.Add(f3); - CHECK_EQUAL(1, c.JsonValue()["ranges"].size()); + CHECK_EQUAL(1, (int)c.JsonValue()["ranges"].size()); CHECK_EQUAL("1", c.JsonValue()["version"].asString()); // Add some frames (out of order) std::shared_ptr f1(new Frame(1, 1280, 720, "Blue", 500, 2)); c.Add(f1); - CHECK_EQUAL(2, c.JsonValue()["ranges"].size()); + CHECK_EQUAL(2, (int)c.JsonValue()["ranges"].size()); CHECK_EQUAL("2", c.JsonValue()["version"].asString()); // Add some frames (out of order) std::shared_ptr f2(new Frame(2, 1280, 720, "Blue", 500, 2)); c.Add(f2); - CHECK_EQUAL(1, c.JsonValue()["ranges"].size()); + CHECK_EQUAL(1, (int)c.JsonValue()["ranges"].size()); CHECK_EQUAL("3", c.JsonValue()["version"].asString()); // Add some frames (out of order) std::shared_ptr f5(new Frame(5, 1280, 720, "Blue", 500, 2)); c.Add(f5); - CHECK_EQUAL(2, c.JsonValue()["ranges"].size()); + CHECK_EQUAL(2, (int)c.JsonValue()["ranges"].size()); CHECK_EQUAL("4", c.JsonValue()["version"].asString()); // Add some frames (out of order) std::shared_ptr f4(new Frame(4, 1280, 720, "Blue", 500, 2)); c.Add(f4); - CHECK_EQUAL(1, c.JsonValue()["ranges"].size()); + CHECK_EQUAL(1, (int)c.JsonValue()["ranges"].size()); CHECK_EQUAL("5", c.JsonValue()["version"].asString()); } diff --git a/tests/Clip_Tests.cpp b/tests/Clip_Tests.cpp index 711fef03c..c66cc9a48 100644 --- a/tests/Clip_Tests.cpp +++ b/tests/Clip_Tests.cpp @@ -241,7 +241,7 @@ TEST(Clip_Effects) CHECK_EQUAL(255, (int)pixels[pixel_index + 3]); // Check the # of Effects - CHECK_EQUAL(1, c10.Effects().size()); + CHECK_EQUAL(1, (int)c10.Effects().size()); // Add a 2nd negate effect @@ -262,5 +262,5 @@ TEST(Clip_Effects) CHECK_EQUAL(255, (int)pixels[pixel_index + 3]); // Check the # of Effects - CHECK_EQUAL(2, c10.Effects().size()); + CHECK_EQUAL(2, (int)c10.Effects().size()); } diff --git a/tests/FFmpegWriter_Tests.cpp b/tests/FFmpegWriter_Tests.cpp index 21940b1b3..cb75a118a 100644 --- a/tests/FFmpegWriter_Tests.cpp +++ b/tests/FFmpegWriter_Tests.cpp @@ -36,7 +36,8 @@ using namespace std; using namespace openshot; -TEST(FFmpegWriter_Test_Webm) +SUITE(FFMpegWriter) { +TEST(Webm) { // Reader stringstream path; @@ -82,3 +83,46 @@ TEST(FFmpegWriter_Test_Webm) CHECK_CLOSE(23, (int)pixels[pixel_index + 2], 5); CHECK_CLOSE(255, (int)pixels[pixel_index + 3], 5); } + +TEST(Options_Overloads) +{ + // Reader + stringstream path; + path << TEST_MEDIA_PATH << "sintel_trailer-720p.mp4"; + FFmpegReader r(path.str()); + r.Open(); + + /* WRITER ---------------- */ + FFmpegWriter w("output1.mp4"); + + // Set options + w.SetAudioOptions("aac", 48000, 192000); + w.SetVideoOptions("libx264", 1280, 720, Fraction(30,1), 5000000); + + // Open writer + w.Open(); + + // Write some frames + w.WriteFrame(&r, 24, 50); + + // Close writer & reader + w.Close(); + r.Close(); + + FFmpegReader r1("output1.mp4"); + r1.Open(); + + // Verify implied settings + CHECK_EQUAL(true, r1.info.has_audio); + CHECK_EQUAL(true, r1.info.has_video); + + CHECK_EQUAL(2, r1.GetFrame(1)->GetAudioChannelsCount()); + CHECK_EQUAL(LAYOUT_STEREO, r1.info.channel_layout); + + CHECK_EQUAL(1, r1.info.pixel_ratio.num); + CHECK_EQUAL(1, r1.info.pixel_ratio.den); + CHECK_EQUAL(false, r1.info.interlaced_frame); + CHECK_EQUAL(true, r1.info.top_field_first); +} + +} // SUITE() diff --git a/tests/Frame_Tests.cpp b/tests/Frame_Tests.cpp new file mode 100644 index 000000000..a92906a3d --- /dev/null +++ b/tests/Frame_Tests.cpp @@ -0,0 +1,150 @@ +/** + * @file + * @brief Unit tests for openshot::Frame + * @author Jonathan Thomas + * @author FeRD (Frank Dana) + * + * @ref License + */ + +/* LICENSE + * + * Copyright (c) 2008-2019 OpenShot Studios, LLC + * . This file is part of + * OpenShot Library (libopenshot), an open-source project dedicated to + * delivering high quality video editing and animation solutions to the + * world. For more information visit . + * + * OpenShot Library (libopenshot) is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * OpenShot Library (libopenshot) is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenShot Library. If not, see . + */ + +#include "UnitTest++.h" +// Prevent name clashes with juce::UnitTest +#define DONT_SET_USING_JUCE_NAMESPACE 1 +#include "../include/OpenShot.h" + +#include + +using namespace openshot; + +SUITE(Frame_Tests) +{ + +TEST(Default_Constructor) +{ + // Create a "blank" default Frame + std::shared_ptr f1(new Frame()); + + CHECK(f1 != nullptr); // Test aborts here if we didn't get a Frame + + // Check basic default parameters + CHECK_EQUAL(1, f1->GetHeight()); + CHECK_EQUAL(1, f1->GetWidth()); + CHECK_EQUAL(44100, f1->SampleRate()); + CHECK_EQUAL(2, f1->GetAudioChannelsCount()); + + // Should be false until we load or create contents + CHECK_EQUAL(false, f1->has_image_data); + CHECK_EQUAL(false, f1->has_audio_data); + + // Calling GetImage() paints a blank frame, by default + std::shared_ptr i1 = f1->GetImage(); + + CHECK(i1 != nullptr); + + CHECK_EQUAL(true,f1->has_image_data); + CHECK_EQUAL(false,f1->has_audio_data); +} + + +TEST(Data_Access) +{ + // Create a video clip + std::stringstream path; + path << TEST_MEDIA_PATH << "sintel_trailer-720p.mp4"; + Clip c1(path.str()); + c1.Open(); + + // Get first frame + std::shared_ptr f1 = c1.GetFrame(1); + + CHECK(f1 != nullptr); + + CHECK_EQUAL(1, f1->number); + CHECK_EQUAL(1280, f1->GetWidth()); + CHECK_EQUAL(720, f1->GetHeight()); +} + + +TEST(AddImage_QImage) +{ + // Create a "blank" default Frame + std::shared_ptr f1(new Frame()); + + // Load an image + std::stringstream path; + path << TEST_MEDIA_PATH << "front.png"; + std::shared_ptr i1(new QImage(QString::fromStdString(path.str()))) ; + + CHECK(f1 != nullptr); // Test aborts here if we didn't get a Frame + CHECK_EQUAL(false, i1->isNull()); + + f1->AddImage(i1); + + // Check loaded image parameters + CHECK_EQUAL(i1->height(), f1->GetHeight()); + CHECK_EQUAL(i1->width(), f1->GetWidth()); + CHECK_EQUAL(true, f1->has_image_data); +} + + +TEST(Copy_Constructor) +{ + // Create a dummy Frame + openshot::Frame f1(1, 800, 600, "#000000"); + + // Load an image + std::stringstream path; + path << TEST_MEDIA_PATH << "front.png"; + std::shared_ptr i1( new QImage(QString::fromStdString(path.str())) ); + + CHECK_EQUAL(false, i1->isNull()); + + // Add image to f1, then copy f1 to f2 + f1.AddImage(i1); + + Frame f2 = f1; + + CHECK_EQUAL(f1.GetHeight(), f2.GetHeight()); + CHECK_EQUAL(f1.GetWidth(), f2.GetWidth()); + + CHECK_EQUAL(f1.has_image_data, f2.has_image_data); + CHECK_EQUAL(f1.has_audio_data, f2.has_audio_data); + + Fraction par1 = f1.GetPixelRatio(); + Fraction par2 = f2.GetPixelRatio(); + + CHECK_EQUAL(par1.num, par2.num); + CHECK_EQUAL(par1.den, par2.den); + + + CHECK_EQUAL(f1.SampleRate(), f2.SampleRate()); + CHECK_EQUAL(f1.GetAudioChannelsCount(), f2.GetAudioChannelsCount()); + CHECK_EQUAL(f1.ChannelsLayout(), f2.ChannelsLayout()); + + CHECK_EQUAL(f1.GetBytes(), f2.GetBytes()); + CHECK_EQUAL(f1.GetAudioSamplesCount(), f2.GetAudioSamplesCount()); +} + +} // SUITE(Frame_Tests) diff --git a/tests/ReaderBase_Tests.cpp b/tests/ReaderBase_Tests.cpp index 8ac283218..776529d3c 100644 --- a/tests/ReaderBase_Tests.cpp +++ b/tests/ReaderBase_Tests.cpp @@ -49,9 +49,9 @@ TEST(ReaderBase_Derived_Class) std::shared_ptr GetFrame(int64_t number) { std::shared_ptr f(new Frame()); return f; } void Close() { }; void Open() { }; - string Json() { return NULL; }; + string Json() const { return ""; }; void SetJson(string value) { }; - Json::Value JsonValue() { return (int) NULL; }; + Json::Value JsonValue() const { return Json::Value("{}"); }; void SetJsonValue(Json::Value root) { }; bool IsOpen() { return true; }; string Name() { return "TestReader"; }; @@ -60,6 +60,23 @@ TEST(ReaderBase_Derived_Class) // Create an instance of the derived class TestReader t1; + // Validate the new class + CHECK_EQUAL("TestReader", t1.Name()); + + t1.Close(); + t1.Open(); + CHECK_EQUAL(true, t1.IsOpen()); + + CHECK_EQUAL(true, t1.GetCache() == NULL); + + t1.SetJson("{ }"); + t1.SetJsonValue(Json::Value("{}")); + CHECK_EQUAL("", t1.Json()); + auto json = t1.JsonValue(); + CHECK_EQUAL(json, Json::Value("{}")); + + auto f = t1.GetFrame(1); + // Check some of the default values of the FileInfo struct on the base class CHECK_EQUAL(false, t1.info.has_audio); CHECK_EQUAL(false, t1.info.has_audio);