diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 612f234c..62495078 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -46,7 +46,7 @@ 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
diff --git a/.travis.yml b/.travis.yml
index 4375b3f2..f449c3f0 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
@@ -32,26 +33,13 @@ addons:
- libswresample-dev
matrix:
-
- # The FFmpeg4 PPA is currently down as a protest
- allow_failures:
- - env: BUILD_VERSION=ffmpeg4
-
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 (Ubuntu 18.04 Bionic)"
+ env:
+ - BUILD_VERSION=coverage_ffmpeg3
+ - CMAKE_EXTRA_ARGS="-DENABLE_COVERAGE=1"
+ - TEST_TARGET=coverage
os: linux
dist: bionic
addons:
@@ -62,9 +50,14 @@ matrix:
packages:
- *ff_common
- qt5-default
+ - 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:
@@ -76,6 +69,7 @@ matrix:
packages:
- *ff_common
- qt5-default
+ - libjsoncpp-dev
- libavcodec58
- libavformat58
- libavdevice58
@@ -86,8 +80,28 @@ matrix:
- libavresample4
- libswresample3
+ - name: "FFmpeg 3 GCC (Ubuntu 18.04 Bionic)"
+ env:
+ - BUILD_VERSION=ffmpeg3
+ - CMAKE_EXTRA_ARGS=""
+ - TEST_TARGET=test
+ os: linux
+ dist: bionic
+ addons:
+ apt:
+ sources:
+ - sourceline: 'ppa:openshot.developers/libopenshot-daily'
+ - sourceline: 'ppa:beineri/opt-qt-5.12.3-bionic'
+ packages:
+ - *ff_common
+ - qt5-default
+ - libjsoncpp-dev
+
- name: "FFmpeg 3 Clang (Ubuntu 18.04 Bionic)"
- env: BUILD_VERSION=ffmpeg3
+ env:
+ - BUILD_VERSION=clang_ffmpeg3
+ - CMAKE_EXTRA_ARGS=""
+ - TEST_TARGET=test
os: linux
dist: bionic
compiler: clang
@@ -101,9 +115,28 @@ matrix:
- qt5-default
- libomp-dev
+ - 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 574ff8bb..cce01d35 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.4-dev1")
+set(PROJECT_SO_VERSION 18)
# Remove the dash and anything following, to get the #.#.# version for project()
STRING(REGEX REPLACE "\-.*$" "" VERSION_NUM "${PROJECT_VERSION_FULL}")
@@ -73,7 +73,9 @@ include(FeatureSummary)
# 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(DISABLE_TESTS "Don't build unit tests" OFF)
option(ENABLE_IWYU "Enable 'Include What You Use' scanner (CMake 3.3+)" OFF)
+option(ENABLE_COVERAGE "Enable coverage reporting" OFF)
########## Configure Version.h header ##############
configure_file(include/OpenShotVersion.h.in include/OpenShotVersion.h @ONLY)
@@ -94,6 +96,22 @@ 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)
@@ -121,6 +139,16 @@ if(NOT DISABLE_TESTS)
add_subdirectory(tests)
endif()
+############## 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
diff --git a/cmake/Modules/CodeCoverage.cmake b/cmake/Modules/CodeCoverage.cmake
new file mode 100644
index 00000000..786a06b4
--- /dev/null
+++ b/cmake/Modules/CodeCoverage.cmake
@@ -0,0 +1,414 @@
+# 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
+#
+# 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)
+ message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
+endif()
+
+set(COVERAGE_COMPILER_FLAGS "-g -fprofile-arcs -ftest-coverage"
+ CACHE INTERNAL "")
+
+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_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")
+ 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}
+ 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})
+ string(REPLACE "*" "\\*" EXCLUDE_REPLACED ${EXCLUDE})
+ list(APPEND GCOVR_EXCLUDE_ARGS "-e")
+ list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE_REPLACED}")
+ 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}
+ 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})
+ string(REPLACE "*" "\\*" EXCLUDE_REPLACED ${EXCLUDE})
+ list(APPEND GCOVR_EXCLUDE_ARGS "-e")
+ list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE_REPLACED}")
+ 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}
+ 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)
+ 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 b03a0667..0538eacd 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 )
+# vim: ts=2 sw=2
+#[=======================================================================[.rst:
+FindRESVG
+---------
+Try to find the shared-library build of resvg, the Rust SVG library
-find_library ( RESVG_LIBRARY NAMES resvg
- PATHS /usr/lib
- /usr/local/lib
- $ENV{RESVGDIR}
- $ENV{RESVGDIR}/lib )
+IMPORTED targets
+^^^^^^^^^^^^^^^^
-set ( RESVG_LIBRARIES ${RESVG_LIBRARY} )
-set ( RESVG_INCLUDE_DIRS ${RESVG_INCLUDE_DIR} )
+This module defines :prop_tgt:`IMPORTED` target ``RESVG::resvg`` when
+the library and headers are found.
-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 )
+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/codecov.yml b/codecov.yml
new file mode 100644
index 00000000..dfdc9375
--- /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/include/AudioBufferSource.h b/include/AudioBufferSource.h
index 42e55c94..3a17feb3 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 33030adf..c4e2d248 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 96615cb3..d88eb7cb 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/Clip.h b/include/Clip.h
index 6891df6c..68d4622a 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
diff --git a/include/ClipBase.h b/include/ClipBase.h
index 729b1c89..1f7f55c4 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"
diff --git a/include/EffectBase.h b/include/EffectBase.h
index d104cf0a..1f967a02 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
diff --git a/include/Frame.h b/include/Frame.h
index b9b989a5..f4ff54d4 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/src/AudioBufferSource.cpp b/src/AudioBufferSource.cpp
index 46b04916..2f3d14ca 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 41c0b3f6..c96d0bcc 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 5314d880..f7b9aee7 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -79,7 +79,7 @@ ENDIF (ImageMagick_FOUND)
################# LIBOPENSHOT-AUDIO ###################
# Find JUCE-based openshot Audio libraries
-FIND_PACKAGE(OpenShotAudio 0.1.8 REQUIRED)
+FIND_PACKAGE(OpenShotAudio 0.1.9 REQUIRED)
# Include Juce headers (needed for compile)
include_directories(${LIBOPENSHOT_AUDIO_INCLUDE_DIRS})
@@ -100,20 +100,6 @@ IF (ENABLE_BLACKMAGIC)
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})
-
- # define a global var (used in the C++)
- add_definitions( -DUSE_RESVG=1 )
- SET(CMAKE_SWIG_FLAGS "-DUSE_RESVG=1")
-endif(RESVG_FOUND)
-
############### PROFILING #################
#set(PROFILER "/usr/lib/libprofiler.so.0.3.2")
#set(PROFILER "/usr/lib/libtcmalloc.so.4")
@@ -358,6 +344,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
@@ -369,10 +375,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()
diff --git a/src/CacheBase.cpp b/src/CacheBase.cpp
index 122aba32..bc57f3f4 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)
diff --git a/src/CacheDisk.cpp b/src/CacheDisk.cpp
index f8e9b919..9f67ce99 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,13 +103,11 @@ 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;
diff --git a/src/CacheMemory.cpp b/src/CacheMemory.cpp
index b00fb21f..70feef03 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()
diff --git a/src/Clip.cpp b/src/Clip.cpp
index 269b82d1..695fb9a1 100644
--- a/src/Clip.cpp
+++ b/src/Clip.cpp
@@ -782,11 +782,8 @@ Json::Value Clip::JsonValue() const {
root["effects"] = Json::Value(Json::arrayValue);
// loop through effects
- std::list::const_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());
}
@@ -893,10 +890,7 @@ void Clip::SetJsonValue(const 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;
@@ -1013,12 +1007,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/EffectBase.cpp b/src/EffectBase.cpp
index 65329194..05ed97c2 100644
--- a/src/EffectBase.cpp
+++ b/src/EffectBase.cpp
@@ -87,7 +87,6 @@ Json::Value EffectBase::JsonValue() const {
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;
@@ -132,7 +131,6 @@ Json::Value EffectBase::JsonInfo() const {
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/FFmpegReader.cpp b/src/FFmpegReader.cpp
index 503d28a3..bb217e61 100644
--- a/src/FFmpegReader.cpp
+++ b/src/FFmpegReader.cpp
@@ -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)");
diff --git a/src/FFmpegWriter.cpp b/src/FFmpegWriter.cpp
index a1be343a..245bd9bd 100644
--- a/src/FFmpegWriter.cpp
+++ b/src/FFmpegWriter.cpp
@@ -615,7 +615,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
@@ -1083,7 +1083,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++)
@@ -1418,7 +1418,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;
}
@@ -1483,7 +1483,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;
@@ -1543,8 +1544,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) {
@@ -1564,15 +1567,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);
@@ -1605,6 +1609,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)));
diff --git a/src/Frame.cpp b/src/Frame.cpp
index 40183422..8a843aa8 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)));
diff --git a/src/FrameMapper.cpp b/src/FrameMapper.cpp
index 62aac6f5..7c4d04bb 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();
diff --git a/src/KeyFrame.cpp b/src/KeyFrame.cpp
index 5f58c7a0..64e78224 100644
--- a/src/KeyFrame.cpp
+++ b/src/KeyFrame.cpp
@@ -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];
@@ -492,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
@@ -515,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];
@@ -534,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);
@@ -564,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";
}
@@ -580,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);
}
@@ -588,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/QtImageReader.cpp b/src/QtImageReader.cpp
index b33a5777..864af23d 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 {
+ if (renderer.isValid()) {
- image = std::shared_ptr(new QImage(renderer.defaultSize(), QImage::Format_RGBA8888));
+ 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;
}
-
- } else {
- // Attempt to open file (old method)
- 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 (!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) {
// 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);
diff --git a/src/ReaderBase.cpp b/src/ReaderBase.cpp
index 1f06760c..474dc624 100644
--- a/src/ReaderBase.cpp
+++ b/src/ReaderBase.cpp
@@ -108,9 +108,8 @@ 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::Value for this object
@@ -160,9 +159,9 @@ Json::Value ReaderBase::JsonValue() const {
// Append metadata map
root["metadata"] = Json::Value(Json::objectValue);
- std::map::const_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;
diff --git a/src/Timeline.cpp b/src/Timeline.cpp
index 70f9b1ce..b5a33920 100644
--- a/src/Timeline.cpp
+++ b/src/Timeline.cpp
@@ -79,15 +79,16 @@ Timeline::~Timeline() {
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 +170,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 +194,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 +372,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 +460,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 +506,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 +611,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 +696,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);
}
@@ -780,10 +780,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 +830,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 +846,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 +922,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;
@@ -998,11 +989,8 @@ Json::Value Timeline::JsonValue() const {
root["clips"] = Json::Value(Json::arrayValue);
// Find Clips at this time
- std::list::const_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 +998,8 @@ Json::Value Timeline::JsonValue() const {
root["effects"] = Json::Value(Json::arrayValue);
// loop through effects
- std::list::const_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());
}
@@ -1057,10 +1042,7 @@ void Timeline::SetJsonValue(const Json::Value root) {
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();
@@ -1077,10 +1059,7 @@ void Timeline::SetJsonValue(const 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;
@@ -1120,17 +1099,15 @@ void Timeline::ApplyJsonDiff(std::string value) {
{
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);
@@ -1156,10 +1133,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()) {
@@ -1167,11 +1142,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
@@ -1198,11 +1170,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);
@@ -1284,9 +1253,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
@@ -1296,11 +1263,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
@@ -1509,12 +1473,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/effects/ColorShift.cpp b/src/effects/ColorShift.cpp
index 5d8b5160..20f496aa 100644
--- a/src/effects/ColorShift.cpp
+++ b/src/effects/ColorShift.cpp
@@ -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;
diff --git a/src/effects/Wave.cpp b/src/effects/Wave.cpp
index 1b652098..9d00585f 100644
--- a/src/effects/Wave.cpp
+++ b/src/effects/Wave.cpp
@@ -95,7 +95,7 @@ std::shared_ptr Wave::GetFrame(std::shared_ptr frame, int64_t fram
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;
+ long unsigned 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))
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index a104d766..4b2284f6 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -76,7 +76,7 @@ ENDIF (ImageMagick_FOUND)
################# LIBOPENSHOT-AUDIO ###################
# Find JUCE-based openshot Audio libraries
-FIND_PACKAGE(OpenShotAudio 0.1.8 REQUIRED)
+FIND_PACKAGE(OpenShotAudio 0.1.9 REQUIRED)
# Include Juce headers (needed for compile)
include_directories(${LIBOPENSHOT_AUDIO_INCLUDE_DIRS})
@@ -94,16 +94,6 @@ IF (ENABLE_BLACKMAGIC)
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)
-
-
############### SET TEST SOURCE FILES #################
SET ( OPENSHOT_TEST_FILES
Cache_Tests.cpp
@@ -115,6 +105,7 @@ SET ( OPENSHOT_TEST_FILES
FFmpegReader_Tests.cpp
FFmpegWriter_Tests.cpp
Fraction_Tests.cpp
+ Frame_Tests.cpp
FrameMapper_Tests.cpp
KeyFrame_Tests.cpp
Point_Tests.cpp
diff --git a/tests/Cache_Tests.cpp b/tests/Cache_Tests.cpp
index ea5b45ce..ddf698f5 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 711fef03..c66cc9a4 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/Frame_Tests.cpp b/tests/Frame_Tests.cpp
new file mode 100644
index 00000000..a92906a3
--- /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)