diff --git a/.github/labeler.yml b/.github/labeler.yml
new file mode 100644
index 00000000..756bf1e4
--- /dev/null
+++ b/.github/labeler.yml
@@ -0,0 +1,4 @@
+# Add 'build' label to CMake changes
+build:
+ - /**/CMakeList.txt
+ - /cmake/**/*.cmake
diff --git a/.github/stale.yml b/.github/stale.yml
index c5ec1048..bba68742 100644
--- a/.github/stale.yml
+++ b/.github/stale.yml
@@ -11,9 +11,17 @@ exemptLabels:
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.
+ Thank you so much for submitting an issue to help improve OpenShot Video Editor. We are sorry about this, but this particular issue has gone unnoticed for quite some time. To help keep the OpenShot GitHub Issue Tracker organized and focused, we must ensure that every issue is correctly labelled and triaged, to get the proper attention.
+
+ This issue will be closed, as it meets the following criteria:
+ - No activity in the past 90 days
+ - No one is assigned to this issue
+
+ We'd like to ask you to help us out and determine whether this issue should be reopened.
+ - If this issue is reporting a bug, please can you attempt to reproduce on the [latest daily build](https://www.openshot.org/download/#daily) to help us to understand whether the bug still needs our attention.
+ - If this issue is proposing a new feature, please can you verify whether the feature proposal is still relevant.
+
+ Thanks again for your help!
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
# Only close issues
diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml
new file mode 100644
index 00000000..e90b599b
--- /dev/null
+++ b/.github/workflows/label.yml
@@ -0,0 +1,19 @@
+# This workflow will triage pull requests and apply a label based on the
+# paths that are modified in the pull request.
+#
+# To use this workflow, you will need to set up a .github/labeler.yml
+# file with configuration. For more information, see:
+# https://github.com/actions/labeler/blob/master/README.md
+
+name: Labeler
+on: [pull_request]
+
+jobs:
+ label:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/labeler@v2
+ with:
+ repo-token: "${{ secrets.GITHUB_TOKEN }}"
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 925bf020..b04c9475 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -2,6 +2,9 @@ stages:
- build-libopenshot
- trigger-openshot-qt
+variables:
+ GIT_LOG_FORMAT: "- %h %ad %s [%aN]"
+
linux-builder:
stage: build-libopenshot
artifacts:
@@ -16,18 +19,18 @@ linux-builder:
- unzip artifacts.zip
- export LIBOPENSHOT_AUDIO_DIR=$CI_PROJECT_DIR/build/install-x64
- mkdir -p build; cd build;
- - cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -D"CMAKE_INSTALL_PREFIX:PATH=$CI_PROJECT_DIR/build/install-x64" -D"PYTHON_MODULE_PATH=python" -D"RUBY_MODULE_PATH=ruby" -DCMAKE_BUILD_TYPE:STRING=Release -DUSE_SYSTEM_JSONCPP=0 ../
+ - cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -D"CMAKE_INSTALL_PREFIX:PATH=$CI_PROJECT_DIR/build/install-x64" -D"PYTHON_MODULE_PATH=python" -D"RUBY_MODULE_PATH=ruby" -DCMAKE_BUILD_TYPE:STRING=Release -DAPPIMAGE_BUILD=1 -DUSE_SYSTEM_JSONCPP=0 ../
- make
- make install
- make doc
- ~/auto-update-docs "$CI_PROJECT_DIR/build" "$CI_COMMIT_REF_NAME"
- 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 @^)..@ --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 --no-abbrev --date=short --no-merges --pretty="tformat:$GIT_LOG_FORMAT" > "install-x64/share/$CI_PROJECT_NAME.log"
when: always
except:
- tags
tags:
- - linux
+ - linux-bionic
mac-builder:
stage: build-libopenshot
@@ -43,11 +46,11 @@ mac-builder:
- unzip artifacts.zip
- export LIBOPENSHOT_AUDIO_DIR=$CI_PROJECT_DIR/build/install-x64
- mkdir -p build; cd build;
- - 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_MODULE_PATH=python -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" ../
+ - cmake -DCMAKE_EXE_LINKER_FLAGS="-stdlib=libc++" -DCMAKE_SHARED_LINKER_FLAGS="-stdlib=libc++" -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -D"CMAKE_INSTALL_PREFIX:PATH=$CI_PROJECT_DIR/build/install-x64" -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang -DCMAKE_PREFIX_PATH=/usr/local/qt5.15.X/qt5.15/5.15.0/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_MODULE_PATH=python -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.11.sdk" -D"CMAKE_OSX_DEPLOYMENT_TARGET=10.9" -D"CMAKE_INSTALL_RPATH_USE_LINK_PATH=1" -D"ENABLE_RUBY=0" ../
- make
- make install
- 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 @^)..@ --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 --no-abbrev --date=short --no-merges --pretty="tformat:$GIT_LOG_FORMAT" > "install-x64/share/$CI_PROJECT_NAME.log"
when: always
except:
- tags
@@ -74,7 +77,7 @@ windows-builder-x64:
- mingw32-make install
- 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..@" --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 "$PREV_GIT_LABEL..@" --oneline --no-abbrev --date=short --no-merges --pretty="tformat:$GIT_LOG_FORMAT" > "install-x64/share/$CI_PROJECT_NAME.log"
when: always
except:
- tags
@@ -101,7 +104,7 @@ windows-builder-x86:
- mingw32-make install
- 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..@" --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"
+ - git log "$PREV_GIT_LABEL..@" --oneline --no-abbrev --date=short --no-merges --pretty="tformat:$GIT_LOG_FORMAT" > "install-x86/share/$CI_PROJECT_NAME.log"
when: always
except:
- tags
diff --git a/.travis.yml b/.travis.yml
index 24e772af..ab338bdd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -33,7 +33,6 @@ addons:
jobs:
include:
-
- name: "Coverage + FFmpeg 3.4 GCC (Ubuntu 18.04 Bionic)"
env:
- BUILD_VERSION=coverage_ffmpeg34
@@ -45,7 +44,6 @@ jobs:
apt:
sources:
- sourceline: 'ppa:openshot.developers/libopenshot-daily'
- - sourceline: 'ppa:beineri/opt-qt-5.12.3-bionic'
packages:
- *p_common
- qt5-default
@@ -54,19 +52,17 @@ jobs:
- lcov
- binutils-common # For c++filt
- - name: "FFmpeg 4 GCC (Ubuntu 18.04 Bionic)"
+ - name: "FFmpeg 4 GCC (Ubuntu 20.04 Focal)"
env:
- BUILD_VERSION=ffmpeg4
- CMAKE_EXTRA_ARGS=""
- TEST_TARGET=test
os: linux
- dist: bionic
+ dist: focal
addons:
apt:
sources:
- sourceline: 'ppa:openshot.developers/libopenshot-daily'
- - sourceline: 'ppa:beineri/opt-qt-5.12.3-bionic'
- - sourceline: 'ppa:jonathonf/ffmpeg-4'
packages:
- *p_common
- qt5-default
@@ -92,39 +88,12 @@ jobs:
apt:
sources:
- sourceline: 'ppa:openshot.developers/libopenshot-daily'
- - sourceline: 'ppa:beineri/opt-qt-5.12.3-bionic'
packages:
- *p_common
- qt5-default
- libavresample-dev
- 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:
- - *p_common
- - libavresample-dev
- - libavcodec57
- - libavdevice57
- - libavfilter6
- - libavformat57
- - libavresample3
- - libavutil55
- - libpostproc54
- - libswresample2
- - libswscale4
-
- name: "FFmpeg 2 GCC (Ubuntu 16.04 Xenial)"
env:
- BUILD_VERSION=ffmpeg2
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bb3fe8cc..ec608257 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -75,28 +75,20 @@ 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_COVERAGE "Scan test coverage using gcov and report" OFF)
option(ENABLE_DOCS "Build API documentation (requires Doxygen)" ON)
+option(APPIMAGE_BUILD "Build to install in an AppImage (Linux only)" OFF)
+option(ENABLE_MAGICK "Use ImageMagick, if available" 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()
+ set(ENABLE_TESTS OFF)
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
@@ -114,9 +106,9 @@ IF (WIN32)
ENDIF(WIN32)
############## 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)
+if (ENABLE_COVERAGE AND NOT ENABLE_TESTS)
+ message(WARNING "ENABLE_COVERAGE requires unit tests, forcing ENABLE_TESTS")
+ set(ENABLE_TESTS ON CACHE BOOL "Don't build unit tests" FORCE)
endif()
if (ENABLE_COVERAGE)
@@ -129,18 +121,28 @@ if (ENABLE_COVERAGE)
endif()
add_feature_info("Coverage" ENABLE_COVERAGE "analyze test coverage and generate report")
-# Juce requires either DEBUG or NDEBUG to be defined on MacOS.
-# -DNDEBUG is set by cmake for all release configs, so add
-# -DDEBUG for debug builds. We'll do this for all OSes, even
-# though only MacOS requires it.
-set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
-# Make sure we've picked some build type, default to debug
+# Make sure we've picked some build type, default to release
if(NOT DEFINED CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "")
- set(CMAKE_BUILD_TYPE "Debug")
+ set(CMAKE_BUILD_TYPE "Release")
endif()
-############## PROCESS src/ DIRECTORIES ##############
+###
+### Process subdirectories
+###
add_subdirectory(src)
+add_subdirectory(examples)
+add_subdirectory(bindings)
+
+###
+### Configure Version.h header
+###
+# (Note: This comes after the subdirectories, because it needs variables
+# set during the dependency discovery in src/CMakeLists.txt)
+configure_file(src/OpenShotVersion.h.in src/OpenShotVersion.h @ONLY)
+# We'll want that installed later
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/src/OpenShotVersion.h
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libopenshot)
+
################### DOCUMENTATION ###################
# Find Doxygen (used for documentation)
@@ -178,8 +180,10 @@ if (ENABLE_COVERAGE)
NAME coverage
LCOV_ARGS "--no-external"
EXECUTABLE openshot-test
- DEPENDENCIES openshot-test)
- message("Generate coverage report with 'make coverage'")
+ DEPENDENCIES openshot-test
+ EXCLUDE "bindings" "examples" "${CMAKE_CURRENT_BINARY_DIR}/bindings"
+ )
+ message("Generate coverage report with 'make coverage'")
endif()
########### PRINT FEATURE SUMMARY ##############
diff --git a/Doxyfile.in b/Doxyfile.in
index c47d6e65..343190a1 100644
--- a/Doxyfile.in
+++ b/Doxyfile.in
@@ -148,7 +148,7 @@ INLINE_INHERITED_MEMB = NO
# shortest path that makes the file name unique will be used
# The default value is: YES.
-FULL_PATH_NAMES = NO
+FULL_PATH_NAMES = YES
# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
# Stripping is only done if one of the specified strings matches the left-hand
@@ -160,7 +160,7 @@ FULL_PATH_NAMES = NO
# will be relative from the directory where doxygen is started.
# This tag requires that the tag FULL_PATH_NAMES is set to YES.
-STRIP_FROM_PATH =
+STRIP_FROM_PATH = "@PROJECT_SOURCE_DIR@/src"
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
# path mentioned in the documentation of a class, which tells the reader which
@@ -169,7 +169,7 @@ STRIP_FROM_PATH =
# specify the list of include paths that are normally passed to the compiler
# using the -I flag.
-STRIP_FROM_INC_PATH =
+STRIP_FROM_INC_PATH = "@PROJECT_SOURCE_DIR@/src"
# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
# less readable) file names. This can be useful is your file systems doesn't
@@ -803,9 +803,9 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
-INPUT = "@CMAKE_CURRENT_SOURCE_DIR@/include" \
- "@CMAKE_CURRENT_SOURCE_DIR@/src" \
- "@CMAKE_CURRENT_SOURCE_DIR@/doc"
+INPUT = "@PROJECT_SOURCE_DIR@/src" \
+ "@PROJECT_SOURCE_DIR@/doc" \
+ "@PROJECT_BINARY_DIR@/src/OpenShotVersion.h"
# This tag can be used to specify the character encoding of the source files
@@ -862,11 +862,7 @@ EXCLUDE_SYMLINKS = NO
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories for example use the pattern */test/*
-EXCLUDE_PATTERNS = "*/.*" \
- "*/.*/*" \
- "*/src/examples/*" \
- "*/src/bindings/*" \
- "*.py"
+EXCLUDE_PATTERNS =
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
# (namespaces, classes, functions, etc.) that should be excluded from the
@@ -883,27 +879,27 @@ EXCLUDE_SYMBOLS =
# that contain example code fragments that are included (see the \include
# command).
-EXAMPLE_PATH = "@CMAKE_CURRENT_SOURCE_DIR@/tests"
+EXAMPLE_PATH = "@PROJECT_SOURCE_DIR@/examples"
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
# *.h) to filter out the source-files in the directories. If left blank all
# files are included.
-EXAMPLE_PATTERNS =
+EXAMPLE_PATTERNS = "*.cpp"
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
# searched for input files to be used with the \include or \dontinclude commands
# irrespective of the value of the RECURSIVE tag.
# The default value is: NO.
-EXAMPLE_RECURSIVE = NO
+EXAMPLE_RECURSIVE = YES
# The IMAGE_PATH tag can be used to specify one or more files or directories
# that contain images that are to be included in the documentation (see the
# \image command).
-IMAGE_PATH = "@CMAKE_CURRENT_SOURCE_DIR@"
+IMAGE_PATH = "@PROJECT_SOURCE_DIR@"
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
@@ -2086,7 +2082,7 @@ INCLUDE_PATH =
# used.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-INCLUDE_FILE_PATTERNS =
+INCLUDE_FILE_PATTERNS = "*.h"
# The PREDEFINED tag can be used to specify one or more macro names that are
# defined before the preprocessor is started (similar to the -D option of e.g.
diff --git a/INSTALL.md b/INSTALL.md
index f2d0dda7..b1ddc796 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -2,74 +2,100 @@
Operating system specific install instructions are located in:
-* [doc/INSTALL-LINUX.md][INSTALL-LINUX]
-* [doc/INSTALL-MAC.md][INSTALL-MAC]
-* [doc/INSTALL-WINDOWS.md][INSTALL-WINDOWS]
+* [doc/INSTALL-LINUX.md][INSTALL-LINUX]
+* [doc/INSTALL-MAC.md][INSTALL-MAC]
+* [doc/INSTALL-WINDOWS.md][INSTALL-WINDOWS]
## Getting Started
-The best way to get started with libopenshot, is to learn about our build system, obtain all the source code,
-install a development IDE and tools, and better understand our dependencies. So, please read through the
-following sections, and follow the instructions. And keep in mind, that your computer is likely different
-than the one used when writing these instructions. Your file paths and versions of applications might be
-slightly different, so keep an eye out for subtle file path differences in the commands you type.
+The best way to get started with libopenshot is to learn about our build system,
+obtain all the source code, install a development IDE and tools,
+and better understand our dependencies.
+So, please read through the following sections, and follow the instructions.
+And keep in mind, your computer is likely different than the one used when writing these instructions.
+Your file paths and versions of applications will be different,
+so you should treat the specifics used here as examples and adjust as necessary.
+Keep an eye out for subtle file path differences in the commands you type.
## Dependencies
The following libraries are required to build libopenshot.
Instructions on how to install these dependencies vary for each operating system.
-Libraries and Executables have been labeled in the list below to help distinguish between them.
+Libraries and executables have been labeled in the list below to help distinguish between them.
#### FFmpeg (libavformat, libavcodec, libavutil, libavdevice, libavresample, libswscale)
+* **(Library)**
- * http://www.ffmpeg.org/ **(Library)**
- * This library is used to decode and encode video, audio, and image files. It is also used to obtain information about media files, such as frame rate, sample rate, aspect ratio, and other common attributes.
+* This library is used to decode and encode video, audio, and image files.
+ It is also used to obtain information about media files,
+ such as frame rate, sample rate, aspect ratio, and other common attributes.
#### ImageMagick++ (libMagick++, libMagickWand, libMagickCore)
- * http://www.imagemagick.org/script/magick++.php **(Library)**
- * This library is **optional**, and used to decode and encode images.
+* **(Library)**
+* This library is **optional**, and used to decode and encode images.
#### OpenShot Audio Library (libopenshot-audio)
- * https://github.com/OpenShot/libopenshot-audio/ **(Library)**
- * This library is used to mix, resample, host plug-ins, and play audio. It is based on the JUCE project, which is an outstanding audio library used by many different applications
+* **(Library)**
+
+* This library is used to mix, resample, host plug-ins, and play audio.
+ It is based on the JUCE project,
+ an outstanding audio library used by many different applications.
#### Qt 5 (libqt5)
- * http://www.qt.io/qt5/ **(Library)**
- * Qt5 is used to display video, store image data, composite images, apply image effects, and many other utility functions, such as file system manipulation, high resolution timers, etc...
+* **(Library)**
+
+* Qt5 is used to display video, store image data, composite images,
+ apply image effects, and many other utility functions,
+ such as file system manipulation, high resolution timers, etc.
#### ZeroMQ (libzmq)
- * http://zeromq.org/ **(Library)**
- * This library is used to communicate between libopenshot and other applications (publisher / subscriber). Primarily used to send debug data from libopenshot.
+* **(Library)**
+
+* This library is used to communicate between libopenshot and other applications (publisher / subscriber).
+ Primarily used to send debug data from libopenshot.
#### OpenMP (`-fopenmp`)
- * http://openmp.org/wp/ **(Compiler Flag)**
- * If your compiler supports this flag (GCC, Clang, and most other compilers), it provides libopenshot with easy methods of using parallel programming techniques to improve performance and take advantage of multi-core processors.
+* **(Compiler Flag)**
+
+* If your compiler supports this flag (GCC, Clang, and most other compilers),
+ it provides libopenshot with implementations of common parallel programming techniques
+ used to improve performance and take advantage of multi-core processors.
#### CMake (`cmake`)
- * http://www.cmake.org/ **(Executable)**
- * This executable is used to automate the generation of Makefiles, check for dependencies, and is the backbone of libopenshot’s cross-platform build process.
+* **(Executable)**
+
+* This executable is used to automate the generation of Makefiles,
+ check for dependencies, and is the backbone of libopenshot’s cross-platform build process.
#### SWIG (`swig`)
- * http://www.swig.org/ **(Executable)**
- * This executable is used to generate the Python and Ruby bindings for libopenshot. It is a simple and powerful wrapper for C++ libraries, and supports many languages.
+* **(Executable)**
+
+* This executable is used to generate the Python and Ruby bindings for libopenshot.
+ It is a powerful wrapper for C++ libraries, and supports many languages.
#### Python 3 (libpython)
- * http://www.python.org/ **(Executable and Library)**
- * This library is used by swig to create the Python (version 3+) bindings for libopenshot. This is also the official language used by OpenShot Video Editor (a graphical interface to libopenshot).
+* **(Executable and Library)**
+
+* This library is used by swig to create the Python (version 3+) bindings for libopenshot.
+ This is also the official language used by OpenShot Video Editor (a graphical interface to libopenshot).
#### Doxygen (doxygen)
- * http://www.stack.nl/~dimitri/doxygen/ **(Executable)**
- * This executable is used to auto-generate the documentation used by libopenshot.
+* **(Executable)**
+* This executable is used to auto-generate the documentation used by libopenshot.
#### UnitTest++ (libunittest++)
- * https://github.com/unittest-cpp/ **(Library)**
- * This library is used to execute unit tests for libopenshot. It contains many macros used to keep our unit testing code very clean and simple.
+* **(Library)**
+
+* This library is used to execute unit tests for libopenshot.
+ It contains many macros used to keep our unit testing code clean and easy-to-follow.
## Obtaining Source Code
-The first step in installing libopenshot is to obtain the most recent source code. The source code is available on [GitHub](https://github.com/OpenShot/libopenshot). Use the following command to obtain the latest libopenshot source code.
+The first step in installing libopenshot is to obtain the most recent source code.
+The source code is available on [GitHub](https://github.com/OpenShot/libopenshot).
+Use the following command to obtain the latest libopenshot source code.
-```
+```sh
git clone https://github.com/OpenShot/libopenshot.git
git clone https://github.com/OpenShot/libopenshot-audio.git
```
@@ -79,27 +105,36 @@ git clone https://github.com/OpenShot/libopenshot-audio.git
The source code is divided up into the following folders.
#### `build/`
-This folder needs to be manually created, and is used by cmake to store the temporary build files, such as makefiles, as well as the final binaries (library and test executables).
+This folder needs to be manually created,
+and is used by cmake to store the build system control files and generated output
+(such as compiled object files and the result of template-file processing)
+as well as the final results of the build (library, tool, and test program binaries).
#### `cmake/`
This folder contains custom modules not included by default in cmake.
-CMake find modules are used to discover dependency libraries on the system, and to incorporate their headers and object files.
+CMake find modules are used to discover dependency libraries on the system,
+and to incorporate their headers and object files.
CMake code modules are used to implement build features such as test coverage scanning.
#### `doc/`
This folder contains documentation and related files.
This includes logos and images required by the doxygen-generated API documentation.
-#### `include/`
-This folder contains all headers (*.h) used by libopenshot.
-
#### `src/`
-This folder contains all source code (*.cpp) used by libopenshot.
+This folder contains all source code (`*.cpp`) and headers (`*.h`) for libopenshot.
+
+#### `bindings/`
+This folder contains language bindings for the libopenshot API.
+Current supported languages are Python and Ruby.
+
+#### `examples/`
+This folder contains various pieces of example code written in C++, Ruby, or Python.
+It also holds the media files (data files) used in examples and unit tests.
#### `tests/`
This folder contains all unit test code.
Each test file (`_Tests.cpp`) contains the tests for the named class.
-We use UnitTest++ macros to keep the test code simple and manageable.
+We use UnitTest++ macros to keep the test code uncomplicated and manageable.
#### `thirdparty/`
This folder contains code not written by the OpenShot team.
@@ -113,79 +148,91 @@ locates header files and libraries, and generates a build system in various form
We use CMake's Makefile generators to compile libopenshot and libopenshot-audio.
CMake uses an out-of-source build concept.
-This means that the build system, all temporary files, and all generated products are kept separate from the source code.
+This means that the build system,
+along with all temporary/intermediate files and generated products output during the build,
+is kept strictly separate from the source code.
This includes Makefiles, object files, and even the final binaries.
-While it is possible to build in-tree, we highly recommend you use a `/build/` sub-folder to compile each library.
-This prevents the build process from cluttering up the source
-code.
-These instructions have only been tested with the GNU compiler suite (including MSYS2/MinGW for Windows), and the Clang compiler (including AppleClang on MacOS).
+The source code files and directories are never modified by the build system,
+which makes it convenient and safe to re-run, reconfigure, or discard builds as needed.
+While it is possible to build in-tree,
+we highly recommend you use a `/build/` sub-folder to compile each library.
-## CMake Flags (Optional)
-There are many different build flags that can be passed to cmake to adjust how libopenshot is compiled. Some of these flags might be required when compiling on certain OSes, just depending on how your build environment is setup.
+These instructions have only been tested with the following compiler stacks:
+* The GNU compiler suite (including MSYS2/MinGW for Windows)
+* The Clang compiler (including AppleClang on MacOS)
+
+Other compilers, including MSVC, are entirely unsupported.
+It may be possible to build libopenshot using other compiler stacks,
+but most likely not without modifications to the build system which you would have to make yourself.
+
+### CMake Flags (Optional)
+There are many different build flags that can be passed to cmake to adjust how libopenshot is compiled.
+Some of these flags might be required when compiling on certain OSes,
+depending on how your build environment is setup.
To add a build flag, follow this general syntax:
```sh
-$ cmake -DMAGICKCORE_HDRI_ENABLE=1 -DENABLE_TESTS=1 ..
+cmake -B build -S . -DMAGICKCORE_HDRI_ENABLE=1 -DENABLE_TESTS=1 ...
```
Following are some of the flags you might need to set when generating your build system.
-##### Optional behavior:
-* `-DENABLE_TESTS=0` (default: `ON`)
-* `-DENABLE_COVERAGE=1` (default: `OFF`)
-* `-DENABLE_DOCS=0` (default: `ON` if doxygen found)
+#### Optional behaviors of the build system
+* `-DENABLE_TESTS=0` (default: `ON`)
+* `-DENABLE_COVERAGE=1` (default: `OFF`)
+* `-DENABLE_DOCS=0` (default: `ON` if doxygen found)
+* `-DENABLE_RUBY=0` (default: `ON` if SWIG and Ruby detected)
+* `-DENABLE_PYTHON=0` (default: `ON` if SWIG and Python detected)
-##### Compiler configuration:
-* `-DCMAKE_BUILD_TYPE=Release`, `-DCMAKE_BUILD_TYPE=Debug` (default: `Debug` if unset)
-* `-DCMAKE_CXX_FLAGS="-Wall -Wextra"` (default: CMake builtin defaults for build type)
-* `-DCMAKE_CXX_COMPILER=/path/to/g++`, `-DCMAKE_CXX_COMPILER=/path/to/clang++`
-* `-DCMAKE_C_COMPILER=/path/to/gcc`, `-DCMAKE_CXX_COMPILER=/path/to/clang` (used by CMake for OS probes)
+#### Options to configure the compiler
+* `-DCMAKE_BUILD_TYPE=Release`, `-DCMAKE_BUILD_TYPE=Debug` (default: `Release` if unset)
+* `-DCMAKE_CXX_FLAGS="-Wall -Wextra"` (default: CMake builtin defaults for build type)
+* `-DCMAKE_CXX_COMPILER=/path/to/g++`, `-DCMAKE_CXX_COMPILER=/path/to/clang++`
+* `-DCMAKE_C_COMPILER=/path/to/gcc`, `-DCMAKE_CXX_COMPILER=/path/to/clang` (used by CMake for OS probes)
-##### Dependency configuration:
-* `-DCMAKE_PREFIX_PATH=/extra/path/to/search/for/libraries/`
-* `-DUSE_SYSTEM_JSONCPP=0` (default: auto if discovered)
-* `-DImageMagick_FOUND=0` (default: auto if discovered)
+#### Options to configure dependencies
+* `-DCMAKE_PREFIX_PATH=/extra/path/to/search/for/libraries/`
+* `-DUSE_SYSTEM_JSONCPP=0` (default: auto if discovered)
+* `-DENABLE_MAGICK=0` (default: auto if discovered)
-##### To compile bindings for a specific Python installation:
-* `-DPYTHON_INCLUDE_DIR=/location/of/python/includes/`
-* `-DPYTHON_LIBRARY=/location/of/libpython*.so`
-* `-DPYTHON_FRAMEWORKS=/usr/local/Cellar/python3/3.3.2/Frameworks/Python.framework/` (MacOS only)
+#### Options to compile bindings for a specific Python installation
+* `-DPYTHON_INCLUDE_DIR=/location/of/python/includes/`
+* `-DPYTHON_LIBRARY=/location/of/libpython*.so`
+* `-DPYTHON_FRAMEWORKS=/usr/local/Cellar/python3/3.3.2/Frameworks/Python.framework/` (MacOS only)
-##### Only used when building with ImageMagick enabled:
-* `-DMAGICKCORE_HDRI_ENABLE=1` (default `0`)
-* `-DMAGICKCORE_QUANTUM_DEPTH=8` (default `16`)
+#### Options only relevant when building with ImageMagick
+* `-DMAGICKCORE_HDRI_ENABLE=1` (default `0`)
+* `-DMAGICKCORE_QUANTUM_DEPTH=8` (default `16`)
## Linux Build Instructions (libopenshot-audio)
-To compile libopenshot-audio, we need to go through a few additional steps to manually build and install it. Launch a terminal and enter:
+To compile libopenshot-audio, we need to build it from source code and install the results.
+Launch a terminal and enter:
-```
+```sh
cd [libopenshot-audio repo folder]
-mkdir build
-cd build
-cmake ../
-make
-make install
-./src/openshot-audio-test-sound (This should play a test sound)
+cmake -B build -S .
+cmake --build build
+./build/src/openshot-audio-test-sound (This should play a test sound)
+cmake --install build
```
## Linux Build Instructions (libopenshot)
Run the following commands to compile libopenshot:
-```
+```sh
cd [libopenshot repo directory]
-mkdir -p build
-cd build
-cmake ../
-make
-make install
+cmake -B build -S .
+cmake --build build
+cmake --build build --target test
+cmake --install build
```
For more detailed instructions, please see:
-* [doc/INSTALL-LINUX.md][INSTALL-LINUX]
-* [doc/INSTALL-MAC.md][INSTALL-MAC]
-* [doc/INSTALL-WINDOWS.md][INSTALL-WINDOWS]
+* [doc/INSTALL-LINUX.md][INSTALL-LINUX]
+* [doc/INSTALL-MAC.md][INSTALL-MAC]
+* [doc/INSTALL-WINDOWS.md][INSTALL-WINDOWS]
[INSTALL-LINUX]: https://github.com/OpenShot/libopenshot/blob/develop/doc/INSTALL-LINUX.md
[INSTALL-MAC]: https://github.com/OpenShot/libopenshot/blob/develop/doc/INSTALL-MAC.md
diff --git a/README.md b/README.md
index cf69c1cf..6105915d 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-OpenShot Video Library (libopenshot) is a free, open-source C++ library dedicated to
-delivering high quality video editing, animation, and playback solutions to the
-world.
+OpenShot Video Library (libopenshot) is a free, open-source C++ library
+dedicated to delivering high quality video editing, animation, and playback
+solutions to the world.
## Build Status
@@ -15,7 +15,8 @@ world.
* Time Mapping (Curve-based Slow Down, Speed Up, Reverse)
* Audio Mixing & Resampling (Curve-based)
* Audio Plug-ins (VST & AU)
-* Audio Drivers (ASIO, WASAPI, DirectSound, CoreAudio, iPhone Audio, ALSA, JACK, and Android)
+* Audio Drivers (ASIO, WASAPI, DirectSound, CoreAudio, iPhone Audio,
+ ALSA, JACK, and Android)
* Telecine and Inverse Telecine (Film to TV, TV to Film)
* Frame Rate Conversions
* Multi-Processor Support (Performance)
@@ -27,8 +28,8 @@ world.
## Install
-Detailed instructions for building libopenshot and libopenshot-audio for each OS. These instructions
-are also available in the /docs/ source folder.
+Detailed instructions for building libopenshot and libopenshot-audio for
+each OS. These instructions are also available in the `/docs/` source folder.
* [Linux](https://github.com/OpenShot/libopenshot/wiki/Linux-Build-Instructions)
* [Mac](https://github.com/OpenShot/libopenshot/wiki/Mac-Build-Instructions)
@@ -36,10 +37,12 @@ are also available in the /docs/ source folder.
## Hardware Acceleration
-OpenShot now supports experimental hardware acceleration, both for encoding and
-decoding videos. When enabled, this can either speed up those operations or slow
-them down, depending on the power and features supported by your graphics card.
-Please see [doc/HW-ACCELL.md](doc/HW-ACCEL.md) for more information.
+OpenShot now supports experimental hardware acceleration, both for encoding
+and decoding videos. When enabled, this can either speed up those operations
+or slow them down, depending on the power and features supported by your
+graphics card.
+
+Please see [`doc/HW-ACCEL.md`](doc/HW-ACCEL.md) for more information.
## Documentation
@@ -51,10 +54,11 @@ make doc
## Developers
-Are you interested in becoming more involved in the development of
-OpenShot? Build exciting new features, fix bugs, make friends, and become a hero!
-Please read the [step-by-step](https://github.com/OpenShot/openshot-qt/wiki/Become-a-Developer)
-instructions for getting source code, configuring dependencies, and building OpenShot.
+Are you interested in becoming more involved in the development of OpenShot?
+Build exciting new features, fix bugs, make friends, and become a hero!
+Please read the [step-by-step](https://github.com/OpenShot/openshot-qt/wiki/Become-a-Developer)
+instructions for getting source code, configuring dependencies, and building
+OpenShot.
## Report a bug
@@ -72,7 +76,7 @@ https://github.com/OpenShot/libopenshot/issues
### License
-Copyright (c) 2008-2019 OpenShot Studios, LLC.
+Copyright (c) 2008-2020 OpenShot Studios, LLC.
OpenShot Library (libopenshot) is free software: you can redistribute it
and/or modify it under the terms of the GNU Lesser General Public License
diff --git a/src/bindings/CMakeLists.txt b/bindings/CMakeLists.txt
similarity index 100%
rename from src/bindings/CMakeLists.txt
rename to bindings/CMakeLists.txt
diff --git a/src/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt
similarity index 97%
rename from src/bindings/python/CMakeLists.txt
rename to bindings/python/CMakeLists.txt
index 6d7ce79e..b020592b 100644
--- a/src/bindings/python/CMakeLists.txt
+++ b/bindings/python/CMakeLists.txt
@@ -50,11 +50,11 @@ include_directories(${PYTHON_INCLUDE_PATH})
if (CMAKE_VERSION VERSION_LESS 3.12)
### Include project headers
include_directories(
- "${PROJECT_SOURCE_DIR}/include"
- "${PROJECT_BINARY_DIR}/include")
+ "${PROJECT_SOURCE_DIR}/src"
+ "${PROJECT_BINARY_DIR}/src")
endif()
-### Enable C++ support in SWIG
+### Enable C++ in SWIG
set_property(SOURCE openshot.i PROPERTY CPLUSPLUS ON)
set_property(SOURCE openshot.i PROPERTY SWIG_MODULE_NAME openshot)
diff --git a/src/bindings/python/openshot.i b/bindings/python/openshot.i
similarity index 99%
rename from src/bindings/python/openshot.i
rename to bindings/python/openshot.i
index 53e514c1..e74b6494 100644
--- a/src/bindings/python/openshot.i
+++ b/bindings/python/openshot.i
@@ -237,5 +237,5 @@
%template(PointsVector) std::vector;
%template(FieldVector) std::vector;
%template(MappedFrameVector) std::vector;
-%template(MappedMetadata) std::map;
+%template(MetadataMap) std::map;
%template(AudioDeviceInfoVector) std::vector;
diff --git a/src/bindings/ruby/CMakeLists.txt b/bindings/ruby/CMakeLists.txt
similarity index 82%
rename from src/bindings/ruby/CMakeLists.txt
rename to bindings/ruby/CMakeLists.txt
index 4d7b922a..72d1a985 100644
--- a/src/bindings/ruby/CMakeLists.txt
+++ b/bindings/ruby/CMakeLists.txt
@@ -46,15 +46,17 @@ endif()
option(SILENCE_RUBY_VERSION_WARNING
"Don't warn about possible SWIG incompatibilities with Ruby 2.7.0+" OFF)
-if (${RUBY_VERSION} VERSION_GREATER 2.6.9 AND ${SWIG_VERSION} VERSION_LESS 4.0.3)
+if (${RUBY_VERSION} VERSION_GREATER 2.6.9 AND ${SWIG_VERSION} VERSION_LESS 4.0.2)
if (NOT ${SILENCE_RUBY_VERSION_WARNING})
- message(WARNING "
+ message(WARNING "\
Ruby 2.7.0+ detected, building the libopenshot Ruby API bindings \
-requires a pre-release version of SWIG 4.0.3 with this commit: \
-https://github.com/swig/swig/commit/5542cc228ad10bdc5c91107afb77c808c43bf2a4")
- message(STATUS "
-To disable this warning, add -DSILENCE_RUBY_VERSION_WARNING:BOOL=1 to the cmake \
-command line, or enable the option in the CMake GUI.")
+requires either SWIG 4.0.2+ or an older version patched with this commit: \
+https://github.com/swig/swig/commit/5542cc228ad10bdc5c91107afb77c808c43bf2a4 \
+(Recent Fedora and Ubuntu distro packages of SWIG 4.0.1 have already been \
+patched.)")
+ message(STATUS "To disable the previous warning, add \
+-DSILENCE_RUBY_VERSION_WARNING:BOOL=1 to the cmake command line, \
+or enable the option in the CMake GUI.")
endif()
endif()
@@ -62,19 +64,16 @@ endif()
include_directories(${RUBY_INCLUDE_DIRS})
if (CMAKE_VERSION VERSION_LESS 3.12)
- ### Include Ruby header files and project headers
+ ### Include project headers
include_directories(
- "${PROJECT_SOURCE_DIR}/include"
- "${PROJECT_BINARY_DIR}/include")
+ "${PROJECT_SOURCE_DIR}/src"
+ "${PROJECT_BINARY_DIR}/src")
endif()
### Enable C++ in SWIG
set_property(SOURCE openshot.i PROPERTY CPLUSPLUS ON)
set_property(SOURCE openshot.i PROPERTY SWIG_MODULE_NAME openshot)
-### Unbreak std::isfinite()
-add_definitions(-DHAVE_ISFINITE=1)
-
### Suppress a ton of warnings in the generated SWIG C++ code
set(SWIG_CXX_FLAGS "-Wno-unused-variable -Wno-unused-function \
-Wno-deprecated-copy -Wno-class-memaccess -Wno-cast-function-type \
@@ -97,6 +96,14 @@ else()
swig_add_library(rbopenshot LANGUAGE ruby SOURCES openshot.i)
endif()
+### Set name of target (with no prefix, since Ruby does not like that)
+# XXX: If this is not done exactly this way, the module builds as
+# e.g. rbopenshot.so, but its initializer method will be named
+# 'Init_openshot()' (via the module name set in the SWIG .i file).
+# Which leads to Ruby barfing when it attempts to load the module.
+set_target_properties(${SWIG_MODULE_rbopenshot_REAL_NAME} PROPERTIES
+ PREFIX "" OUTPUT_NAME "openshot")
+
### Link the new Ruby wrapper library with libopenshot
target_link_libraries(${SWIG_MODULE_rbopenshot_REAL_NAME} PUBLIC
${RUBY_LIBRARY} openshot)
diff --git a/src/bindings/ruby/openshot.i b/bindings/ruby/openshot.i
similarity index 94%
rename from src/bindings/ruby/openshot.i
rename to bindings/ruby/openshot.i
index 2f24d220..b27510e2 100644
--- a/src/bindings/ruby/openshot.i
+++ b/bindings/ruby/openshot.i
@@ -42,23 +42,20 @@
%include "std_list.i"
%include "std_vector.i"
%include "std_map.i"
+%include
/* Unhandled STL Exception Handling */
%include
-namespace std {
- template class shared_ptr {
- public:
- T *operator->();
- };
-}
+/* Include shared pointer code */
+%include
/* Mark these classes as shared_ptr classes */
#ifdef USE_IMAGEMAGICK
- %template(SPtrImage) std::shared_ptr;
+ %shared_ptr(Magick::Image)
#endif
-%template(SPtrAudioBuffer) std::shared_ptr;
-%template(SPtrOpenFrame) std::shared_ptr;
+%shared_ptr(juce::AudioSampleBuffer)
+%shared_ptr(openshot::Frame)
%{
/* Ruby and FFmpeg define competing RSHIFT macros,
@@ -227,5 +224,5 @@ namespace std {
%template(PointsVector) std::vector;
%template(FieldVector) std::vector;
%template(MappedFrameVector) std::vector;
-%template(MappedMetadata) std::map;
+%template(MetadataMap) std::map;
%template(AudioDeviceInfoVector) std::vector;
diff --git a/cmake/Modules/FindOpenShotAudio.cmake b/cmake/Modules/FindOpenShotAudio.cmake
index a0cb9200..4716112e 100644
--- a/cmake/Modules/FindOpenShotAudio.cmake
+++ b/cmake/Modules/FindOpenShotAudio.cmake
@@ -11,12 +11,14 @@ endif()
# Find the libopenshot-audio header files (check env/cache vars first)
find_path(
- LIBOPENSHOT_AUDIO_INCLUDE_DIR
+ OpenShotAudio_INCLUDE_DIR
JuceHeader.h
HINTS
ENV LIBOPENSHOT_AUDIO_DIR
PATHS
${LIBOPENSHOT_AUDIO_DIR}
+ ${OpenShotAudio_ROOT}
+ ${OpenShotAudio_INCLUDE_DIR}
PATH_SUFFIXES
include/libopenshot-audio
libopenshot-audio
@@ -26,12 +28,14 @@ find_path(
# Find the libopenshot-audio header files (fallback to std. paths)
find_path(
- LIBOPENSHOT_AUDIO_INCLUDE_DIR
+ OpenShotAudio_INCLUDE_DIR
JuceHeader.h
HINTS
ENV LIBOPENSHOT_AUDIO_DIR
PATHS
${LIBOPENSHOT_AUDIO_DIR}
+ ${OpenShotAudio_ROOT}
+ ${OpenShotAudio_INCLUDE_DIR}
PATH_SUFFIXES
include/libopenshot-audio
libopenshot-audio
@@ -40,7 +44,7 @@ find_path(
# Find libopenshot-audio.so / libopenshot-audio.dll (check env/cache vars first)
find_library(
- LIBOPENSHOT_AUDIO_LIBRARY
+ OpenShotAudio_LIBRARY
NAMES
libopenshot-audio
openshot-audio
@@ -48,6 +52,8 @@ find_library(
ENV LIBOPENSHOT_AUDIO_DIR
PATHS
${LIBOPENSHOT_AUDIO_DIR}
+ ${OpenShotAudio_ROOT}
+ ${OpenShotAudio_LIBRARY}
PATH_SUFFIXES
lib/libopenshot-audio
libopenshot-audio
@@ -57,7 +63,7 @@ find_library(
# Find libopenshot-audio.so / libopenshot-audio.dll (fallback)
find_library(
- LIBOPENSHOT_AUDIO_LIBRARY
+ OpenShotAudio_LIBRARY
NAMES
libopenshot-audio
openshot-audio
@@ -65,39 +71,41 @@ find_library(
ENV LIBOPENSHOT_AUDIO_DIR
PATHS
${LIBOPENSHOT_AUDIO_DIR}
+ ${OpenShotAudio_ROOT}
+ ${OpenShotAudio_LIBRARY}
PATH_SUFFIXES
lib/libopenshot-audio
libopenshot-audio
lib
)
-set(LIBOPENSHOT_AUDIO_LIBRARIES "${LIBOPENSHOT_AUDIO_LIBRARY}")
-set(LIBOPENSHOT_AUDIO_LIBRARY "${LIBOPENSHOT_AUDIO_LIBRARIES}")
-set(LIBOPENSHOT_AUDIO_INCLUDE_DIRS "${LIBOPENSHOT_AUDIO_INCLUDE_DIR}")
+set(OpenShotAudio_LIBRARIES "${OpenShotAudio_LIBRARY}")
+set(OpenShotAudio_LIBRARY "${OpenShotAudio_LIBRARIES}")
+set(OpenShotAudio_INCLUDE_DIRS "${OpenShotAudio_INCLUDE_DIR}")
-if(LIBOPENSHOT_AUDIO_INCLUDE_DIR AND EXISTS "${LIBOPENSHOT_AUDIO_INCLUDE_DIR}/JuceHeader.h")
- file(STRINGS "${LIBOPENSHOT_AUDIO_INCLUDE_DIR}/JuceHeader.h" libosa_version_str
+if(OpenShotAudio_INCLUDE_DIR AND EXISTS "${OpenShotAudio_INCLUDE_DIR}/JuceHeader.h")
+ file(STRINGS "${OpenShotAudio_INCLUDE_DIR}/JuceHeader.h" libosa_version_str
REGEX "versionString.*=.*\"[^\"]+\"")
if(libosa_version_str MATCHES "versionString.*=.*\"([^\"]+)\"")
- set(LIBOPENSHOT_AUDIO_VERSION_STRING ${CMAKE_MATCH_1})
+ set(OpenShotAudio_VERSION_STRING ${CMAKE_MATCH_1})
endif()
unset(libosa_version_str)
string(REGEX REPLACE "^([0-9]+\.[0-9]+\.[0-9]+).*$" "\\1"
- LIBOPENSHOT_AUDIO_VERSION "${LIBOPENSHOT_AUDIO_VERSION_STRING}")
+ OpenShotAudio_VERSION "${OpenShotAudio_VERSION_STRING}")
endif()
# If we couldn't parse M.N.B version, don't keep any of it
-if(NOT LIBOPENSHOT_AUDIO_VERSION)
- unset(LIBOPENSHOT_AUDIO_VERSION)
- unset(LIBOPENSHOT_AUDIO_VERSION_STRING)
+if(NOT OpenShotAudio_VERSION)
+ unset(OpenShotAudio_VERSION)
+ unset(OpenShotAudio_VERSION_STRING)
endif()
# Determine compatibility with requested version in find_package()
-if(OpenShotAudio_FIND_VERSION AND LIBOPENSHOT_AUDIO_VERSION)
- if("${OpenShotAudio_FIND_VERSION}" STREQUAL "${LIBOPENSHOT_AUDIO_VERSION}")
+if(OpenShotAudio_FIND_VERSION AND OpenShotAudio_VERSION)
+ if("${OpenShotAudio_FIND_VERSION}" STREQUAL "${OpenShotAudio_VERSION}")
set(OpenShotAudio_VERSION_EXACT TRUE)
endif()
- if("${OpenShotAudio_FIND_VERSION}" VERSION_GREATER "${LIBOPENSHOT_AUDIO_VERSION}")
+ if("${OpenShotAudio_FIND_VERSION}" VERSION_GREATER "${OpenShotAudio_VERSION}")
set(OpenShotAudio_VERSION_COMPATIBLE FALSE)
else()
set(OpenShotAudio_VERSION_COMPATIBLE TRUE)
@@ -105,12 +113,76 @@ if(OpenShotAudio_FIND_VERSION AND LIBOPENSHOT_AUDIO_VERSION)
endif()
include(FindPackageHandleStandardArgs)
-# handle the QUIETLY and REQUIRED arguments and set LIBOPENSHOT_AUDIO_FOUND to TRUE
+# handle the QUIETLY and REQUIRED arguments and set OpenShotAudio_FOUND to TRUE
# if all listed variables are TRUE
find_package_handle_standard_args(OpenShotAudio
REQUIRED_VARS
- LIBOPENSHOT_AUDIO_LIBRARY
- LIBOPENSHOT_AUDIO_INCLUDE_DIRS
+ OpenShotAudio_LIBRARIES
+ OpenShotAudio_INCLUDE_DIRS
VERSION_VAR
- LIBOPENSHOT_AUDIO_VERSION_STRING
+ OpenShotAudio_VERSION_STRING
)
+
+if(OpenShotAudio_FOUND)
+ set(OpenShotAudio_INCLUDE_DIRS "${OpenShotAudio_INCLUDE_DIRS}"
+ CACHE PATH "The paths to libopenshot-audio's header files" FORCE)
+ set(OpenShotAudio_LIBRARIES "${OpenShotAudio_LIBRARIES}"
+ CACHE STRING "The libopenshot-audio library to link with" FORCE)
+ if(DEFINED OpenShotAudio_VERSION)
+ set(OpenShotAudio_VERSION ${OpenShotAudio_VERSION}
+ CACHE STRING "The version of libopenshot-audio detected" FORCE)
+ endif()
+endif()
+
+if(OpenShotAudio_FOUND AND NOT TARGET OpenShot::Audio)
+ message(STATUS "Creating IMPORTED target OpenShot::Audio")
+ if(WIN32)
+ add_library(OpenShot::Audio UNKNOWN IMPORTED)
+ else()
+ add_library(OpenShot::Audio SHARED IMPORTED)
+ endif()
+
+ set_property(TARGET OpenShot::Audio APPEND PROPERTY
+ INTERFACE_INCLUDE_DIRECTORIES "${OpenShotAudio_INCLUDE_DIRS}")
+
+ # Juce requires either DEBUG or NDEBUG to be defined on MacOS.
+ # -DNDEBUG is set by cmake for all release configs, so add
+ # -DDEBUG for debug builds. We'll do this for all OSes, even
+ # though only MacOS requires it.
+ # The generator expression translates to:
+ # CONFIG == "DEBUG" ? "DEBUG" : ""
+ set_property(TARGET OpenShot::Audio APPEND PROPERTY
+ INTERFACE_COMPILE_DEFINITIONS $<$:DEBUG>)
+
+ # For the Ruby bindings
+ set_property(TARGET OpenShot::Audio APPEND PROPERTY
+ INTERFACE_COMPILE_DEFINITIONS HAVE_ISFINITE=1)
+
+ if(WIN32)
+ set_property(TARGET OpenShot::Audio APPEND PROPERTY
+ INTERFACE_COMPILE_DEFINITIONS IGNORE_JUCE_HYPOT=1)
+ set_property(TARGET OpenShot::Audio APPEND PROPERTY
+ INTERFACE_COMPILE_OPTIONS -include cmath)
+ elseif(APPLE)
+ # Prevent compiling with __cxx11
+ set_property(TARGET OpenShot::Audio APPEND PROPERTY
+ INTERFACE_COMPILE_DEFINITIONS _GLIBCXX_USE_CXX11_ABI=0)
+ list(APPEND framework_deps
+ "-framework Carbon"
+ "-framework Cocoa"
+ "-framework CoreFoundation"
+ "-framework CoreAudio"
+ "-framework CoreMidi"
+ "-framework IOKit"
+ "-framework AGL"
+ "-framework AudioToolbox"
+ "-framework QuartzCore"
+ "-lobjc"
+ "-framework Accelerate"
+ )
+ target_link_libraries(OpenShot::Audio INTERFACE ${framework_deps})
+ endif()
+
+ set_property(TARGET OpenShot::Audio APPEND PROPERTY
+ IMPORTED_LOCATION "${OpenShotAudio_LIBRARIES}")
+endif()
diff --git a/cmake/Modules/FindRESVG.cmake b/cmake/Modules/FindResvg.cmake
similarity index 59%
rename from cmake/Modules/FindRESVG.cmake
rename to cmake/Modules/FindResvg.cmake
index 0538eacd..452a81c8 100644
--- a/cmake/Modules/FindRESVG.cmake
+++ b/cmake/Modules/FindResvg.cmake
@@ -1,13 +1,13 @@
# vim: ts=2 sw=2
#[=======================================================================[.rst:
-FindRESVG
+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
+This module defines :prop_tgt:`IMPORTED` target ``Resvg::Resvg`` when
the library and headers are found.
Result Variables
@@ -17,10 +17,10 @@ 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)
+ 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
^^^^^^^^^^^^^^^^^^^^^^^
@@ -47,7 +47,7 @@ if(DEFINED RESVGDIR AND CMAKE_VERSION VERSION_GREATER 3.4)
BASE_DIR ${CMAKE_CURRENT_BINARY_DIR})
endif()
-find_path(RESVG_INCLUDE_DIRS
+find_path(Resvg_INCLUDE_DIRS
ResvgQt.h
PATHS
${RESVGDIR}
@@ -62,7 +62,7 @@ find_path(RESVG_INCLUDE_DIRS
resvg/capi/include
)
-find_library(RESVG_LIBRARIES
+find_library(Resvg_LIBRARIES
NAMES resvg
PATHS
${RESVGDIR}
@@ -77,39 +77,39 @@ find_library(RESVG_LIBRARIES
resvg/target/release
)
-if (RESVG_INCLUDE_DIRS AND RESVG_LIBRARIES)
- set(RESVG_FOUND TRUE)
+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")
+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)
+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 )
+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(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)
+ 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
+ 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_INCLUDE_DIRECTORIES "${Resvg_INCLUDE_DIRS}")
- set_property(TARGET RESVG::resvg APPEND PROPERTY
- INTERFACE_COMPILE_DEFINITIONS "${RESVG_DEFINITIONS}")
+ set_property(TARGET Resvg::Resvg APPEND PROPERTY
+ INTERFACE_COMPILE_DEFINITIONS "${Resvg_DEFINITIONS}")
- set_property(TARGET RESVG::resvg APPEND PROPERTY
- IMPORTED_LOCATION "${RESVG_LIBRARIES}")
+ set_property(TARGET Resvg::Resvg APPEND PROPERTY
+ IMPORTED_LOCATION "${Resvg_LIBRARIES}")
endif()
diff --git a/cmake/Modules/FindZMQ.cmake b/cmake/Modules/FindZMQ.cmake
deleted file mode 100644
index 50d27bc5..00000000
--- a/cmake/Modules/FindZMQ.cmake
+++ /dev/null
@@ -1,24 +0,0 @@
-# - Try to find ZMQ
-# Once done this will define
-# ZMQ_FOUND - System has ZMQ
-# ZMQ_INCLUDE_DIRS - The ZMQ include directories
-# ZMQ_LIBRARIES - The libraries needed to use ZMQ
-# ZMQ_DEFINITIONS - Compiler switches required for using ZMQ
-
-find_path ( ZMQ_INCLUDE_DIR zmq.h
- PATHS /usr/include/
- /usr/local/include/
- $ENV{ZMQDIR}/include/ )
-
-find_library ( ZMQ_LIBRARY NAMES zmq
- PATHS /usr/lib/
- /usr/local/lib/
- $ENV{ZMQDIR}/lib/ )
-
-set ( ZMQ_LIBRARIES ${ZMQ_LIBRARY} )
-set ( ZMQ_INCLUDE_DIRS ${ZMQ_INCLUDE_DIR} )
-
-include ( FindPackageHandleStandardArgs )
-# handle the QUIETLY and REQUIRED arguments and set ZMQ_FOUND to TRUE
-# if all listed variables are TRUE
-find_package_handle_standard_args ( ZMQ DEFAULT_MSG ZMQ_LIBRARY ZMQ_INCLUDE_DIR )
\ No newline at end of file
diff --git a/codecov.yml b/codecov.yml
index ee151daa..e00103e7 100644
--- a/codecov.yml
+++ b/codecov.yml
@@ -7,10 +7,11 @@ coverage:
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/jsoncpp/*.cpp"
- - "/thirdparty/jsoncpp/json/*.h"
+ - "/examples"
+ - "/bindings"
+ - "/thirdparty/jsoncpp"
- "/doc"
- "/cmake"
- "/*.md"
+ - "bindings"
+ - "src/openshot_autogen"
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
new file mode 100644
index 00000000..87475128
--- /dev/null
+++ b/examples/CMakeLists.txt
@@ -0,0 +1,65 @@
+####################### CMakeLists.txt (libopenshot) #########################
+# @brief CMake build file for libopenshot (used to generate makefiles)
+# @author Jonathan Thomas
+# @author FeRD (Frank Dana)
+#
+# @section LICENSE
+#
+# Copyright (c) 2008-2020 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(GNUInstallDirs)
+
+# Dependencies
+find_package(Qt5 COMPONENTS Gui REQUIRED)
+
+############### CLI EXECUTABLES ################
+# Create test executable
+add_executable(openshot-example Example.cpp)
+
+# Define path to test input files
+file(TO_NATIVE_PATH "${PROJECT_SOURCE_DIR}/examples/" TEST_MEDIA_PATH)
+target_compile_definitions(openshot-example PRIVATE
+ -DTEST_MEDIA_PATH="${TEST_MEDIA_PATH}" )
+
+# Link test executable to the new library
+target_link_libraries(openshot-example openshot)
+
+add_executable(openshot-html-test ExampleHtml.cpp)
+target_link_libraries(openshot-html-test openshot Qt5::Gui)
+
+############### PLAYER EXECUTABLE ################
+# Create test executable
+add_executable(openshot-player qt-demo/main.cpp)
+
+set_target_properties(openshot-player PROPERTIES AUTOMOC ON)
+
+# Link test executable to the new library
+target_link_libraries(openshot-player openshot)
+
+############### TEST BLACKMAGIC CAPTURE APP ################
+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()
diff --git a/src/examples/Example.cpp b/examples/Example.cpp
similarity index 97%
rename from src/examples/Example.cpp
rename to examples/Example.cpp
index c9e61628..2cd77071 100644
--- a/src/examples/Example.cpp
+++ b/examples/Example.cpp
@@ -31,8 +31,8 @@
#include
#include
#include
-#include "../../include/OpenShot.h"
-#include "../../include/CrashHandler.h"
+#include "OpenShot.h"
+#include "CrashHandler.h"
using namespace openshot;
diff --git a/src/examples/Example.py b/examples/Example.py
similarity index 100%
rename from src/examples/Example.py
rename to examples/Example.py
diff --git a/src/examples/Example.rb b/examples/Example.rb
similarity index 66%
rename from src/examples/Example.rb
rename to examples/Example.rb
index 7d2e4e1a..5e4c7940 100644
--- a/src/examples/Example.rb
+++ b/examples/Example.rb
@@ -1,13 +1,13 @@
# Find and load the ruby libopenshot wrapper library
-require "./openshot"
+require "openshot"
# Create a new FFmpegReader and Open it
-r = OpenShot::FFmpegReader.new("myfile.mp4")
+r = Openshot::FFmpegReader.new("test.mp4")
r.Open()
# Get frame 1
f = r.GetFrame(1)
# Display the frame
-r.Display()
+f.Display()
diff --git a/src/examples/ExampleBlackmagic.cpp b/examples/ExampleBlackmagic.cpp
similarity index 100%
rename from src/examples/ExampleBlackmagic.cpp
rename to examples/ExampleBlackmagic.cpp
diff --git a/src/examples/ExampleHtml.cpp b/examples/ExampleHtml.cpp
similarity index 97%
rename from src/examples/ExampleHtml.cpp
rename to examples/ExampleHtml.cpp
index f315e252..928a05fe 100644
--- a/src/examples/ExampleHtml.cpp
+++ b/examples/ExampleHtml.cpp
@@ -32,11 +32,12 @@
#include
#include
#include
-#include "../../include/OpenShot.h"
-//#include "../../include/CrashHandler.h"
#include
#include
+#include "OpenShot.h"
+#include "CrashHandler.h"
+
using namespace openshot;
int main(int argc, char* argv[]) {
diff --git a/src/examples/ExampleHtml.py b/examples/ExampleHtml.py
similarity index 100%
rename from src/examples/ExampleHtml.py
rename to examples/ExampleHtml.py
diff --git a/src/examples/OpenShot Wipe Tests.py b/examples/OpenShot Wipe Tests.py
similarity index 100%
rename from src/examples/OpenShot Wipe Tests.py
rename to examples/OpenShot Wipe Tests.py
diff --git a/src/examples/back.png b/examples/back.png
similarity index 100%
rename from src/examples/back.png
rename to examples/back.png
diff --git a/src/examples/final-composite.png b/examples/final-composite.png
similarity index 100%
rename from src/examples/final-composite.png
rename to examples/final-composite.png
diff --git a/src/examples/front.png b/examples/front.png
similarity index 100%
rename from src/examples/front.png
rename to examples/front.png
diff --git a/src/examples/front3.png b/examples/front3.png
similarity index 100%
rename from src/examples/front3.png
rename to examples/front3.png
diff --git a/src/examples/interlaced.png b/examples/interlaced.png
similarity index 100%
rename from src/examples/interlaced.png
rename to examples/interlaced.png
diff --git a/src/examples/mask.png b/examples/mask.png
similarity index 100%
rename from src/examples/mask.png
rename to examples/mask.png
diff --git a/src/examples/mask2.png b/examples/mask2.png
similarity index 100%
rename from src/examples/mask2.png
rename to examples/mask2.png
diff --git a/src/examples/output-final.png b/examples/output-final.png
similarity index 100%
rename from src/examples/output-final.png
rename to examples/output-final.png
diff --git a/src/examples/piano-mono.wav b/examples/piano-mono.wav
similarity index 100%
rename from src/examples/piano-mono.wav
rename to examples/piano-mono.wav
diff --git a/src/examples/piano.wav b/examples/piano.wav
similarity index 100%
rename from src/examples/piano.wav
rename to examples/piano.wav
diff --git a/src/Qt/demo/main.cpp b/examples/qt-demo/main.cpp
similarity index 96%
rename from src/Qt/demo/main.cpp
rename to examples/qt-demo/main.cpp
index 3e5f00ba..8c02795a 100644
--- a/src/Qt/demo/main.cpp
+++ b/examples/qt-demo/main.cpp
@@ -28,7 +28,9 @@
* along with OpenShot Library. If not, see .
*/
-#include "../../../include/Qt/PlayerDemo.h"
+#include "Qt/PlayerDemo.h"
+
+#include
int main(int argc, char *argv[])
{
diff --git a/src/examples/sintel_trailer-720p.mp4 b/examples/sintel_trailer-720p.mp4
similarity index 100%
rename from src/examples/sintel_trailer-720p.mp4
rename to examples/sintel_trailer-720p.mp4
diff --git a/src/examples/test.mp4 b/examples/test.mp4
similarity index 100%
rename from src/examples/test.mp4
rename to examples/test.mp4
diff --git a/src/examples/test.wav b/examples/test.wav
similarity index 100%
rename from src/examples/test.wav
rename to examples/test.wav
diff --git a/src/examples/test1.mp4 b/examples/test1.mp4
similarity index 100%
rename from src/examples/test1.mp4
rename to examples/test1.mp4
diff --git a/src/AudioBufferSource.cpp b/src/AudioBufferSource.cpp
index 69e5713a..6b2bc59e 100644
--- a/src/AudioBufferSource.cpp
+++ b/src/AudioBufferSource.cpp
@@ -28,7 +28,7 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/AudioBufferSource.h"
+#include "AudioBufferSource.h"
using namespace std;
using namespace openshot;
diff --git a/include/AudioBufferSource.h b/src/AudioBufferSource.h
similarity index 100%
rename from include/AudioBufferSource.h
rename to src/AudioBufferSource.h
diff --git a/include/AudioDeviceInfo.h b/src/AudioDeviceInfo.h
similarity index 100%
rename from include/AudioDeviceInfo.h
rename to src/AudioDeviceInfo.h
diff --git a/src/AudioReaderSource.cpp b/src/AudioReaderSource.cpp
index 999d109d..b6a01a89 100644
--- a/src/AudioReaderSource.cpp
+++ b/src/AudioReaderSource.cpp
@@ -28,7 +28,7 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/AudioReaderSource.h"
+#include "AudioReaderSource.h"
using namespace std;
using namespace openshot;
diff --git a/include/AudioReaderSource.h b/src/AudioReaderSource.h
similarity index 100%
rename from include/AudioReaderSource.h
rename to src/AudioReaderSource.h
diff --git a/src/AudioResampler.cpp b/src/AudioResampler.cpp
index 145f5d91..8c27043e 100644
--- a/src/AudioResampler.cpp
+++ b/src/AudioResampler.cpp
@@ -28,7 +28,7 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/AudioResampler.h"
+#include "AudioResampler.h"
using namespace std;
using namespace openshot;
diff --git a/include/AudioResampler.h b/src/AudioResampler.h
similarity index 100%
rename from include/AudioResampler.h
rename to src/AudioResampler.h
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 17b73863..f8bd4436 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -29,76 +29,9 @@ include(FeatureSummary)
include(GNUInstallDirs)
-################ WINDOWS ##################
-# Set some compiler options for Windows
-# required for libopenshot-audio headers
-if (WIN32)
- add_definitions( -DIGNORE_JUCE_HYPOT=1 )
- 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(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)
-IF (MAGICKCORE_QUANTUM_DEPTH)
- add_definitions( -DMAGICKCORE_QUANTUM_DEPTH=${MAGICKCORE_QUANTUM_DEPTH} )
-ELSE (MAGICKCORE_QUANTUM_DEPTH)
- add_definitions( -DMAGICKCORE_QUANTUM_DEPTH=16 )
-ENDIF (MAGICKCORE_QUANTUM_DEPTH)
-IF (MAGICKCORE_HDRI_ENABLE)
- add_definitions( -DMAGICKCORE_HDRI_ENABLE=${MAGICKCORE_HDRI_ENABLE} )
-ELSE (MAGICKCORE_HDRI_ENABLE)
- add_definitions( -DMAGICKCORE_HDRI_ENABLE=0 )
-ENDIF (MAGICKCORE_HDRI_ENABLE)
-IF (OPENSHOT_IMAGEMAGICK_COMPATIBILITY)
- add_definitions( -DOPENSHOT_IMAGEMAGICK_COMPATIBILITY=${OPENSHOT_IMAGEMAGICK_COMPATIBILITY} )
-ELSE (OPENSHOT_IMAGEMAGICK_COMPATIBILITY)
- add_definitions( -DOPENSHOT_IMAGEMAGICK_COMPATIBILITY=0 )
-ENDIF (OPENSHOT_IMAGEMAGICK_COMPATIBILITY)
-
-# Find the ImageMagick++ library
-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()
-
-################# LIBOPENSHOT-AUDIO ###################
-# Find JUCE-based openshot Audio libraries
-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 (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()
-
+# Enable IN_LIST in older CMake
+if (POLICY CMP0057)
+ cmake_policy(SET CMP0057 NEW)
endif()
############### PROFILING #################
@@ -151,7 +84,7 @@ set(OPENSHOT_SOURCES
Fraction.cpp
Frame.cpp
FrameMapper.cpp
- Json.cpp
+ Json.cpp
KeyFrame.cpp
OpenShotVersion.cpp
ZmqLogger.cpp
@@ -192,9 +125,6 @@ set(QT_PLAYER_SOURCES
Qt/VideoRenderer.cpp
Qt/VideoRenderWidget.cpp)
-# 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)
@@ -206,7 +136,6 @@ target_sources(openshot PRIVATE
${OPENSHOT_SOURCES}
${EFFECTS_SOURCES}
${QT_PLAYER_SOURCES}
- ${OPENSHOT_QT_HEADERS}
)
# Set SONAME and other library properties
@@ -215,40 +144,83 @@ set_target_properties(openshot PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_SO_VERSION}
INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib"
- )
-
-# Add optional ImageMagic-dependent sources
-if(ImageMagick_FOUND)
- target_sources(openshot PRIVATE
- ImageReader.cpp
- ImageWriter.cpp
- TextReader.cpp)
-endif()
-
-# BlackMagic related files
-if(BLACKMAGIC_FOUND)
- target_sources(openshot PRIVATE
- DecklinkInput.cpp
- DecklinkReader.cpp
- DecklinkOutput.cpp
- DecklinkWriter.cpp)
-endif()
+)
# Location of our includes, both internally and when installed
target_include_directories(openshot
PRIVATE
- ${CMAKE_SOURCE_DIR}/include
- ${CMAKE_BINARY_DIR}/include
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
PUBLIC
- $
- $
+ $
+ $
$)
+################# LIBOPENSHOT-AUDIO ###################
+# Find JUCE-based openshot Audio libraries
+find_package(OpenShotAudio 0.2.0 REQUIRED)
+target_link_libraries(openshot PUBLIC OpenShot::Audio)
+
+###
+### ImageMagick
+###
+
+# Find the ImageMagick++ library
+find_package(ImageMagick COMPONENTS Magick++ MagickWand MagickCore)
+
+if(ImageMagick_FOUND)
+ if(NOT TARGET ImageMagick::Magick++ AND NOT TARGET Magick++_TARGET)
+ add_library(Magick++_TARGET INTERFACE)
+
+ # Include ImageMagick++ headers (needed for compile)
+ set_property(TARGET Magick++_TARGET APPEND PROPERTY
+ INTERFACE_INCLUDE_DIRECTORIES ${ImageMagick_INCLUDE_DIRS})
+
+ # Set the Quantum Depth that ImageMagick was built with (default to 16 bits)
+ if(NOT DEFINED MAGICKCORE_QUANTUM_DEPTH)
+ set(MAGICKCORE_QUANTUM_DEPTH 16)
+ endif()
+ if(NOT DEFINED MAGICKCORE_HDRI_ENABLE)
+ set(MAGICKCORE_HDRI_ENABLE 0)
+ endif()
+
+ set_property(TARGET Magick++_TARGET APPEND PROPERTY
+ INTERFACE_COMPILE_DEFINITIONS
+ MAGICKCORE_QUANTUM_DEPTH=${MAGICKCORE_QUANTUM_DEPTH})
+ set_property(TARGET Magick++_TARGET APPEND PROPERTY
+ INTERFACE_COMPILE_DEFINITIONS
+ MAGICKCORE_HDRI_ENABLE=${MAGICKCORE_HDRI_ENABLE})
+
+ target_link_libraries(Magick++_TARGET INTERFACE
+ ${ImageMagick_LIBRARIES})
+
+ # Alias to our namespaced name
+ add_library(ImageMagick::Magick++ ALIAS Magick++_TARGET)
+
+ endif()
+
+ # Add optional ImageMagic-dependent sources
+ target_sources(openshot PRIVATE
+ ImageReader.cpp
+ ImageWriter.cpp
+ TextReader.cpp)
+
+ # define a preprocessor macro (used in the C++ source)
+ target_compile_definitions(openshot PUBLIC USE_IMAGEMAGICK=1)
+ list(APPEND CMAKE_SWIG_FLAGS -DUSE_IMAGEMAGICK=1)
+
+ # Link with ImageMagick library
+ target_link_libraries(openshot PUBLIC ImageMagick::Magick++)
+
+ set(HAVE_IMAGEMAGICK TRUE CACHE BOOL "Building with ImageMagick support" FORCE)
+ mark_as_advanced(HAVE_IMAGEMAGICK)
+endif()
+
################### JSONCPP #####################
# Include jsoncpp headers (needed for JSON parsing)
if (USE_SYSTEM_JSONCPP)
- message(STATUS "Looking for system jsoncpp")
+ message(STATUS "Looking for system jsoncpp")
# Either an installed config or our find module will
# create the IMPORTED target jsoncpp_lib if successful
find_package(jsoncpp)
@@ -280,7 +252,7 @@ endif ()
################# QT5 ###################
# Find QT5 libraries
-set(_qt_components Widgets Core Gui Multimedia MultimediaWidgets)
+set(_qt_components Core Gui Widgets)
find_package(Qt5 COMPONENTS ${_qt_components} REQUIRED)
foreach(_qt_comp IN LISTS _qt_components)
@@ -289,22 +261,29 @@ foreach(_qt_comp IN LISTS _qt_components)
endif()
endforeach()
+# Keep track of Qt version, to embed in our version header
+set(QT_VERSION_STR ${Qt5Core_VERSION_STRING} CACHE STRING "Qt version linked with" FORCE)
+mark_as_advanced(QT_VERSION_STR)
+
################### FFMPEG #####################
# Find FFmpeg libraries (used for video encoding / decoding)
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})
- target_link_libraries(openshot PUBLIC FFmpeg::${ff_comp})
- endif()
-endforeach()
+set(all_comps avcodec avdevice avformat avfilter avutil postproc swscale swresample avresample)
+set(version_comps avcodec avformat avutil)
-################### Threads ####################
-# Threading library -- uses IMPORTED target Threads::Threads (since CMake 3.1)
-set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
-set(THREADS_PREFER_PTHREAD_FLAG TRUE)
-find_package(Threads REQUIRED)
-target_link_libraries(openshot PUBLIC Threads::Threads)
+foreach(ff_comp IN LISTS all_comps)
+ if(TARGET FFmpeg::${ff_comp})
+ target_link_libraries(openshot PUBLIC FFmpeg::${ff_comp})
+ # Keep track of some FFmpeg lib versions, to embed in our version header
+ if(${ff_comp} IN_LIST version_comps)
+ string(TOUPPER ${ff_comp} v_name)
+ set(${v_name}_VERSION_STR ${${ff_comp}_VERSION} CACHE STRING "${ff_comp} version used" FORCE)
+ mark_as_advanced(${v_name}_VERSION_STR)
+ endif()
+ endif()
+
+endforeach()
################### OPENMP #####################
# Check for OpenMP (used for multi-core processing)
@@ -317,13 +296,15 @@ if(NOT TARGET OpenMP::OpenMP_CXX)
add_library(OpenMP_TARGET INTERFACE)
add_library(OpenMP::OpenMP_CXX ALIAS OpenMP_TARGET)
target_compile_options(OpenMP_TARGET INTERFACE ${OpenMP_CXX_FLAGS})
- target_link_libraries(OpenMP_TARGET INTERFACE Threads::Threads)
target_link_libraries(OpenMP_TARGET INTERFACE ${OpenMP_CXX_FLAGS})
endif()
target_link_libraries(openshot PUBLIC OpenMP::OpenMP_CXX)
-################### ZEROMQ #####################
+###
+### ZeroMQ
+###
+
# Find ZeroMQ library (used for socket communication & logging)
find_package(ZeroMQ REQUIRED) # Creates libzmq target
@@ -333,113 +314,102 @@ find_package(cppzmq QUIET) # Creates cppzmq target
# Link ZeroMQ library
if (TARGET libzmq)
- target_link_libraries(openshot PUBLIC libzmq)
+ target_link_libraries(openshot PUBLIC libzmq)
endif()
# Include cppzmq headers, if not bundled into libzmq
if (TARGET cppzmq)
target_link_libraries(openshot PUBLIC cppzmq)
endif()
-################### RESVG #####################
+###
+### Resvg, the Rust SVG library
+###
+
# Migrate some legacy variable names
-if(DEFINED RESVGDIR AND NOT DEFINED RESVG_ROOT)
- set(RESVG_ROOT ${RESVGDIR})
+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})
+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)
+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)
+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")
+ target_compile_definitions(openshot PUBLIC -DUSE_RESVG=1)
+ list(APPEND CMAKE_SWIG_FLAGS -DUSE_RESVG=1)
+
+ set(HAVE_RESVG TRUE CACHE BOOL "Building with Resvg support" FORCE)
+ mark_as_advanced(HAVE_RESVG)
+endif()
+
+################# BLACKMAGIC DECKLINK ###################
+# Find BlackMagic DeckLinkAPI libraries
+if (ENABLE_BLACKMAGIC)
+ find_package(BlackMagic)
+
+ if (BLACKMAGIC_FOUND)
+ # BlackMagic related files
+ target_sources(openshot PRIVATE
+ DecklinkInput.cpp
+ DecklinkReader.cpp
+ DecklinkOutput.cpp
+ DecklinkWriter.cpp)
+
+ # Include Blackmagic headers (needed for compile)
+ target_include_directories(openshot PRIVATE ${BLACKMAGIC_INCLUDE_DIR})
+
+ # Link libopenshot with BlackMagic libs
+ target_link_libraries(openshot PUBLIC ${BLACKMAGIC_LIBRARY_DIR})
+
+ # define a preprocessor macro (used in the C++)
+ target_compile_definitions(openshot PUBLIC -DUSE_BLACKMAGIC=1)
+ list(APPEND CMAKE_SWIG_FLAGS -DUSE_BLACKMAGIC=1)
+ endif()
endif()
############### LINK LIBRARY #################
# Link remaining dependency libraries
-target_link_libraries(openshot PUBLIC
- ${LIBOPENSHOT_AUDIO_LIBRARIES}
- ${PROFILER})
-
-if(ImageMagick_FOUND)
- target_link_libraries(openshot PUBLIC ${ImageMagick_LIBRARIES})
-endif()
-
-if(BLACKMAGIC_FOUND)
- target_link_libraries(openshot PUBLIC ${BLACKMAGIC_LIBRARY_DIR})
+if(DEFINED PROFILER)
+ target_link_libraries(openshot PUBLIC ${PROFILER})
endif()
if(WIN32)
- # Required for exception handling on Windows
- target_link_libraries(openshot PUBLIC "imagehlp" "dbghelp" )
+ # Required for exception handling on Windows
+ target_link_libraries(openshot PUBLIC "imagehlp" "dbghelp" )
endif()
-
-############### CLI EXECUTABLES ################
-# Create test executable
-add_executable(openshot-example examples/Example.cpp)
-
-# Define path to test input files
-SET(TEST_MEDIA_PATH "${PROJECT_SOURCE_DIR}/src/examples/")
-if (WIN32)
- STRING(REPLACE "/" "\\\\" TEST_MEDIA_PATH TEST_MEDIA_PATH)
-endif()
-target_compile_definitions(openshot-example PRIVATE
- -DTEST_MEDIA_PATH="${TEST_MEDIA_PATH}" )
-
-# Link test executable to the new library
-target_link_libraries(openshot-example openshot)
-
-add_executable(openshot-html-test examples/ExampleHtml.cpp)
-target_link_libraries(openshot-html-test openshot Qt5::Gui)
-
-############### PLAYER EXECUTABLE ################
-# Create test executable
-add_executable(openshot-player Qt/demo/main.cpp)
-
-# Link test executable to the new library
-target_link_libraries(openshot-player openshot)
-
-############### TEST BLACKMAGIC CAPTURE APP ################
-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()
-
-############### INCLUDE SWIG BINDINGS ################
-add_subdirectory(bindings)
-
-############### INSTALL HEADERS & LIBRARY ################
+###
+### INSTALL HEADERS & LIBRARY
+###
# Install primary library
install(TARGETS openshot
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
- RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR})
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libopenshot)
-install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/
+install(
+ DIRECTORY .
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libopenshot
- FILES_MATCHING PATTERN "*.h")
+ FILES_MATCHING PATTERN "*.h"
+)
############### CPACK PACKAGING ##############
if(MINGW)
- set(CPACK_GENERATOR "NSIS")
+ set(CPACK_GENERATOR "NSIS")
endif()
if(UNIX AND NOT APPLE)
- set(CPACK_GENERATOR "DEB")
+ set(CPACK_GENERATOR "DEB")
endif()
#if(UNIX AND APPLE)
-# set(CPACK_GENERATOR "DragNDrop")
+# set(CPACK_GENERATOR "DragNDrop")
#endif()
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Jonathan Thomas") #required
diff --git a/src/CacheBase.cpp b/src/CacheBase.cpp
index bc57f3f4..72381792 100644
--- a/src/CacheBase.cpp
+++ b/src/CacheBase.cpp
@@ -28,7 +28,7 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/CacheBase.h"
+#include "CacheBase.h"
using namespace std;
using namespace openshot;
diff --git a/include/CacheBase.h b/src/CacheBase.h
similarity index 100%
rename from include/CacheBase.h
rename to src/CacheBase.h
diff --git a/src/CacheDisk.cpp b/src/CacheDisk.cpp
index bb2e12c4..c252d0b2 100644
--- a/src/CacheDisk.cpp
+++ b/src/CacheDisk.cpp
@@ -28,7 +28,11 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/CacheDisk.h"
+#include "CacheDisk.h"
+#include "QtUtilities.h"
+#include
+#include
+#include
using namespace std;
using namespace openshot;
@@ -191,10 +195,10 @@ void CacheDisk::Add(std::shared_ptr frame)
if (audio_file.open(QIODevice::WriteOnly)) {
QTextStream audio_stream(&audio_file);
- audio_stream << frame->SampleRate() << endl;
- audio_stream << frame->GetAudioChannelsCount() << endl;
- audio_stream << frame->GetAudioSamplesCount() << endl;
- audio_stream << frame->ChannelsLayout() << endl;
+ audio_stream << frame->SampleRate() << Qt::endl;
+ audio_stream << frame->GetAudioChannelsCount() << Qt::endl;
+ audio_stream << frame->GetAudioSamplesCount() << Qt::endl;
+ audio_stream << frame->ChannelsLayout() << Qt::endl;
// Loop through all samples
for (int channel = 0; channel < frame->GetAudioChannelsCount(); channel++)
@@ -202,7 +206,7 @@ void CacheDisk::Add(std::shared_ptr frame)
// Get audio for this channel
float *samples = frame->GetAudioSamples(channel);
for (int sample = 0; sample < frame->GetAudioSamplesCount(); sample++)
- audio_stream << samples[sample] << endl;
+ audio_stream << samples[sample] << Qt::endl;
}
}
@@ -227,14 +231,14 @@ std::shared_ptr CacheDisk::GetFrame(int64_t frame_number)
if (path.exists(frame_path)) {
// Load image file
- std::shared_ptr image = std::shared_ptr(new QImage());
+ auto image = std::make_shared();
image->load(frame_path);
// Set pixel formatimage->
- image = std::shared_ptr(new QImage(image->convertToFormat(QImage::Format_RGBA8888)));
+ image = std::make_shared(image->convertToFormat(QImage::Format_RGBA8888));
// Create frame object
- std::shared_ptr frame(new Frame());
+ auto frame = std::make_shared();
frame->number = frame_number;
frame->AddImage(image);
diff --git a/include/CacheDisk.h b/src/CacheDisk.h
similarity index 99%
rename from include/CacheDisk.h
rename to src/CacheDisk.h
index 09ebd4ab..c60f5f6e 100644
--- a/include/CacheDisk.h
+++ b/src/CacheDisk.h
@@ -38,8 +38,6 @@
#include "Frame.h"
#include "Exceptions.h"
#include
-#include
-#include
namespace openshot {
diff --git a/src/CacheMemory.cpp b/src/CacheMemory.cpp
index 70feef03..685c86ec 100644
--- a/src/CacheMemory.cpp
+++ b/src/CacheMemory.cpp
@@ -28,7 +28,7 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/CacheMemory.h"
+#include "CacheMemory.h"
using namespace std;
using namespace openshot;
diff --git a/include/CacheMemory.h b/src/CacheMemory.h
similarity index 100%
rename from include/CacheMemory.h
rename to src/CacheMemory.h
diff --git a/include/ChannelLayouts.h b/src/ChannelLayouts.h
similarity index 100%
rename from include/ChannelLayouts.h
rename to src/ChannelLayouts.h
diff --git a/src/ChunkReader.cpp b/src/ChunkReader.cpp
index c194ce33..5301c687 100644
--- a/src/ChunkReader.cpp
+++ b/src/ChunkReader.cpp
@@ -28,8 +28,10 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/ChunkReader.h"
-#include "../include/FFmpegReader.h"
+#include "ChunkReader.h"
+#include "FFmpegReader.h"
+
+#include
using namespace openshot;
diff --git a/include/ChunkReader.h b/src/ChunkReader.h
similarity index 97%
rename from include/ChunkReader.h
rename to src/ChunkReader.h
index cd7cd67f..7a041f71 100644
--- a/include/ChunkReader.h
+++ b/src/ChunkReader.h
@@ -32,18 +32,12 @@
#define OPENSHOT_CHUNK_READER_H
#include "ReaderBase.h"
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
+#include
#include
+
+#include "Frame.h"
#include "Json.h"
#include "CacheMemory.h"
-#include "Exceptions.h"
namespace openshot
{
diff --git a/src/ChunkWriter.cpp b/src/ChunkWriter.cpp
index f9d653b0..bbb10be9 100644
--- a/src/ChunkWriter.cpp
+++ b/src/ChunkWriter.cpp
@@ -28,7 +28,7 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/ChunkWriter.h"
+#include "ChunkWriter.h"
using namespace openshot;
@@ -134,7 +134,9 @@ void ChunkWriter::WriteFrame(std::shared_ptr frame)
writer_thumb->WriteFrame(last_frame);
} else {
// Write the 1st frame (of the 1st chunk)... since no previous chunk is available
- std::shared_ptr blank_frame(new Frame(1, info.width, info.height, "#000000", info.sample_rate, info.channels));
+ auto blank_frame = std::make_shared(
+ 1, info.width, info.height, "#000000",
+ info.sample_rate, info.channels);
blank_frame->AddColor(info.width, info.height, "#000000");
writer_final->WriteFrame(blank_frame);
writer_preview->WriteFrame(blank_frame);
diff --git a/include/ChunkWriter.h b/src/ChunkWriter.h
similarity index 99%
rename from include/ChunkWriter.h
rename to src/ChunkWriter.h
index 5fee5a37..a63e4ca1 100644
--- a/include/ChunkWriter.h
+++ b/src/ChunkWriter.h
@@ -34,19 +34,19 @@
#include "ReaderBase.h"
#include "WriterBase.h"
#include "FFmpegWriter.h"
+#include "CacheMemory.h"
+#include "Exceptions.h"
+#include "Json.h"
#include
#include
#include
#include
-#include
-#include
-#include
+#include
#include
#include
-#include "CacheMemory.h"
-#include "Exceptions.h"
-#include "Json.h"
+#include
+#include
namespace openshot
diff --git a/src/Clip.cpp b/src/Clip.cpp
index d9f69440..f3660f0a 100644
--- a/src/Clip.cpp
+++ b/src/Clip.cpp
@@ -28,17 +28,17 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/Clip.h"
-#include "../include/FFmpegReader.h"
-#include "../include/FrameMapper.h"
+#include "Clip.h"
+#include "FFmpegReader.h"
+#include "FrameMapper.h"
#ifdef USE_IMAGEMAGICK
- #include "../include/ImageReader.h"
- #include "../include/TextReader.h"
+ #include "ImageReader.h"
+ #include "TextReader.h"
#endif
-#include "../include/QtImageReader.h"
-#include "../include/ChunkReader.h"
-#include "../include/DummyReader.h"
-#include "../include/Timeline.h"
+#include "QtImageReader.h"
+#include "ChunkReader.h"
+#include "DummyReader.h"
+#include "Timeline.h"
using namespace openshot;
@@ -153,7 +153,7 @@ Clip::Clip(ReaderBase* new_reader) : resampler(NULL), reader(new_reader), alloca
// Update duration and set parent
if (reader) {
End(reader->info.duration);
- reader->SetClip(this);
+ reader->SetParentClip(this);
}
}
@@ -210,7 +210,7 @@ Clip::Clip(std::string path) : resampler(NULL), reader(NULL), allocated_reader(N
// Update duration and set parent
if (reader) {
End(reader->info.duration);
- reader->SetClip(this);
+ reader->SetParentClip(this);
allocated_reader = reader;
init_reader_rotation();
}
@@ -239,7 +239,7 @@ void Clip::Reader(ReaderBase* new_reader)
reader = new_reader;
// set parent
- reader->SetClip(this);
+ reader->SetParentClip(this);
// Init rotation (if any)
init_reader_rotation();
@@ -339,7 +339,10 @@ std::shared_ptr Clip::GetFrame(int64_t requested_frame)
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()));
+ auto frame = std::make_shared(
+ new_frame_number, 1, 1, "#000000",
+ original_frame->GetAudioSamplesCount(),
+ original_frame->GetAudioChannelsCount());
{
frame->SampleRate(original_frame->SampleRate());
frame->ChannelsLayout(original_frame->ChannelsLayout());
@@ -347,7 +350,7 @@ std::shared_ptr Clip::GetFrame(int64_t requested_frame)
// Copy the image from the odd field
if (enabled_video)
- frame->AddImage(std::shared_ptr(new QImage(*original_frame->GetImage())));
+ frame->AddImage(std::make_shared(*original_frame->GetImage()));
// Loop through each channel, add audio
if (enabled_audio && reader->info.has_audio)
@@ -368,6 +371,18 @@ std::shared_ptr Clip::GetFrame(int64_t requested_frame)
throw ReaderClosed("No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.");
}
+// Look up an effect by ID
+openshot::EffectBase* Clip::GetEffect(const std::string& id)
+{
+ // Find the matching effect (if any)
+ for (const auto& effect : effects) {
+ if (effect->Id() == id) {
+ return effect;
+ }
+ }
+ return nullptr;
+}
+
// Get file extension
std::string Clip::get_file_extension(std::string path)
{
@@ -993,7 +1008,7 @@ void Clip::SetJsonValue(const Json::Value root) {
// mark as managed reader and set parent
if (reader) {
- reader->SetClip(this);
+ reader->SetParentClip(this);
allocated_reader = reader;
}
diff --git a/include/Clip.h b/src/Clip.h
similarity index 98%
rename from include/Clip.h
rename to src/Clip.h
index 0fbed159..0c98526a 100644
--- a/include/Clip.h
+++ b/src/Clip.h
@@ -169,6 +169,9 @@ namespace openshot {
/// Return the list of effects on the timeline
std::list Effects() { return effects; };
+ /// Look up an effect by ID
+ openshot::EffectBase* GetEffect(const std::string& id);
+
/// @brief Get an openshot::Frame object for a specific frame number of this timeline.
///
/// @returns The requested frame (containing the image)
@@ -253,8 +256,6 @@ namespace openshot {
openshot::Keyframe has_audio; ///< An optional override to determine if this clip has audio (-1=undefined, 0=no, 1=yes)
openshot::Keyframe has_video; ///< An optional override to determine if this clip has video (-1=undefined, 0=no, 1=yes)
};
+} // namespace
-
-}
-
-#endif
+#endif // OPENSHOT_CLIP_H
diff --git a/src/ClipBase.cpp b/src/ClipBase.cpp
index a51c6573..f7695904 100644
--- a/src/ClipBase.cpp
+++ b/src/ClipBase.cpp
@@ -28,7 +28,7 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/ClipBase.h"
+#include "ClipBase.h"
using namespace openshot;
diff --git a/include/ClipBase.h b/src/ClipBase.h
similarity index 100%
rename from include/ClipBase.h
rename to src/ClipBase.h
diff --git a/src/Color.cpp b/src/Color.cpp
index 705ece4a..622a912c 100644
--- a/src/Color.cpp
+++ b/src/Color.cpp
@@ -28,7 +28,7 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/Color.h"
+#include "Color.h"
using namespace openshot;
diff --git a/include/Color.h b/src/Color.h
similarity index 100%
rename from include/Color.h
rename to src/Color.h
diff --git a/src/Coordinate.cpp b/src/Coordinate.cpp
index f87fb7a0..0d4f577a 100644
--- a/src/Coordinate.cpp
+++ b/src/Coordinate.cpp
@@ -28,7 +28,7 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/Coordinate.h"
+#include "Coordinate.h"
using namespace std;
using namespace openshot;
diff --git a/include/Coordinate.h b/src/Coordinate.h
similarity index 100%
rename from include/Coordinate.h
rename to src/Coordinate.h
diff --git a/src/CrashHandler.cpp b/src/CrashHandler.cpp
index 1782f5ba..7b6c4e7e 100644
--- a/src/CrashHandler.cpp
+++ b/src/CrashHandler.cpp
@@ -28,7 +28,7 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/CrashHandler.h"
+#include "CrashHandler.h"
using namespace std;
using namespace openshot;
diff --git a/include/CrashHandler.h b/src/CrashHandler.h
similarity index 100%
rename from include/CrashHandler.h
rename to src/CrashHandler.h
diff --git a/src/DecklinkInput.cpp b/src/DecklinkInput.cpp
index da5a8d00..21c4f7bb 100644
--- a/src/DecklinkInput.cpp
+++ b/src/DecklinkInput.cpp
@@ -53,7 +53,7 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/DecklinkInput.h"
+#include "DecklinkInput.h"
using namespace std;
@@ -139,7 +139,7 @@ HRESULT DeckLinkInputDelegate::VideoInputFrameArrived(IDeckLinkVideoInputFrame*
{
// Handle Video Frame
if(videoFrame)
- {
+ {
if (videoFrame->GetFlags() & bmdFrameHasNoInputSource)
{
@@ -245,7 +245,8 @@ omp_set_nested(true);
m_rgbFrame->GetBytes(&frameBytes);
// *********** CREATE OPENSHOT FRAME **********
- std::shared_ptr f(new openshot::Frame(copy_frameCount, width, height, "#000000", 2048, 2));
+ auto f = std::make_shared(
+ copy_frameCount, width, height, "#000000", 2048, 2);
// Add Image data to openshot frame
// TODO: Fix Decklink support with QImage Upgrade
@@ -289,6 +290,3 @@ HRESULT DeckLinkInputDelegate::VideoInputFormatChanged(BMDVideoInputFormatChange
{
return S_OK;
}
-
-
-
diff --git a/include/DecklinkInput.h b/src/DecklinkInput.h
similarity index 100%
rename from include/DecklinkInput.h
rename to src/DecklinkInput.h
diff --git a/src/DecklinkOutput.cpp b/src/DecklinkOutput.cpp
index 2dee7e9e..13da185f 100644
--- a/src/DecklinkOutput.cpp
+++ b/src/DecklinkOutput.cpp
@@ -53,7 +53,7 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/DecklinkOutput.h"
+#include "DecklinkOutput.h"
using namespace std;
diff --git a/include/DecklinkOutput.h b/src/DecklinkOutput.h
similarity index 100%
rename from include/DecklinkOutput.h
rename to src/DecklinkOutput.h
diff --git a/src/DecklinkReader.cpp b/src/DecklinkReader.cpp
index 14c2f87c..bb31f316 100644
--- a/src/DecklinkReader.cpp
+++ b/src/DecklinkReader.cpp
@@ -28,7 +28,7 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/DecklinkReader.h"
+#include "DecklinkReader.h"
using namespace openshot;
diff --git a/include/DecklinkReader.h b/src/DecklinkReader.h
similarity index 100%
rename from include/DecklinkReader.h
rename to src/DecklinkReader.h
diff --git a/src/DecklinkWriter.cpp b/src/DecklinkWriter.cpp
index 4ebbd1f0..1bfb59a8 100644
--- a/src/DecklinkWriter.cpp
+++ b/src/DecklinkWriter.cpp
@@ -28,7 +28,7 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/DecklinkWriter.h"
+#include "DecklinkWriter.h"
using namespace openshot;
@@ -142,7 +142,7 @@ void DecklinkWriter::Open()
// throw DecklinkError("Failed to enable audio output. Is another application using the card?");
// Begin video preroll by scheduling a second of frames in hardware
- //std::shared_ptr f(new Frame(1, displayMode->GetWidth(), displayMode->GetHeight(), "Blue"));
+ //auto f = std::make_shared(1, displayMode->GetWidth(), displayMode->GetHeight(), "Blue");
//f->AddColor(displayMode->GetWidth(), displayMode->GetHeight(), "Blue");
// Preroll 1 second of video
diff --git a/include/DecklinkWriter.h b/src/DecklinkWriter.h
similarity index 100%
rename from include/DecklinkWriter.h
rename to src/DecklinkWriter.h
diff --git a/src/DummyReader.cpp b/src/DummyReader.cpp
index 8fd98bcb..c295c298 100644
--- a/src/DummyReader.cpp
+++ b/src/DummyReader.cpp
@@ -28,20 +28,12 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/DummyReader.h"
+#include "DummyReader.h"
using namespace openshot;
-// Blank constructor for DummyReader, with default settings.
-DummyReader::DummyReader() {
-
- // Call actual constructor with default values
- DummyReader(Fraction(24,1), 1280, 768, 44100, 2, 30.0);
-}
-
-// Constructor for DummyReader. Pass a framerate and samplerate.
-DummyReader::DummyReader(Fraction fps, int width, int height, int sample_rate, int channels, float duration) {
-
+// Initialize variables used by constructor
+void DummyReader::init(Fraction fps, int width, int height, int sample_rate, int channels, float duration) {
// Set key info settings
info.has_audio = false;
info.has_video = true;
@@ -68,10 +60,30 @@ DummyReader::DummyReader(Fraction fps, int width, int height, int sample_rate, i
// Set the ratio based on the reduced fraction
info.display_ratio.num = size.num;
info.display_ratio.den = size.den;
+}
- // Open and Close the reader, to populate its attributes (such as height, width, etc...)
- Open();
- Close();
+// Blank constructor for DummyReader, with default settings.
+DummyReader::DummyReader() : dummy_cache(NULL), is_open(false) {
+
+ // Initialize important variables
+ init(Fraction(24,1), 1280, 768, 44100, 2, 30.0);
+}
+
+// Constructor for DummyReader. Pass a framerate and samplerate.
+DummyReader::DummyReader(Fraction fps, int width, int height, int sample_rate, int channels, float duration) : dummy_cache(NULL), is_open(false) {
+
+ // Initialize important variables
+ init(fps, width, height, sample_rate, channels, duration);
+}
+
+// Constructor which also takes a cache object
+DummyReader::DummyReader(Fraction fps, int width, int height, int sample_rate, int channels, float duration, CacheBase* cache) : is_open(false) {
+
+ // Initialize important variables
+ init(fps, width, height, sample_rate, channels, duration);
+
+ // Set cache object
+ dummy_cache = (CacheBase*) cache;
}
DummyReader::~DummyReader() {
@@ -102,21 +114,40 @@ void DummyReader::Close()
}
}
-// Get an openshot::Frame object for a specific frame number of this reader.
+// Get an openshot::Frame object for a specific frame number of this reader. It is either a blank frame
+// or a custom frame added with passing a Cache object to the constructor.
std::shared_ptr DummyReader::GetFrame(int64_t requested_frame)
{
// Check for open reader (or throw exception)
if (!is_open)
throw ReaderClosed("The ImageReader is closed. Call Open() before calling this method.", "dummy");
- if (image_frame)
- {
+ int dummy_cache_count = 0;
+ if (dummy_cache) {
+ dummy_cache_count = dummy_cache->Count();
+ }
+
+ if (dummy_cache_count == 0 && image_frame) {
// Create a scoped lock, allowing only a single thread to run the following code at one time
const GenericScopedLock lock(getFrameCriticalSection);
// Always return same frame (regardless of which frame number was requested)
image_frame->number = requested_frame;
return image_frame;
+
+ } else if (dummy_cache_count > 0) {
+ // Create a scoped lock, allowing only a single thread to run the following code at one time
+ const GenericScopedLock lock(getFrameCriticalSection);
+
+ // Get a frame from the dummy cache
+ std::shared_ptr f = dummy_cache->GetFrame(requested_frame);
+ if (f) {
+ // return frame from cache (if found)
+ return f;
+ } else {
+ // No cached frame found
+ throw InvalidFile("Requested frame not found. You can only access Frame numbers that exist in the Cache object.", "dummy");
+ }
}
else
// no frame loaded
diff --git a/include/DummyReader.h b/src/DummyReader.h
similarity index 54%
rename from include/DummyReader.h
rename to src/DummyReader.h
index 4c935103..af06656d 100644
--- a/include/DummyReader.h
+++ b/src/DummyReader.h
@@ -46,17 +46,71 @@
namespace openshot
{
/**
- * @brief This class is used as a simple, dummy reader, which always returns a blank frame.
+ * @brief This class is used as a simple, dummy reader, which can be very useful when writing
+ * unit tests. It can return a single blank frame or it can return custom frame objects
+ * which were passed into the constructor with a Cache object.
*
* A dummy reader can be created with any framerate or samplerate. This is useful in unit
* tests that need to test different framerates or samplerates.
+ *
+ * @note Timeline does buffering by requesting more frames than it
+ * strictly needs. Thus if you use this DummyReader with a custom
+ * cache in a Timeline, make sure it has enough
+ * frames. Specifically you need some frames after the last frame
+ * you plan to access through the Timeline.
+ *
+ * @code
+ * // Create cache object to store fake Frame objects
+ * CacheMemory cache;
+ *
+ * // Now let's create some test frames
+ * for (int64_t frame_number = 1; frame_number <= 30; frame_number++)
+ * {
+ * // Create blank frame (with specific frame #, samples, and channels)
+ * // Sample count should be 44100 / 30 fps = 1470 samples per frame
+ * int sample_count = 1470;
+ * auto f = std::make_shared(frame_number, sample_count, 2);
+ *
+ * // Create test samples with incrementing value
+ * float *audio_buffer = new float[sample_count];
+ * for (int64_t sample_number = 0; sample_number < sample_count; sample_number++)
+ * {
+ * // Generate an incrementing audio sample value (just as an example)
+ * audio_buffer[sample_number] = float(frame_number) + (float(sample_number) / float(sample_count));
+ * }
+ *
+ * // Add custom audio samples to Frame (bool replaceSamples, int destChannel, int destStartSample, const float* source,
+ * // int numSamples, float gainToApplyToSource = 1.0f)
+ * f->AddAudio(true, 0, 0, audio_buffer, sample_count, 1.0); // add channel 1
+ * f->AddAudio(true, 1, 0, audio_buffer, sample_count, 1.0); // add channel 2
+ *
+ * // Add test frame to cache
+ * cache.Add(f);
+ * }
+ *
+ * // Create a reader (Fraction fps, int width, int height, int sample_rate, int channels, float duration, CacheBase* cache)
+ * openshot::DummyReader r(openshot::Fraction(30, 1), 1920, 1080, 44100, 2, 30.0, &cache);
+ * r.Open(); // Open the reader
+ *
+ * // Now let's verify our DummyReader works
+ * std::shared_ptr f = r.GetFrame(1);
+ * // r.GetFrame(1)->GetAudioSamples(0)[1] should equal 1.00068033 based on our above calculations
+ *
+ * // Clean up
+ * r.Close();
+ * cache.Clear()
+ * @endcode
*/
class DummyReader : public ReaderBase
{
private:
+ CacheBase* dummy_cache;
std::shared_ptr image_frame;
bool is_open;
+ /// Initialize variables used by constructor
+ void init(Fraction fps, int width, int height, int sample_rate, int channels, float duration);
+
public:
/// Blank constructor for DummyReader, with default settings.
@@ -65,6 +119,9 @@ namespace openshot
/// Constructor for DummyReader.
DummyReader(openshot::Fraction fps, int width, int height, int sample_rate, int channels, float duration);
+ /// Constructor for DummyReader which takes a frame cache object.
+ DummyReader(openshot::Fraction fps, int width, int height, int sample_rate, int channels, float duration, CacheBase* cache);
+
virtual ~DummyReader();
/// Close File
diff --git a/src/EffectBase.cpp b/src/EffectBase.cpp
index 05ed97c2..f1d7d030 100644
--- a/src/EffectBase.cpp
+++ b/src/EffectBase.cpp
@@ -28,7 +28,7 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/EffectBase.h"
+#include "EffectBase.h"
using namespace openshot;
diff --git a/include/EffectBase.h b/src/EffectBase.h
similarity index 100%
rename from include/EffectBase.h
rename to src/EffectBase.h
diff --git a/src/EffectInfo.cpp b/src/EffectInfo.cpp
index 6829f4eb..1359238d 100644
--- a/src/EffectInfo.cpp
+++ b/src/EffectInfo.cpp
@@ -28,8 +28,7 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/EffectInfo.h"
-
+#include "EffectInfo.h"
using namespace openshot;
diff --git a/include/EffectInfo.h b/src/EffectInfo.h
similarity index 100%
rename from include/EffectInfo.h
rename to src/EffectInfo.h
diff --git a/include/Effects.h b/src/Effects.h
similarity index 100%
rename from include/Effects.h
rename to src/Effects.h
diff --git a/include/Enums.h b/src/Enums.h
similarity index 100%
rename from include/Enums.h
rename to src/Enums.h
diff --git a/include/Exceptions.h b/src/Exceptions.h
similarity index 100%
rename from include/Exceptions.h
rename to src/Exceptions.h
diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp
index 21b007cb..f8af2ba1 100644
--- a/src/FFmpegReader.cpp
+++ b/src/FFmpegReader.cpp
@@ -31,7 +31,10 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/FFmpegReader.h"
+#include "FFmpegReader.h"
+
+#include // for std::this_thread::sleep_for
+#include // for std::chrono::milliseconds
#define ENABLE_VAAPI 0
@@ -83,7 +86,7 @@ int hw_de_on = 0;
AVHWDeviceType hw_de_av_device_type_global = AV_HWDEVICE_TYPE_NONE;
#endif
-FFmpegReader::FFmpegReader(std::string path)
+FFmpegReader::FFmpegReader(const std::string& path, bool inspect_reader)
: last_frame(0), is_seeking(0), seeking_pts(0), seeking_frame(0), seek_count(0),
audio_pts_offset(99999), video_pts_offset(99999), path(path), is_video_seek(true), check_interlace(false),
check_fps(false), enable_seek(true), is_open(false), seek_audio_frame_found(0), seek_video_frame_found(0),
@@ -91,27 +94,11 @@ FFmpegReader::FFmpegReader(std::string path)
current_video_frame(0), has_missing_frames(false), num_packets_since_video_frame(0), num_checks_since_final(0),
packet(NULL) {
- // Initialize FFMpeg, and register all formats and codecs
- AV_REGISTER_ALL
- AVCODEC_REGISTER_ALL
-
- // Init cache
- working_cache.SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * info.fps.ToDouble() * 2, info.width, info.height, info.sample_rate, info.channels);
- missing_frames.SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * 2, info.width, info.height, info.sample_rate, info.channels);
- final_cache.SetMaxBytesFromInfo(OPEN_MP_NUM_PROCESSORS * 2, info.width, info.height, info.sample_rate, info.channels);
-
- // Open and Close the reader, to populate its attributes (such as height, width, etc...)
- Open();
- Close();
-}
-
-FFmpegReader::FFmpegReader(std::string path, bool inspect_reader)
- : last_frame(0), is_seeking(0), seeking_pts(0), seeking_frame(0), seek_count(0),
- audio_pts_offset(99999), video_pts_offset(99999), path(path), is_video_seek(true), check_interlace(false),
- check_fps(false), enable_seek(true), is_open(false), seek_audio_frame_found(0), seek_video_frame_found(0),
- prev_samples(0), prev_pts(0), pts_total(0), pts_counter(0), is_duration_known(false), largest_frame_processed(0),
- current_video_frame(0), has_missing_frames(false), num_packets_since_video_frame(0), num_checks_since_final(0),
- packet(NULL) {
+ // Configure OpenMP parallelism
+ // Default number of threads per section
+ omp_set_num_threads(OPEN_MP_NUM_PROCESSORS);
+ // Allow nested parallel sections as deeply as supported
+ omp_set_max_active_levels(OPEN_MP_MAX_ACTIVE);
// Initialize FFMpeg, and register all formats and codecs
AV_REGISTER_ALL
@@ -911,11 +898,6 @@ std::shared_ptr FFmpegReader::ReadStream(int64_t requested_frame) {
int minimum_packets = OPEN_MP_NUM_PROCESSORS;
int max_packets = 4096;
- // Set the number of threads in OpenMP
- omp_set_num_threads(OPEN_MP_NUM_PROCESSORS);
- // Allow nested OpenMP sections
- omp_set_nested(true);
-
// Debug output
ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::ReadStream", "requested_frame", requested_frame, "OPEN_MP_NUM_PROCESSORS", OPEN_MP_NUM_PROCESSORS);
@@ -938,7 +920,7 @@ std::shared_ptr FFmpegReader::ReadStream(int64_t requested_frame) {
// Wait if too many frames are being processed
while (processing_video_frames_size + processing_audio_frames_size >= minimum_packets) {
- usleep(2500);
+ std::this_thread::sleep_for(std::chrono::milliseconds(3));
const GenericScopedLock lock(processingCriticalSection);
processing_video_frames_size = processing_video_frames.size();
processing_audio_frames_size = processing_audio_frames.size();
@@ -1306,7 +1288,7 @@ void FFmpegReader::ProcessVideoPacket(int64_t requested_frame) {
if (max_height <= 0)
max_height = info.height;
- Clip *parent = (Clip *) GetClip();
+ Clip *parent = (Clip *) GetParentClip();
if (parent) {
if (parent->scale == SCALE_FIT || parent->scale == SCALE_STRETCH) {
// Best fit or Stretch scaling (based on max timeline size * scaling keyframes)
@@ -1729,7 +1711,7 @@ void FFmpegReader::Seek(int64_t requested_frame) {
// Wait for any processing frames to complete
while (processing_video_frames_size + processing_audio_frames_size > 0) {
- usleep(2500);
+ std::this_thread::sleep_for(std::chrono::milliseconds(3));
const GenericScopedLock lock(processingCriticalSection);
processing_video_frames_size = processing_video_frames.size();
processing_audio_frames_size = processing_audio_frames.size();
@@ -1878,8 +1860,20 @@ void FFmpegReader::UpdatePTSOffset(bool is_video) {
// VIDEO PACKET
if (video_pts_offset == 99999) // Has the offset been set yet?
{
- // Find the difference between PTS and frame number (no more than 10 timebase units allowed)
- video_pts_offset = 0 - std::max(GetVideoPTS(), (int64_t) info.video_timebase.ToInt() * 10);
+ // Find the difference between PTS and frame number
+ video_pts_offset = 0 - GetVideoPTS();
+
+ // Find the difference between PTS and frame number
+ // Also, determine if PTS is invalid (too far away from zero)
+ // We compare the PTS to the timebase value equal to 1 second (which means the PTS
+ // must be within the -1 second to +1 second of zero, otherwise we ignore it)
+ // TODO: Please see https://github.com/OpenShot/libopenshot/pull/565#issuecomment-690985272
+ // for ideas to improve this logic.
+ int64_t max_offset = info.video_timebase.Reciprocal().ToFloat();
+ if (video_pts_offset < -max_offset || video_pts_offset > max_offset) {
+ // Ignore PTS, it seems invalid
+ video_pts_offset = 0;
+ }
// debug output
ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::UpdatePTSOffset (Video)", "video_pts_offset", video_pts_offset, "is_video", is_video);
@@ -1888,8 +1882,18 @@ void FFmpegReader::UpdatePTSOffset(bool is_video) {
// AUDIO PACKET
if (audio_pts_offset == 99999) // Has the offset been set yet?
{
- // Find the difference between PTS and frame number (no more than 10 timebase units allowed)
- audio_pts_offset = 0 - std::max(packet->pts, (int64_t) info.audio_timebase.ToInt() * 10);
+ // Find the difference between PTS and frame number
+ // Also, determine if PTS is invalid (too far away from zero)
+ // We compare the PTS to the timebase value equal to 1 second (which means the PTS
+ // must be within the -1 second to +1 second of zero, otherwise we ignore it)
+ // TODO: Please see https://github.com/OpenShot/libopenshot/pull/565#issuecomment-690985272
+ // for ideas to improve this logic.
+ audio_pts_offset = 0 - packet->pts;
+ int64_t max_offset = info.audio_timebase.Reciprocal().ToFloat();
+ if (audio_pts_offset < -max_offset || audio_pts_offset > max_offset) {
+ // Ignore PTS, it seems invalid
+ audio_pts_offset = 0;
+ }
// debug output
ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::UpdatePTSOffset (Audio)", "audio_pts_offset", audio_pts_offset, "is_video", is_video);
@@ -2145,7 +2149,7 @@ bool FFmpegReader::CheckMissingFrame(int64_t requested_frame) {
// Add this frame to the processed map (since it's already done)
std::shared_ptr parent_image = parent_frame->GetImage();
if (parent_image) {
- missing_frame->AddImage(std::shared_ptr(new QImage(*parent_image)));
+ missing_frame->AddImage(std::make_shared(*parent_image));
processed_video_frames[missing_frame->number] = missing_frame->number;
}
}
@@ -2235,7 +2239,7 @@ void FFmpegReader::CheckWorkingFrames(bool end_of_stream, int64_t requested_fram
if (info.has_video && !is_video_ready && last_video_frame) {
// Copy image from last frame
- f->AddImage(std::shared_ptr(new QImage(*last_video_frame->GetImage())));
+ f->AddImage(std::make_shared(*last_video_frame->GetImage()));
is_video_ready = true;
}
@@ -2257,7 +2261,7 @@ void FFmpegReader::CheckWorkingFrames(bool end_of_stream, int64_t requested_fram
// Add missing image (if needed - sometimes end_of_stream causes frames with only audio)
if (info.has_video && !is_video_ready && last_video_frame)
// Copy image from last frame
- f->AddImage(std::shared_ptr(new QImage(*last_video_frame->GetImage())));
+ f->AddImage(std::make_shared(*last_video_frame->GetImage()));
// Reset counter since last 'final' frame
num_checks_since_final = 0;
diff --git a/include/FFmpegReader.h b/src/FFmpegReader.h
similarity index 94%
rename from include/FFmpegReader.h
rename to src/FFmpegReader.h
index 76d3243a..e4718ea8 100644
--- a/include/FFmpegReader.h
+++ b/src/FFmpegReader.h
@@ -236,14 +236,13 @@ namespace openshot {
/// codecs have trouble seeking, and can introduce artifacts or blank images into the video.
bool enable_seek;
- /// Constructor for FFmpegReader. This automatically opens the media file and loads
- /// frame 1, or it throws one of the following exceptions.
- FFmpegReader(std::string path);
-
- /// Constructor for FFmpegReader. This only opens the media file to inspect its properties
- /// if inspect_reader=true. When not inspecting the media file, it's much faster, and useful
- /// when you are inflating the object using JSON after instantiating it.
- FFmpegReader(std::string path, bool inspect_reader);
+ /// @brief Constructor for FFmpegReader.
+ ///
+ /// Sets (and possibly opens) the media file path,
+ /// or throws an exception.
+ /// @param path The filesystem location to load
+ /// @param inspect_reader if true (the default), automatically open the media file and loads frame 1.
+ FFmpegReader(const std::string& path, bool inspect_reader=true);
/// Destructor
virtual ~FFmpegReader();
diff --git a/include/FFmpegUtilities.h b/src/FFmpegUtilities.h
similarity index 97%
rename from include/FFmpegUtilities.h
rename to src/FFmpegUtilities.h
index b4ec951f..c637f944 100644
--- a/include/FFmpegUtilities.h
+++ b/src/FFmpegUtilities.h
@@ -40,7 +40,7 @@
#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
@@ -122,6 +122,9 @@
#ifndef PIX_FMT_YUV420P
#define PIX_FMT_YUV420P AV_PIX_FMT_YUV420P
#endif
+ #ifndef PIX_FMT_YUV444P
+ #define PIX_FMT_YUV444P AV_PIX_FMT_YUV444P
+ #endif
// FFmpeg's libavutil/common.h defines an RSHIFT incompatible with Ruby's
// definition in ruby/config.h, so we move it to FF_RSHIFT
@@ -177,13 +180,13 @@
#define AV_OUTPUT_CONTEXT(output_context, path) avformat_alloc_output_context2( output_context, NULL, NULL, path)
#define AV_OPTION_FIND(priv_data, name) av_opt_find(priv_data, name, NULL, 0, 0)
#define AV_OPTION_SET( av_stream, priv_data, name, value, avcodec) av_opt_set(priv_data, name, value, 0); avcodec_parameters_from_context(av_stream->codecpar, avcodec);
- #define AV_FORMAT_NEW_STREAM(oc, st_codec, av_codec, av_st) av_st = avformat_new_stream(oc, NULL);\
+ #define AV_FORMAT_NEW_STREAM(oc, st_codec_ctx, av_codec, av_st) av_st = avformat_new_stream(oc, NULL);\
if (!av_st) \
throw OutOfMemory("Could not allocate memory for the video stream.", path); \
c = avcodec_alloc_context3(av_codec); \
- st_codec = c; \
+ st_codec_ctx = c; \
av_st->codecpar->codec_id = av_codec->id;
- #define AV_COPY_PARAMS_FROM_CONTEXT(av_stream, av_codec) avcodec_parameters_from_context(av_stream->codecpar, av_codec);
+ #define AV_COPY_PARAMS_FROM_CONTEXT(av_stream, av_codec_ctx) avcodec_parameters_from_context(av_stream->codecpar, av_codec_ctx);
#elif IS_FFMPEG_3_2
#define AV_REGISTER_ALL av_register_all();
#define AVCODEC_REGISTER_ALL avcodec_register_all();
diff --git a/src/FFmpegWriter.cpp b/src/FFmpegWriter.cpp
index ce8f6557..e5e92fd4 100644
--- a/src/FFmpegWriter.cpp
+++ b/src/FFmpegWriter.cpp
@@ -31,7 +31,9 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/FFmpegWriter.h"
+#include "FFmpegWriter.h"
+
+#include
using namespace openshot;
@@ -59,7 +61,7 @@ static int set_hwframe_ctx(AVCodecContext *ctx, AVBufferRef *hw_device_ctx, int6
int err = 0;
if (!(hw_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx))) {
- fprintf(stderr, "Failed to create HW frame context.\n");
+ std::clog << "Failed to create HW frame context.\n";
return -1;
}
frames_ctx = (AVHWFramesContext *)(hw_frames_ref->data);
@@ -69,8 +71,8 @@ static int set_hwframe_ctx(AVCodecContext *ctx, AVBufferRef *hw_device_ctx, int6
frames_ctx->height = height;
frames_ctx->initial_pool_size = 20;
if ((err = av_hwframe_ctx_init(hw_frames_ref)) < 0) {
- fprintf(stderr, "Failed to initialize HW frame context."
- "Error code: %s\n",av_err2str(err));
+ std::clog << "Failed to initialize HW frame context. " <<
+ "Error code: " << av_err2str(err) << "\n";
av_buffer_unref(&hw_frames_ref);
return err;
}
@@ -83,11 +85,11 @@ static int set_hwframe_ctx(AVCodecContext *ctx, AVBufferRef *hw_device_ctx, int6
}
#endif // HAVE_HW_ACCEL
-FFmpegWriter::FFmpegWriter(std::string path) :
+FFmpegWriter::FFmpegWriter(const std::string& path) :
path(path), fmt(NULL), oc(NULL), audio_st(NULL), video_st(NULL), samples(NULL),
audio_outbuf(NULL), audio_outbuf_size(0), audio_input_frame_size(0), audio_input_position(0),
initial_audio_input_frame_size(0), img_convert_ctx(NULL), cache_size(8), num_of_rescalers(32),
- rescaler_position(0), video_codec(NULL), audio_codec(NULL), is_writing(false), write_video_count(0), write_audio_count(0),
+ rescaler_position(0), video_codec_ctx(NULL), audio_codec_ctx(NULL), is_writing(false), write_video_count(0), write_audio_count(0),
original_sample_rate(0), original_channels(0), avr(NULL), avr_planar(NULL), is_open(false), prepare_streams(false),
write_header(false), write_trailer(false), audio_encoder_buffer_size(0), audio_encoder_buffer(NULL) {
@@ -95,6 +97,12 @@ FFmpegWriter::FFmpegWriter(std::string path) :
info.has_audio = false;
info.has_video = false;
+ // Configure OpenMP parallelism
+ // Default number of threads per block
+ omp_set_num_threads(OPEN_MP_NUM_PROCESSORS);
+ // Allow nested parallel sections as deeply as supported
+ omp_set_max_active_levels(OPEN_MP_MAX_ACTIVE);
+
// Initialize FFMpeg, and register all formats and codecs
AV_REGISTER_ALL
@@ -220,10 +228,10 @@ void FFmpegWriter::SetVideoOptions(bool has_video, std::string codec, Fraction f
hw_en_on = 0;
hw_en_supported = 0;
}
- #else // is FFmpeg 3 but not linux
+#else // unknown OS
new_codec = avcodec_find_encoder_by_name(codec.c_str());
- #endif //__linux__
-#else // not ffmpeg 3
+#endif //__linux__/_WIN32/__APPLE__
+#else // HAVE_HW_ACCEL
new_codec = avcodec_find_encoder_by_name(codec.c_str());
#endif // HAVE_HW_ACCEL
if (new_codec == NULL)
@@ -253,9 +261,9 @@ void FFmpegWriter::SetVideoOptions(bool has_video, std::string codec, Fraction f
info.pixel_ratio.num = pixel_ratio.num;
info.pixel_ratio.den = pixel_ratio.den;
}
- if (bit_rate >= 1000) // bit_rate is the bitrate in b/s
+ if (bit_rate >= 1000) // bit_rate is the bitrate in b/s
info.video_bit_rate = bit_rate;
- if ((bit_rate >= 0) && (bit_rate < 64)) // bit_rate is the bitrate in crf
+ if ((bit_rate >= 0) && (bit_rate < 256)) // bit_rate is the bitrate in crf
info.video_bit_rate = bit_rate;
info.interlaced_frame = interlaced;
@@ -280,8 +288,10 @@ void FFmpegWriter::SetVideoOptions(bool has_video, std::string codec, Fraction f
// 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);
+ FFmpegWriter::SetVideoOptions(
+ true, codec, fps, width, height,
+ openshot::Fraction(1, 1), false, true, bit_rate
+ );
}
@@ -314,7 +324,11 @@ void FFmpegWriter::SetAudioOptions(bool has_audio, std::string codec, int sample
if (original_channels == 0)
original_channels = info.channels;
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::SetAudioOptions (" + codec + ")", "sample_rate", sample_rate, "channels", channels, "bit_rate", bit_rate);
+ ZmqLogger::Instance()->AppendDebugMethod(
+ "FFmpegWriter::SetAudioOptions (" + codec + ")",
+ "sample_rate", sample_rate,
+ "channels", channels,
+ "bit_rate", bit_rate);
// Enable / Disable audio
info.has_audio = has_audio;
@@ -324,8 +338,10 @@ void FFmpegWriter::SetAudioOptions(bool has_audio, std::string codec, int sample
// 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);
+ FFmpegWriter::SetAudioOptions(
+ true, codec, sample_rate, 2,
+ openshot::LAYOUT_STEREO, bit_rate
+ );
}
@@ -339,11 +355,19 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va
if (info.has_video && stream == VIDEO_STREAM && video_st) {
st = video_st;
// Get codec context
- c = AV_GET_CODEC_PAR_CONTEXT(st, video_codec);
+ c = AV_GET_CODEC_PAR_CONTEXT(st, video_codec_ctx);
+ // Was a codec / stream found?
+ if (c) {
+ if (info.interlaced_frame) {
+ c->field_order = info.top_field_first ? AV_FIELD_TT : AV_FIELD_BB;
+ // We only use these two version and ignore AV_FIELD_TB and AV_FIELD_BT
+ // Otherwise we would need to change the whole export window
+ }
+ }
} else if (info.has_audio && stream == AUDIO_STREAM && audio_st) {
st = audio_st;
// Get codec context
- c = AV_GET_CODEC_PAR_CONTEXT(st, audio_codec);
+ c = AV_GET_CODEC_PAR_CONTEXT(st, audio_codec_ctx);
} else
throw NoStreamsFound("The stream was not found. Be sure to call PrepareStreams() first.", path);
@@ -358,7 +382,7 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va
// Was option found?
if (option || (name == "g" || name == "qmin" || name == "qmax" || name == "max_b_frames" || name == "mb_decision" ||
name == "level" || name == "profile" || name == "slices" || name == "rc_min_rate" || name == "rc_max_rate" ||
- name == "rc_buffer_size" || name == "crf" || name == "cqp")) {
+ name == "rc_buffer_size" || name == "crf" || name == "cqp" || name == "qp")) {
// Check for specific named options
if (name == "g")
// Set gop_size
@@ -408,57 +432,56 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va
// encode quality and special settings like lossless
// 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 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 // HAVE_HW_ACCEL
- {
+#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 // HAVE_HW_ACCEL
+ {
switch (c->codec_id) {
- #if (LIBAVCODEC_VERSION_MAJOR >= 58)
- case AV_CODEC_ID_AV1 :
- c->bit_rate = 0;
- av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value),63), 0); // 0-63
- break;
- #endif
- case AV_CODEC_ID_VP8 :
- c->bit_rate = 10000000;
- av_opt_set_int(c->priv_data, "qp", std::max(std::min(std::stoi(value), 63), 4), 0); // 4-63
- break;
- case AV_CODEC_ID_VP9 :
- c->bit_rate = 0; // Must be zero!
- av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value), 63), 0); // 0-63
- if (std::stoi(value) == 0) {
- av_opt_set(c->priv_data, "preset", "veryslow", 0);
- av_opt_set_int(c->priv_data, "lossless", 1, 0);
- }
- break;
- case AV_CODEC_ID_H264 :
- 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);
- }
- break;
- 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);
- av_opt_set_int(c->priv_data, "lossless", 1, 0);
- }
- break;
- default:
- // For all other codecs assume a range of 0-63
- av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value), 63), 0); // 0-63
- c->bit_rate = 0;
+#if (LIBAVCODEC_VERSION_MAJOR >= 58)
+ // FFmpeg 4.0+
+ case AV_CODEC_ID_AV1 :
+ c->bit_rate = 0;
+ av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value),63), 0); // 0-63
+ break;
+#endif
+ case AV_CODEC_ID_VP8 :
+ c->bit_rate = 10000000;
+ av_opt_set_int(c->priv_data, "qp", std::max(std::min(std::stoi(value), 63), 4), 0); // 4-63
+ break;
+ case AV_CODEC_ID_VP9 :
+ c->bit_rate = 0; // Must be zero!
+ av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value), 63), 0); // 0-63
+ if (std::stoi(value) == 0) {
+ av_opt_set(c->priv_data, "preset", "veryslow", 0);
+ av_opt_set_int(c->priv_data, "lossless", 1, 0);
+ }
+ break;
+ case AV_CODEC_ID_H264 :
+ 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);
+ c->pix_fmt = PIX_FMT_YUV444P; // no chroma subsampling
+ }
+ break;
+ 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);
+ av_opt_set_int(c->priv_data, "lossless", 1, 0);
+ }
+ break;
+ default:
+ // For all other codecs assume a range of 0-63
+ av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value), 63), 0); // 0-63
+ c->bit_rate = 0;
}
}
- #endif
} else if (name == "crf") {
// encode quality and special settings like lossless
// 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 HAVE_HW_ACCEL
if (hw_en_on) {
double mbs = 15000000.0;
@@ -476,8 +499,10 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va
{
switch (c->codec_id) {
#if (LIBAVCODEC_VERSION_MAJOR >= 58)
+ // FFmpeg 4.0+
case AV_CODEC_ID_AV1 :
c->bit_rate = 0;
+ // AV1 only supports "crf" quality values
av_opt_set_int(c->priv_data, "crf", std::min(std::stoi(value),63), 0);
break;
#endif
@@ -497,10 +522,18 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va
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);
+ c->pix_fmt = PIX_FMT_YUV444P; // no chroma subsampling
}
break;
case AV_CODEC_ID_HEVC :
- av_opt_set_int(c->priv_data, "crf", std::min(std::stoi(value), 51), 0); // 0-51
+ if (strstr(info.vcodec.c_str(), "svt_hevc") != NULL) {
+ av_opt_set_int(c->priv_data, "preset", 7, 0);
+ av_opt_set_int(c->priv_data, "forced-idr",1,0);
+ av_opt_set_int(c->priv_data, "qp",std::min(std::stoi(value), 51),0);
+ }
+ else {
+ 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);
av_opt_set_int(c->priv_data, "lossless", 1, 0);
@@ -520,7 +553,42 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va
c->bit_rate = (int) (mbs);
}
}
-#endif
+ } else if (name == "qp") {
+ // encode quality and special settings like lossless
+ // This might be better in an extra methods as more options
+ // and way to set quality are possible
+#if (LIBAVCODEC_VERSION_MAJOR >= 58)
+ // FFmpeg 4.0+
+ switch (c->codec_id) {
+ case AV_CODEC_ID_AV1 :
+ c->bit_rate = 0;
+ if (strstr(info.vcodec.c_str(), "svtav1") != NULL) {
+ av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value),63), 0);
+ }
+ else if (strstr(info.vcodec.c_str(), "rav1e") != NULL) {
+ // Set number of tiles to a fixed value
+ // TODO Let user choose number of tiles
+ av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value),255), 0);
+ }
+ else if (strstr(info.vcodec.c_str(), "aom") != NULL) {
+ // Set number of tiles to a fixed value
+ // TODO Let user choose number of tiles
+ // libaom doesn't have qp only crf
+ av_opt_set_int(c->priv_data, "crf", std::min(std::stoi(value),63), 0);
+ }
+ else {
+ av_opt_set_int(c->priv_data, "crf", std::min(std::stoi(value),63), 0);
+ }
+ case AV_CODEC_ID_HEVC :
+ c->bit_rate = 0;
+ if (strstr(info.vcodec.c_str(), "svt_hevc") != NULL) {
+ av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value),51), 0);
+ av_opt_set_int(c->priv_data, "preset", 7, 0);
+ av_opt_set_int(c->priv_data, "forced-idr",1,0);
+ }
+ break;
+ }
+#endif // FFmpeg 4.0+
} else {
// Set AVOption
AV_OPTION_SET(st, c->priv_data, name.c_str(), value.c_str(), c);
@@ -633,15 +701,8 @@ void FFmpegWriter::WriteFrame(std::shared_ptr frame) {
// Write the frames once it reaches the correct 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
- write_queued_frames();
-
- else {
- // Write frames to video file
- write_queued_frames();
- }
+ // Write frames to video file
+ write_queued_frames();
}
// Keep track of the last frame added
@@ -663,11 +724,6 @@ void FFmpegWriter::write_queued_frames() {
spooled_video_frames.clear();
spooled_audio_frames.clear();
- // Set the number of threads in OpenMP
- omp_set_num_threads(OPEN_MP_NUM_PROCESSORS);
- // Allow nested OpenMP sections
- omp_set_nested(true);
-
// Create blank exception
bool has_error_encoding_video = false;
@@ -796,31 +852,29 @@ void FFmpegWriter::WriteTrailer() {
// Flush encoders
void FFmpegWriter::flush_encoders() {
- if (info.has_audio && audio_codec && AV_GET_CODEC_TYPE(audio_st) == AVMEDIA_TYPE_AUDIO && AV_GET_CODEC_ATTRIBUTES(audio_st, audio_codec)->frame_size <= 1)
+ if (info.has_audio && audio_codec_ctx && AV_GET_CODEC_TYPE(audio_st) == AVMEDIA_TYPE_AUDIO && AV_GET_CODEC_ATTRIBUTES(audio_st, audio_codec_ctx)->frame_size <= 1)
return;
#if (LIBAVFORMAT_VERSION_MAJOR < 58)
- if (info.has_video && video_codec && AV_GET_CODEC_TYPE(video_st) == AVMEDIA_TYPE_VIDEO && (oc->oformat->flags & AVFMT_RAWPICTURE) && AV_FIND_DECODER_CODEC_ID(video_st) == AV_CODEC_ID_RAWVIDEO)
+ // FFmpeg < 4.0
+ if (info.has_video && video_codec_ctx && AV_GET_CODEC_TYPE(video_st) == AVMEDIA_TYPE_VIDEO && (oc->oformat->flags & AVFMT_RAWPICTURE) && AV_FIND_DECODER_CODEC_ID(video_st) == AV_CODEC_ID_RAWVIDEO)
+ return;
+#else
+ if (info.has_video && video_codec_ctx && AV_GET_CODEC_TYPE(video_st) == AVMEDIA_TYPE_VIDEO && AV_FIND_DECODER_CODEC_ID(video_st) == AV_CODEC_ID_RAWVIDEO)
return;
#endif
- int error_code = 0;
- int stop_encoding = 1;
-
// FLUSH VIDEO ENCODER
if (info.has_video)
for (;;) {
// Increment PTS (in frames and scaled to the codec's timebase)
- write_video_count += av_rescale_q(1, (AVRational) {info.fps.den, info.fps.num}, video_codec->time_base);
+ write_video_count += av_rescale_q(1, av_make_q(info.fps.den, info.fps.num), video_codec_ctx->time_base);
AVPacket pkt;
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
- // Pointer for video buffer (if using old FFmpeg version)
- uint8_t *video_outbuf = NULL;
-
/* encode the image */
int got_packet = 0;
int error_code = 0;
@@ -829,70 +883,47 @@ void FFmpegWriter::flush_encoders() {
#pragma omp critical (write_video_packet)
{
// Encode video packet (latest version of FFmpeg)
- error_code = avcodec_send_frame(video_codec, NULL);
+ error_code = avcodec_send_frame(video_codec_ctx, NULL);
got_packet = 0;
while (error_code >= 0) {
- error_code = avcodec_receive_packet(video_codec, &pkt);
+ error_code = avcodec_receive_packet(video_codec_ctx, &pkt);
if (error_code == AVERROR(EAGAIN)|| error_code == AVERROR_EOF) {
got_packet = 0;
// Write packet
- avcodec_flush_buffers(video_codec);
+ avcodec_flush_buffers(video_codec_ctx);
break;
}
if (pkt.pts != AV_NOPTS_VALUE)
- pkt.pts = av_rescale_q(pkt.pts, video_codec->time_base, video_st->time_base);
+ pkt.pts = av_rescale_q(pkt.pts, video_codec_ctx->time_base, video_st->time_base);
if (pkt.dts != AV_NOPTS_VALUE)
- pkt.dts = av_rescale_q(pkt.dts, video_codec->time_base, video_st->time_base);
+ pkt.dts = av_rescale_q(pkt.dts, video_codec_ctx->time_base, video_st->time_base);
if (pkt.duration > 0)
- pkt.duration = av_rescale_q(pkt.duration, video_codec->time_base, video_st->time_base);
+ pkt.duration = av_rescale_q(pkt.duration, video_codec_ctx->time_base, video_st->time_base);
pkt.stream_index = video_st->index;
error_code = av_interleaved_write_frame(oc, &pkt);
}
}
#else // IS_FFMPEG_3_2
-#if LIBAVFORMAT_VERSION_MAJOR >= 54
// Encode video packet (older than FFmpeg 3.2)
- error_code = avcodec_encode_video2(video_codec, &pkt, NULL, &got_packet);
+ error_code = avcodec_encode_video2(video_codec_ctx, &pkt, NULL, &got_packet);
-#else
- // Encode video packet (even older version of FFmpeg)
- int video_outbuf_size = 0;
-
- /* encode the image */
- int out_size = avcodec_encode_video(video_codec, NULL, video_outbuf_size, NULL);
-
- /* if zero size, it means the image was buffered */
- if (out_size > 0) {
- if(video_codec->coded_frame->key_frame)
- pkt.flags |= AV_PKT_FLAG_KEY;
- pkt.data= video_outbuf;
- pkt.size= out_size;
-
- // got data back (so encode this frame)
- got_packet = 1;
- }
-#endif // LIBAVFORMAT_VERSION_MAJOR >= 54
#endif // IS_FFMPEG_3_2
if (error_code < 0) {
ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::flush_encoders ERROR [" + (std::string) av_err2str(error_code) + "]", "error_code", error_code);
}
if (!got_packet) {
- stop_encoding = 1;
break;
}
- // Override PTS (in frames and scaled to the codec's timebase)
- //pkt.pts = write_video_count;
-
// set the timestamp
if (pkt.pts != AV_NOPTS_VALUE)
- pkt.pts = av_rescale_q(pkt.pts, video_codec->time_base, video_st->time_base);
+ pkt.pts = av_rescale_q(pkt.pts, video_codec_ctx->time_base, video_st->time_base);
if (pkt.dts != AV_NOPTS_VALUE)
- pkt.dts = av_rescale_q(pkt.dts, video_codec->time_base, video_st->time_base);
+ pkt.dts = av_rescale_q(pkt.dts, video_codec_ctx->time_base, video_st->time_base);
if (pkt.duration > 0)
- pkt.duration = av_rescale_q(pkt.duration, video_codec->time_base, video_st->time_base);
+ pkt.duration = av_rescale_q(pkt.duration, video_codec_ctx->time_base, video_st->time_base);
pkt.stream_index = video_st->index;
// Write packet
@@ -900,10 +931,6 @@ void FFmpegWriter::flush_encoders() {
if (error_code < 0) {
ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::flush_encoders ERROR [" + (std::string)av_err2str(error_code) + "]", "error_code", error_code);
}
-
- // Deallocate memory (if needed)
- if (video_outbuf)
- av_freep(&video_outbuf);
}
// FLUSH AUDIO ENCODER
@@ -911,12 +938,8 @@ void FFmpegWriter::flush_encoders() {
for (;;) {
// Increment PTS (in samples and scaled to the codec's timebase)
-#if LIBAVFORMAT_VERSION_MAJOR >= 54
// for some reason, it requires me to multiply channels X 2
- write_audio_count += av_rescale_q(audio_input_position / (audio_codec->channels * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16)), (AVRational){1, info.sample_rate}, audio_codec->time_base);
-#else
- write_audio_count += av_rescale_q(audio_input_position / audio_codec->channels, (AVRational){1, info.sample_rate}, audio_codec->time_base);
-#endif
+ write_audio_count += av_rescale_q(audio_input_position / (audio_codec_ctx->channels * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16)), av_make_q(1, info.sample_rate), audio_codec_ctx->time_base);
AVPacket pkt;
av_init_packet(&pkt);
@@ -925,18 +948,17 @@ void FFmpegWriter::flush_encoders() {
pkt.pts = pkt.dts = write_audio_count;
/* encode the image */
+ int error_code = 0;
int got_packet = 0;
#if IS_FFMPEG_3_2
- avcodec_send_frame(audio_codec, NULL);
- got_packet = 0;
+ error_code = avcodec_send_frame(audio_codec_ctx, NULL);
#else
- error_code = avcodec_encode_audio2(audio_codec, &pkt, NULL, &got_packet);
+ error_code = avcodec_encode_audio2(audio_codec_ctx, &pkt, NULL, &got_packet);
#endif
if (error_code < 0) {
ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::flush_encoders ERROR [" + (std::string)av_err2str(error_code) + "]", "error_code", error_code);
}
if (!got_packet) {
- stop_encoding = 1;
break;
}
@@ -946,11 +968,11 @@ void FFmpegWriter::flush_encoders() {
// Scale the PTS to the audio stream timebase (which is sometimes different than the codec's timebase)
if (pkt.pts != AV_NOPTS_VALUE)
- pkt.pts = av_rescale_q(pkt.pts, audio_codec->time_base, audio_st->time_base);
+ pkt.pts = av_rescale_q(pkt.pts, audio_codec_ctx->time_base, audio_st->time_base);
if (pkt.dts != AV_NOPTS_VALUE)
- pkt.dts = av_rescale_q(pkt.dts, audio_codec->time_base, audio_st->time_base);
+ pkt.dts = av_rescale_q(pkt.dts, audio_codec_ctx->time_base, audio_st->time_base);
if (pkt.duration > 0)
- pkt.duration = av_rescale_q(pkt.duration, audio_codec->time_base, audio_st->time_base);
+ pkt.duration = av_rescale_q(pkt.duration, audio_codec_ctx->time_base, audio_st->time_base);
// set stream
pkt.stream_index = audio_st->index;
@@ -1067,15 +1089,16 @@ AVStream *FFmpegWriter::add_audio_stream() {
if (codec == NULL)
throw InvalidCodec("A valid audio codec could not be found for this file.", path);
+ // Free any previous memory allocations
+ if (audio_codec_ctx != NULL) {
+ AV_FREE_CONTEXT(audio_codec_ctx);
+ }
+
// Create a new audio stream
- AV_FORMAT_NEW_STREAM(oc, audio_codec, codec, st)
+ AV_FORMAT_NEW_STREAM(oc, audio_codec_ctx, codec, st)
c->codec_id = codec->id;
-#if LIBAVFORMAT_VERSION_MAJOR >= 53
c->codec_type = AVMEDIA_TYPE_AUDIO;
-#else
- c->codec_type = CODEC_TYPE_AUDIO;
-#endif
// Set the sample parameters
c->bit_rate = info.audio_bit_rate;
@@ -1129,6 +1152,7 @@ AVStream *FFmpegWriter::add_audio_stream() {
// some formats want stream headers to be separate
if (oc->oformat->flags & AVFMT_GLOBALHEADER)
#if (LIBAVCODEC_VERSION_MAJOR >= 57)
+ // FFmpeg 3.0+
c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
#else
c->flags |= CODEC_FLAG_GLOBAL_HEADER;
@@ -1151,34 +1175,70 @@ AVStream *FFmpegWriter::add_video_stream() {
throw InvalidCodec("A valid video codec could not be found for this file.", path);
// Create a new video stream
- AV_FORMAT_NEW_STREAM(oc, video_codec, codec, st)
+ AV_FORMAT_NEW_STREAM(oc, video_codec_ctx, codec, st)
c->codec_id = codec->id;
-#if LIBAVFORMAT_VERSION_MAJOR >= 53
c->codec_type = AVMEDIA_TYPE_VIDEO;
-#else
- c->codec_type = CODEC_TYPE_VIDEO;
-#endif
/* Init video encoder options */
- if (info.video_bit_rate >= 1000) {
+ if (info.video_bit_rate >= 1000
+#if (LIBAVCODEC_VERSION_MAJOR >= 58)
+ && c->codec_id != AV_CODEC_ID_AV1
+#endif
+ ) {
c->bit_rate = info.video_bit_rate;
if (info.video_bit_rate >= 1500000) {
- c->qmin = 2;
- c->qmax = 30;
+ if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
+ c->qmin = 2;
+ c->qmax = 30;
+ }
}
// Here should be the setting for low fixed bitrate
// Defaults are used because mpeg2 otherwise had problems
} else {
- // Check if codec supports crf
+ // Check if codec supports crf or qp
switch (c->codec_id) {
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 39, 101)
#if (LIBAVCODEC_VERSION_MAJOR >= 58)
+ // FFmpeg 4.0+
case AV_CODEC_ID_AV1 :
+ // TODO: Set `crf` or `qp` according to bitrate, as bitrate is not supported by these encoders yet.
+ if (info.video_bit_rate >= 1000) {
+ c->bit_rate = 0;
+ if (strstr(info.vcodec.c_str(), "aom") != NULL) {
+ int calculated_quality = 35;
+ if (info.video_bit_rate < 500000) calculated_quality = 50;
+ if (info.video_bit_rate > 5000000) calculated_quality = 10;
+ av_opt_set_int(c->priv_data, "crf", calculated_quality, 0);
+ info.video_bit_rate = calculated_quality;
+ } else {
+ int calculated_quality = 50;
+ if (info.video_bit_rate < 500000) calculated_quality = 60;
+ if (info.video_bit_rate > 5000000) calculated_quality = 15;
+ av_opt_set_int(c->priv_data, "qp", calculated_quality, 0);
+ info.video_bit_rate = calculated_quality;
+ } // medium
+ }
+ if (strstr(info.vcodec.c_str(), "svtav1") != NULL) {
+ av_opt_set_int(c->priv_data, "preset", 6, 0);
+ av_opt_set_int(c->priv_data, "forced-idr",1,0);
+ }
+ else if (strstr(info.vcodec.c_str(), "rav1e") != NULL) {
+ av_opt_set_int(c->priv_data, "speed", 7, 0);
+ av_opt_set_int(c->priv_data, "tile-rows", 2, 0);
+ av_opt_set_int(c->priv_data, "tile-columns", 4, 0);
+ }
+ else if (strstr(info.vcodec.c_str(), "aom") != NULL) {
+ // Set number of tiles to a fixed value
+ // TODO: Allow user to chose their own number of tiles
+ av_opt_set_int(c->priv_data, "tile-rows", 1, 0); // log2 of number of rows
+ av_opt_set_int(c->priv_data, "tile-columns", 2, 0); // log2 of number of columns
+ av_opt_set_int(c->priv_data, "row-mt", 1, 0); // use multiple cores
+ av_opt_set_int(c->priv_data, "cpu-used", 3, 0); // default is 1, usable is 4
+ }
+ //break;
#endif
case AV_CODEC_ID_VP9 :
case AV_CODEC_ID_HEVC :
-#endif
case AV_CODEC_ID_VP8 :
case AV_CODEC_ID_H264 :
if (info.video_bit_rate < 40) {
@@ -1215,13 +1275,20 @@ AVStream *FFmpegWriter::add_video_stream() {
identically 1. */
c->time_base.num = info.video_timebase.num;
c->time_base.den = info.video_timebase.den;
-// AVCodecContext->framerate was added in FFmpeg 2.2
+// AVCodecContext->framerate was added in FFmpeg 2.6
#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);
st->time_base.num = info.video_timebase.num;
st->time_base.den = info.video_timebase.den;
+#if (LIBAVFORMAT_VERSION_MAJOR >= 58)
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ st->codec->time_base.num = info.video_timebase.num;
+ st->codec->time_base.den = info.video_timebase.den;
+ #pragma GCC diagnostic pop
+#endif
c->gop_size = 12; /* TODO: add this to "info"... emit one intra frame every twelve frames at most */
c->max_b_frames = 10;
@@ -1236,6 +1303,7 @@ AVStream *FFmpegWriter::add_video_stream() {
// some formats want stream headers to be separate
if (oc->oformat->flags & AVFMT_GLOBALHEADER)
#if (LIBAVCODEC_VERSION_MAJOR >= 57)
+ // FFmpeg 3.0+
c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
#else
c->flags |= CODEC_FLAG_GLOBAL_HEADER;
@@ -1257,6 +1325,7 @@ AVStream *FFmpegWriter::add_video_stream() {
c->pix_fmt = PIX_FMT_RGB24;
#if (LIBAVFORMAT_VERSION_MAJOR < 58)
+ // FFmpeg < 4.0
if (strcmp(fmt->name, "gif") != 0)
// If not GIF format, skip the encoding process
// Set raw picture flag (so we don't encode this video)
@@ -1270,6 +1339,7 @@ AVStream *FFmpegWriter::add_video_stream() {
AV_COPY_PARAMS_FROM_CONTEXT(st, c);
#if (LIBAVFORMAT_VERSION_MAJOR < 58)
+ // FFmpeg < 4.0
ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::add_video_stream (" + (std::string)fmt->name + " : " + (std::string)av_get_pix_fmt_name(c->pix_fmt) + ")", "c->codec_id", c->codec_id, "c->bit_rate", c->bit_rate, "c->pix_fmt", c->pix_fmt, "oc->oformat->flags", oc->oformat->flags, "AVFMT_RAWPICTURE", AVFMT_RAWPICTURE);
#else
ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::add_video_stream (" + (std::string)fmt->name + " : " + (std::string)av_get_pix_fmt_name(c->pix_fmt) + ")", "c->codec_id", c->codec_id, "c->bit_rate", c->bit_rate, "c->pix_fmt", c->pix_fmt, "oc->oformat->flags", oc->oformat->flags);
@@ -1281,15 +1351,15 @@ AVStream *FFmpegWriter::add_video_stream() {
// open audio codec
void FFmpegWriter::open_audio(AVFormatContext *oc, AVStream *st) {
AVCodec *codec;
- AV_GET_CODEC_FROM_STREAM(st, audio_codec)
+ AV_GET_CODEC_FROM_STREAM(st, audio_codec_ctx)
// Set number of threads equal to number of processors (not to exceed 16)
- audio_codec->thread_count = std::min(FF_NUM_PROCESSORS, 16);
+ audio_codec_ctx->thread_count = std::min(FF_NUM_PROCESSORS, 16);
// Find the audio encoder
codec = avcodec_find_encoder_by_name(info.acodec.c_str());
if (!codec)
- codec = avcodec_find_encoder(audio_codec->codec_id);
+ codec = avcodec_find_encoder(audio_codec_ctx->codec_id);
if (!codec)
throw InvalidCodec("Could not find codec", path);
@@ -1298,16 +1368,16 @@ void FFmpegWriter::open_audio(AVFormatContext *oc, AVStream *st) {
av_dict_set(&opts, "strict", "experimental", 0);
// Open the codec
- if (avcodec_open2(audio_codec, codec, &opts) < 0)
+ if (avcodec_open2(audio_codec_ctx, codec, &opts) < 0)
throw InvalidCodec("Could not open audio codec", path);
- AV_COPY_PARAMS_FROM_CONTEXT(st, audio_codec);
+ AV_COPY_PARAMS_FROM_CONTEXT(st, audio_codec_ctx);
// Free options
av_dict_free(&opts);
// Calculate the size of the input frame (i..e how many samples per packet), and the output buffer
// TODO: Ugly hack for PCM codecs (will be removed ASAP with new PCM support to compute the input frame size in samples
- if (audio_codec->frame_size <= 1) {
+ if (audio_codec_ctx->frame_size <= 1) {
// No frame size found... so calculate
audio_input_frame_size = 50000 / info.channels;
@@ -1324,7 +1394,7 @@ void FFmpegWriter::open_audio(AVFormatContext *oc, AVStream *st) {
}
} else {
// Set frame size based on the codec
- audio_input_frame_size = audio_codec->frame_size;
+ audio_input_frame_size = audio_codec_ctx->frame_size;
}
// Set the initial frame size (since it might change during resampling)
@@ -1346,16 +1416,16 @@ void FFmpegWriter::open_audio(AVFormatContext *oc, AVStream *st) {
av_dict_set(&st->metadata, iter->first.c_str(), iter->second.c_str(), 0);
}
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::open_audio", "audio_codec->thread_count", audio_codec->thread_count, "audio_input_frame_size", audio_input_frame_size, "buffer_size", AVCODEC_MAX_AUDIO_FRAME_SIZE + MY_INPUT_BUFFER_PADDING_SIZE);
+ ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::open_audio", "audio_codec_ctx->thread_count", audio_codec_ctx->thread_count, "audio_input_frame_size", audio_input_frame_size, "buffer_size", AVCODEC_MAX_AUDIO_FRAME_SIZE + MY_INPUT_BUFFER_PADDING_SIZE);
}
// open video codec
void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) {
AVCodec *codec;
- AV_GET_CODEC_FROM_STREAM(st, video_codec)
+ AV_GET_CODEC_FROM_STREAM(st, video_codec_ctx)
// Set number of threads equal to number of processors (not to exceed 16)
- video_codec->thread_count = std::min(FF_NUM_PROCESSORS, 16);
+ video_codec_ctx->thread_count = std::min(FF_NUM_PROCESSORS, 16);
#if HAVE_HW_ACCEL
if (hw_en_on && hw_en_supported) {
@@ -1365,15 +1435,13 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) {
int adapter_num;
// Use the hw device given in the environment variable HW_EN_DEVICE_SET or the default if not set
adapter_num = openshot::Settings::Instance()->HW_EN_DEVICE_SET;
- fprintf(stderr, "\n\nEncodiing Device Nr: %d\n", adapter_num);
+ std::clog << "Encoding Device Nr: " << adapter_num << "\n";
if (adapter_num < 3 && adapter_num >=0) {
#if defined(__linux__)
snprintf(adapter,sizeof(adapter),"/dev/dri/renderD%d", adapter_num+128);
// Maybe 127 is better because the first card would be 1?!
adapter_ptr = adapter;
-#elif defined(_WIN32)
- adapter_ptr = NULL;
-#elif defined(__APPLE__)
+#elif defined(_WIN32) || defined(__APPLE__)
adapter_ptr = NULL;
#endif
}
@@ -1383,20 +1451,18 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) {
// Check if it is there and writable
#if defined(__linux__)
if( adapter_ptr != NULL && access( adapter_ptr, W_OK ) == 0 ) {
-#elif defined(_WIN32)
- if( adapter_ptr != NULL ) {
-#elif defined(__APPLE__)
+#elif defined(_WIN32) || defined(__APPLE__)
if( adapter_ptr != NULL ) {
#endif
ZmqLogger::Instance()->AppendDebugMethod("Encode Device present using device", "adapter", adapter_num);
}
else {
adapter_ptr = NULL; // use default
- ZmqLogger::Instance()->AppendDebugMethod("Encode Device not present using default");
+ ZmqLogger::Instance()->AppendDebugMethod("Encode Device not present, using default");
}
if (av_hwdevice_ctx_create(&hw_device_ctx, hw_en_av_device_type,
adapter_ptr, NULL, 0) < 0) {
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::open_video : Codec name: ", info.vcodec.c_str(), -1, " ERROR creating\n", -1);
+ ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::open_video ERROR creating hwdevice, Codec name:", info.vcodec.c_str(), -1);
throw InvalidCodec("Could not create hwdevice", path);
}
}
@@ -1410,8 +1476,8 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) {
throw InvalidCodec("Could not find codec", path);
/* Force max_b_frames to 0 in some cases (i.e. for mjpeg image sequences */
- if (video_codec->max_b_frames && video_codec->codec_id != AV_CODEC_ID_MPEG4 && video_codec->codec_id != AV_CODEC_ID_MPEG1VIDEO && video_codec->codec_id != AV_CODEC_ID_MPEG2VIDEO)
- video_codec->max_b_frames = 0;
+ if (video_codec_ctx->max_b_frames && video_codec_ctx->codec_id != AV_CODEC_ID_MPEG4 && video_codec_ctx->codec_id != AV_CODEC_ID_MPEG1VIDEO && video_codec_ctx->codec_id != AV_CODEC_ID_MPEG2VIDEO)
+ video_codec_ctx->max_b_frames = 0;
// Init options
AVDictionary *opts = NULL;
@@ -1419,7 +1485,7 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) {
#if HAVE_HW_ACCEL
if (hw_en_on && hw_en_supported) {
- video_codec->pix_fmt = hw_en_av_pix_fmt;
+ video_codec_ctx->pix_fmt = hw_en_av_pix_fmt;
// for the list of possible options, see the list of codec-specific options:
// e.g. ffmpeg -h encoder=h264_vaapi or ffmpeg -h encoder=hevc_vaapi
@@ -1429,23 +1495,23 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) {
// which is ffmpeg version-specific.
if (hw_en_av_pix_fmt == AV_PIX_FMT_VAAPI) {
int64_t qp;
- if (av_opt_get_int(video_codec->priv_data, "qp", 0, &qp) != 0 || qp == 0) {
+ if (av_opt_get_int(video_codec_ctx->priv_data, "qp", 0, &qp) != 0 || qp == 0) {
// unless "qp" was set for CQP, switch to VBR RC mode
- av_opt_set(video_codec->priv_data, "rc_mode", "VBR", 0);
+ av_opt_set(video_codec_ctx->priv_data, "rc_mode", "VBR", 0);
// 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;
+ video_codec_ctx->rc_max_rate = video_codec_ctx->bit_rate;
}
}
- switch (video_codec->codec_id) {
+ switch (video_codec_ctx->codec_id) {
case AV_CODEC_ID_H264:
- video_codec->max_b_frames = 0; // At least this GPU doesn't support b-frames
- video_codec->profile = FF_PROFILE_H264_BASELINE | FF_PROFILE_H264_CONSTRAINED;
- av_opt_set(video_codec->priv_data, "preset", "slow", 0);
- av_opt_set(video_codec->priv_data, "tune", "zerolatency", 0);
- av_opt_set(video_codec->priv_data, "vprofile", "baseline", AV_OPT_SEARCH_CHILDREN);
+ video_codec_ctx->max_b_frames = 0; // At least this GPU doesn't support b-frames
+ video_codec_ctx->profile = FF_PROFILE_H264_BASELINE | FF_PROFILE_H264_CONSTRAINED;
+ av_opt_set(video_codec_ctx->priv_data, "preset", "slow", 0);
+ av_opt_set(video_codec_ctx->priv_data, "tune", "zerolatency", 0);
+ av_opt_set(video_codec_ctx->priv_data, "vprofile", "baseline", AV_OPT_SEARCH_CHILDREN);
break;
case AV_CODEC_ID_HEVC:
// tested to work with defaults
@@ -1455,13 +1521,13 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) {
break;
default:
ZmqLogger::Instance()->AppendDebugMethod("No codec-specific options defined for this codec. HW encoding may fail",
- "codec_id", video_codec->codec_id);
+ "codec_id", video_codec_ctx->codec_id);
break;
}
// set hw_frames_ctx for encoder's AVCodecContext
int err;
- if ((err = set_hwframe_ctx(video_codec, hw_device_ctx, info.width, info.height)) < 0) {
+ if ((err = set_hwframe_ctx(video_codec_ctx, hw_device_ctx, info.width, info.height)) < 0) {
ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::open_video (set_hwframe_ctx) ERROR faled to set hwframe context",
"width", info.width, "height", info.height, av_err2str(err), -1);
}
@@ -1469,9 +1535,9 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) {
#endif // HAVE_HW_ACCEL
/* open the codec */
- if (avcodec_open2(video_codec, codec, &opts) < 0)
+ if (avcodec_open2(video_codec_ctx, codec, &opts) < 0)
throw InvalidCodec("Could not open video codec", path);
- AV_COPY_PARAMS_FROM_CONTEXT(st, video_codec);
+ AV_COPY_PARAMS_FROM_CONTEXT(st, video_codec_ctx);
// Free options
av_dict_free(&opts);
@@ -1481,7 +1547,7 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) {
av_dict_set(&st->metadata, iter->first.c_str(), iter->second.c_str(), 0);
}
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::open_video", "video_codec->thread_count", video_codec->thread_count);
+ ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::open_video", "video_codec_ctx->thread_count", video_codec_ctx->thread_count);
}
@@ -1515,29 +1581,27 @@ void FFmpegWriter::write_audio_packets(bool is_final) {
channels_in_frame = frame->GetAudioChannelsCount();
channel_layout_in_frame = frame->ChannelsLayout();
-
// Get audio sample array
float *frame_samples_float = NULL;
// Get samples interleaved together (c1 c2 c1 c2 c1 c2)
frame_samples_float = frame->GetInterleavedAudioSamples(sample_rate_in_frame, NULL, &samples_in_frame);
-
// Calculate total samples
total_frame_samples = samples_in_frame * channels_in_frame;
// Translate audio sample values back to 16 bit integers with saturation
- float valF;
- int16_t conv;
const int16_t max16 = 32767;
const int16_t min16 = -32768;
for (int s = 0; s < total_frame_samples; s++, frame_position++) {
- valF = frame_samples_float[s] * (1 << 15);
- if (valF > max16)
+ float valF = frame_samples_float[s] * (1 << 15);
+ int16_t conv;
+ if (valF > max16) {
conv = max16;
- else if (valF < min16)
+ } else if (valF < min16) {
conv = min16;
- else
+ } else {
conv = int(valF + 32768.5) - 32768; // +0.5 is for rounding
+ }
// Copy into buffer
all_queued_samples[frame_position] = conv;
@@ -1561,7 +1625,7 @@ void FFmpegWriter::write_audio_packets(bool is_final) {
ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_audio_packets", "is_final", is_final, "total_frame_samples", total_frame_samples, "channel_layout_in_frame", channel_layout_in_frame, "channels_in_frame", channels_in_frame, "samples_in_frame", samples_in_frame, "LAYOUT_MONO", LAYOUT_MONO);
// Keep track of the original sample format
- AVSampleFormat output_sample_fmt = audio_codec->sample_fmt;
+ AVSampleFormat output_sample_fmt = audio_codec_ctx->sample_fmt;
AVFrame *audio_frame = NULL;
if (!is_final) {
@@ -1577,7 +1641,7 @@ void FFmpegWriter::write_audio_packets(bool is_final) {
}
// Do not convert audio to planar format (yet). We need to keep everything interleaved at this point.
- switch (audio_codec->sample_fmt) {
+ switch (audio_codec_ctx->sample_fmt) {
case AV_SAMPLE_FMT_FLTP: {
output_sample_fmt = AV_SAMPLE_FMT_FLT;
break;
@@ -1628,20 +1692,25 @@ void FFmpegWriter::write_audio_packets(bool is_final) {
int nb_samples = 0;
// Convert audio samples
- nb_samples = SWR_CONVERT(avr, // audio resample context
- audio_converted->data, // output data pointers
- audio_converted->linesize[0], // output plane size, in bytes. (0 if unknown)
- audio_converted->nb_samples, // maximum number of samples that the output buffer can hold
- audio_frame->data, // input data pointers
- audio_frame->linesize[0], // input plane size, in bytes (0 if unknown)
- audio_frame->nb_samples); // number of input samples to convert
+ nb_samples = SWR_CONVERT(
+ avr, // audio resample context
+ audio_converted->data, // output data pointers
+ audio_converted->linesize[0], // output plane size, in bytes. (0 if unknown)
+ audio_converted->nb_samples, // maximum number of samples that the output buffer can hold
+ audio_frame->data, // input data pointers
+ 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)));
+ sizeof(int16_t) * nb_samples * info.channels
+ * (av_get_bytes_per_sample(output_sample_fmt) /
+ av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) )
+ );
// Copy audio samples over original samples
memcpy(all_resampled_samples, audio_converted->data[0], nb_samples * info.channels * av_get_bytes_per_sample(output_sample_fmt));
@@ -1663,22 +1732,28 @@ void FFmpegWriter::write_audio_packets(bool is_final) {
// Determine how many samples we need
int diff = 0;
- if (remaining_frame_samples >= remaining_packet_samples)
+ if (remaining_frame_samples >= remaining_packet_samples) {
diff = remaining_packet_samples;
- else if (remaining_frame_samples < remaining_packet_samples)
+ } else {
diff = remaining_frame_samples;
+ }
// Copy frame samples into the packet samples array
if (!is_final)
//TODO: Make this more sane
- memcpy(samples + (audio_input_position * (av_get_bytes_per_sample(output_sample_fmt) / av_get_bytes_per_sample(AV_SAMPLE_FMT_S16))),
- all_resampled_samples + samples_position, diff * av_get_bytes_per_sample(output_sample_fmt));
+ memcpy(
+ samples + (audio_input_position
+ * (av_get_bytes_per_sample(output_sample_fmt) /
+ av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) )
+ ),
+ all_resampled_samples + samples_position,
+ diff * av_get_bytes_per_sample(output_sample_fmt)
+ );
// Increment counters
audio_input_position += diff;
samples_position += diff * (av_get_bytes_per_sample(output_sample_fmt) / av_get_bytes_per_sample(AV_SAMPLE_FMT_S16));
remaining_frame_samples -= diff;
- remaining_packet_samples -= diff;
// Do we have enough samples to proceed?
if (audio_input_position < (audio_input_frame_size * info.channels) && !is_final)
@@ -1688,8 +1763,16 @@ void FFmpegWriter::write_audio_packets(bool is_final) {
// Convert to planar (if needed by audio codec)
AVFrame *frame_final = AV_ALLOCATE_FRAME();
AV_RESET_FRAME(frame_final);
- if (av_sample_fmt_is_planar(audio_codec->sample_fmt)) {
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_audio_packets (2nd resampling for Planar formats)", "in_sample_fmt", output_sample_fmt, "out_sample_fmt", audio_codec->sample_fmt, "in_sample_rate", info.sample_rate, "out_sample_rate", info.sample_rate, "in_channels", info.channels, "out_channels", info.channels);
+ if (av_sample_fmt_is_planar(audio_codec_ctx->sample_fmt)) {
+ ZmqLogger::Instance()->AppendDebugMethod(
+ "FFmpegWriter::write_audio_packets (2nd resampling for Planar formats)",
+ "in_sample_fmt", output_sample_fmt,
+ "out_sample_fmt", audio_codec_ctx->sample_fmt,
+ "in_sample_rate", info.sample_rate,
+ "out_sample_rate", info.sample_rate,
+ "in_channels", info.channels,
+ "out_channels", info.channels
+ );
// setup resample context
if (!avr_planar) {
@@ -1697,7 +1780,7 @@ void FFmpegWriter::write_audio_packets(bool is_final) {
av_opt_set_int(avr_planar, "in_channel_layout", info.channel_layout, 0);
av_opt_set_int(avr_planar, "out_channel_layout", info.channel_layout, 0);
av_opt_set_int(avr_planar, "in_sample_fmt", output_sample_fmt, 0);
- av_opt_set_int(avr_planar, "out_sample_fmt", audio_codec->sample_fmt, 0); // planar not allowed here
+ av_opt_set_int(avr_planar, "out_sample_fmt", audio_codec_ctx->sample_fmt, 0); // planar not allowed here
av_opt_set_int(avr_planar, "in_sample_rate", info.sample_rate, 0);
av_opt_set_int(avr_planar, "out_sample_rate", info.sample_rate, 0);
av_opt_set_int(avr_planar, "in_channels", info.channels, 0);
@@ -1712,31 +1795,39 @@ void FFmpegWriter::write_audio_packets(bool is_final) {
// Create a new array
final_samples_planar = (int16_t *) av_malloc(
- sizeof(int16_t) * audio_frame->nb_samples * info.channels * (av_get_bytes_per_sample(output_sample_fmt) / av_get_bytes_per_sample(AV_SAMPLE_FMT_S16)));
+ sizeof(int16_t) * audio_frame->nb_samples * info.channels
+ * (av_get_bytes_per_sample(output_sample_fmt) /
+ av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) )
+ );
// Copy audio into buffer for frame
memcpy(final_samples_planar, samples, audio_frame->nb_samples * info.channels * av_get_bytes_per_sample(output_sample_fmt));
// Fill input frame with sample data
- avcodec_fill_audio_frame(audio_frame, info.channels, output_sample_fmt, (uint8_t *) final_samples_planar,
- audio_encoder_buffer_size, 0);
+ avcodec_fill_audio_frame(audio_frame, info.channels, output_sample_fmt,
+ (uint8_t *) final_samples_planar, audio_encoder_buffer_size, 0);
// Create output frame (and allocate arrays)
frame_final->nb_samples = audio_input_frame_size;
- av_samples_alloc(frame_final->data, frame_final->linesize, info.channels, frame_final->nb_samples, audio_codec->sample_fmt, 0);
+ av_samples_alloc(frame_final->data, frame_final->linesize, info.channels,
+ frame_final->nb_samples, audio_codec_ctx->sample_fmt, 0);
// Convert audio samples
- int nb_samples = SWR_CONVERT(avr_planar, // audio resample context
- frame_final->data, // output data pointers
- frame_final->linesize[0], // output plane size, in bytes. (0 if unknown)
- frame_final->nb_samples, // maximum number of samples that the output buffer can hold
- audio_frame->data, // input data pointers
- audio_frame->linesize[0], // input plane size, in bytes (0 if unknown)
- audio_frame->nb_samples); // number of input samples to convert
+ int nb_samples = SWR_CONVERT(
+ avr_planar, // audio resample context
+ frame_final->data, // output data pointers
+ frame_final->linesize[0], // output plane size, in bytes. (0 if unknown)
+ frame_final->nb_samples, // maximum number of samples that the output buffer can hold
+ audio_frame->data, // input data pointers
+ audio_frame->linesize[0], // input plane size, in bytes (0 if unknown)
+ audio_frame->nb_samples // number of input samples to convert
+ );
// Copy audio samples over original samples
- if (nb_samples > 0)
- memcpy(samples, frame_final->data[0], nb_samples * av_get_bytes_per_sample(audio_codec->sample_fmt) * info.channels);
+ if (nb_samples > 0) {
+ memcpy(samples, frame_final->data[0],
+ nb_samples * av_get_bytes_per_sample(audio_codec_ctx->sample_fmt) * info.channels);
+ }
// deallocate AVFrame
av_freep(&(audio_frame->data[0]));
@@ -1748,17 +1839,22 @@ void FFmpegWriter::write_audio_packets(bool is_final) {
} else {
// Create a new array
final_samples = (int16_t *) av_malloc(
- sizeof(int16_t) * audio_input_position * (av_get_bytes_per_sample(audio_codec->sample_fmt) / av_get_bytes_per_sample(AV_SAMPLE_FMT_S16)));
+ sizeof(int16_t) * audio_input_position
+ * (av_get_bytes_per_sample(audio_codec_ctx->sample_fmt) /
+ av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) )
+ );
// Copy audio into buffer for frame
- memcpy(final_samples, samples, audio_input_position * av_get_bytes_per_sample(audio_codec->sample_fmt));
+ memcpy(final_samples, samples,
+ audio_input_position * av_get_bytes_per_sample(audio_codec_ctx->sample_fmt));
// Init the nb_samples property
frame_final->nb_samples = audio_input_frame_size;
// Fill the final_frame AVFrame with audio (non planar)
- avcodec_fill_audio_frame(frame_final, audio_codec->channels, audio_codec->sample_fmt, (uint8_t *) final_samples,
- audio_encoder_buffer_size, 0);
+ avcodec_fill_audio_frame(frame_final, audio_codec_ctx->channels,
+ audio_codec_ctx->sample_fmt, (uint8_t *) final_samples,
+ audio_encoder_buffer_size, 0);
}
// Increment PTS (in samples)
@@ -1782,18 +1878,18 @@ void FFmpegWriter::write_audio_packets(bool is_final) {
int error_code;
int ret = 0;
int frame_finished = 0;
- error_code = ret = avcodec_send_frame(audio_codec, frame_final);
+ error_code = ret = avcodec_send_frame(audio_codec_ctx, frame_final);
if (ret < 0 && ret != AVERROR(EINVAL) && ret != AVERROR_EOF) {
- avcodec_send_frame(audio_codec, NULL);
+ avcodec_send_frame(audio_codec_ctx, NULL);
}
else {
if (ret >= 0)
pkt.size = 0;
- ret = avcodec_receive_packet(audio_codec, &pkt);
+ ret = avcodec_receive_packet(audio_codec_ctx, &pkt);
if (ret >= 0)
frame_finished = 1;
if(ret == AVERROR(EINVAL) || ret == AVERROR_EOF) {
- avcodec_flush_buffers(audio_codec);
+ avcodec_flush_buffers(audio_codec_ctx);
ret = 0;
}
if (ret >= 0) {
@@ -1807,7 +1903,7 @@ void FFmpegWriter::write_audio_packets(bool is_final) {
got_packet_ptr = ret;
#else
// Encode audio (older versions of FFmpeg)
- int error_code = avcodec_encode_audio2(audio_codec, &pkt, frame_final, &got_packet_ptr);
+ int error_code = avcodec_encode_audio2(audio_codec_ctx, &pkt, frame_final, &got_packet_ptr);
#endif
/* if zero size, it means the image was buffered */
if (error_code == 0 && got_packet_ptr) {
@@ -1818,21 +1914,18 @@ void FFmpegWriter::write_audio_packets(bool is_final) {
// Scale the PTS to the audio stream timebase (which is sometimes different than the codec's timebase)
if (pkt.pts != AV_NOPTS_VALUE)
- pkt.pts = av_rescale_q(pkt.pts, audio_codec->time_base, audio_st->time_base);
+ pkt.pts = av_rescale_q(pkt.pts, audio_codec_ctx->time_base, audio_st->time_base);
if (pkt.dts != AV_NOPTS_VALUE)
- pkt.dts = av_rescale_q(pkt.dts, audio_codec->time_base, audio_st->time_base);
+ pkt.dts = av_rescale_q(pkt.dts, audio_codec_ctx->time_base, audio_st->time_base);
if (pkt.duration > 0)
- pkt.duration = av_rescale_q(pkt.duration, audio_codec->time_base, audio_st->time_base);
+ pkt.duration = av_rescale_q(pkt.duration, audio_codec_ctx->time_base, audio_st->time_base);
// set stream
pkt.stream_index = audio_st->index;
pkt.flags |= AV_PKT_FLAG_KEY;
/* write the compressed frame in the media file */
- int error_code = av_interleaved_write_frame(oc, &pkt);
- if (error_code < 0) {
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_audio_packets ERROR [" + (std::string) av_err2str(error_code) + "]", "error_code", error_code);
- }
+ error_code = av_interleaved_write_frame(oc, &pkt);
}
if (error_code < 0) {
@@ -1933,10 +2026,13 @@ void FFmpegWriter::process_video_packet(std::shared_ptr frame) {
} else
#endif // HAVE_HW_ACCEL
{
- frame_final = allocate_avframe((AVPixelFormat)(video_st->codecpar->format), info.width, info.height, &bytes_final, NULL);
+ 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);
+ AVFrame *frame_final = allocate_avframe(video_codec_ctx->pix_fmt, info.width, info.height, &bytes_final, NULL);
#endif // IS_FFMPEG_3_2
// Fill with data
@@ -1945,7 +2041,7 @@ void FFmpegWriter::process_video_packet(std::shared_ptr frame) {
// Resize & convert pixel format
sws_scale(scaler, frame_source->data, frame_source->linesize, 0,
- source_image_height, frame_final->data, frame_final->linesize);
+ source_image_height, frame_final->data, frame_final->linesize);
// Add resized AVFrame to av_frames map
#pragma omp critical (av_frames_section)
@@ -1961,11 +2057,18 @@ void FFmpegWriter::process_video_packet(std::shared_ptr frame) {
// write video frame
bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *frame_final) {
#if (LIBAVFORMAT_VERSION_MAJOR >= 58)
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_video_packet", "frame->number", frame->number, "oc->oformat->flags", oc->oformat->flags);
+ // FFmpeg 4.0+
+ ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_video_packet",
+ "frame->number", frame->number, "oc->oformat->flags", oc->oformat->flags);
+
+ if (AV_GET_CODEC_TYPE(video_st) == AVMEDIA_TYPE_VIDEO && AV_FIND_DECODER_CODEC_ID(video_st) == AV_CODEC_ID_RAWVIDEO) {
#else
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_video_packet", "frame->number", frame->number, "oc->oformat->flags & AVFMT_RAWPICTURE", oc->oformat->flags & AVFMT_RAWPICTURE);
+ ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_video_packet",
+ "frame->number", frame->number,
+ "oc->oformat->flags & AVFMT_RAWPICTURE", oc->oformat->flags & AVFMT_RAWPICTURE);
if (oc->oformat->flags & AVFMT_RAWPICTURE) {
+#endif
// Raw video case.
AVPacket pkt;
av_init_packet(&pkt);
@@ -1976,7 +2079,7 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra
pkt.size = sizeof(AVPicture);
// Increment PTS (in frames and scaled to the codec's timebase)
- write_video_count += av_rescale_q(1, (AVRational) {info.fps.den, info.fps.num}, video_codec->time_base);
+ write_video_count += av_rescale_q(1, av_make_q(info.fps.den, info.fps.num), video_codec_ctx->time_base);
pkt.pts = write_video_count;
/* write the compressed frame in the media file */
@@ -1990,7 +2093,6 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra
AV_FREE_PACKET(&pkt);
} else
-#endif
{
AVPacket pkt;
@@ -1999,28 +2101,25 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra
pkt.size = 0;
pkt.pts = pkt.dts = AV_NOPTS_VALUE;
- // Pointer for video buffer (if using old FFmpeg version)
- uint8_t *video_outbuf = NULL;
-
// Increment PTS (in frames and scaled to the codec's timebase)
- write_video_count += av_rescale_q(1, (AVRational) {info.fps.den, info.fps.num}, video_codec->time_base);
+ write_video_count += av_rescale_q(1, av_make_q(info.fps.den, info.fps.num), video_codec_ctx->time_base);
// Assign the initial AVFrame PTS from the frame counter
frame_final->pts = write_video_count;
#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");
+ std::clog << "Error code: av_hwframe_alloc\n";
}
- if (av_hwframe_get_buffer(video_codec->hw_frames_ctx, hw_frame, 0) < 0) {
- fprintf(stderr, "Error code: av_hwframe_get_buffer\n");
+ if (av_hwframe_get_buffer(video_codec_ctx->hw_frames_ctx, hw_frame, 0) < 0) {
+ std::clog << "Error code: av_hwframe_get_buffer\n";
}
if (!hw_frame->hw_frames_ctx) {
- fprintf(stderr, "Error hw_frames_ctx.\n");
+ std::clog << "Error hw_frames_ctx.\n";
}
hw_frame->format = AV_PIX_FMT_NV12;
if ( av_hwframe_transfer_data(hw_frame, frame_final, 0) < 0) {
- fprintf(stderr, "Error while transferring frame data to surface.\n");
+ std::clog << "Error while transferring frame data to surface.\n";
}
av_frame_copy_props(hw_frame, frame_final);
}
@@ -2034,29 +2133,29 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra
#if HAVE_HW_ACCEL
if (hw_en_on && hw_en_supported) {
- ret = avcodec_send_frame(video_codec, hw_frame); //hw_frame!!!
+ ret = avcodec_send_frame(video_codec_ctx, hw_frame); //hw_frame!!!
} else
#endif // HAVE_HW_ACCEL
{
- ret = avcodec_send_frame(video_codec, frame_final);
+ ret = avcodec_send_frame(video_codec_ctx, frame_final);
}
error_code = ret;
if (ret < 0 ) {
ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_video_packet (Frame not sent)");
if (ret == AVERROR(EAGAIN) ) {
- std::cerr << "Frame EAGAIN" << "\n";
+ std::clog << "Frame EAGAIN\n";
}
if (ret == AVERROR_EOF ) {
- std::cerr << "Frame AVERROR_EOF" << "\n";
+ std::clog << "Frame AVERROR_EOF\n";
}
- avcodec_send_frame(video_codec, NULL);
+ avcodec_send_frame(video_codec_ctx, NULL);
}
else {
while (ret >= 0) {
- ret = avcodec_receive_packet(video_codec, &pkt);
+ ret = avcodec_receive_packet(video_codec_ctx, &pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
- avcodec_flush_buffers(video_codec);
+ avcodec_flush_buffers(video_codec_ctx);
got_packet_ptr = 0;
break;
}
@@ -2067,34 +2166,14 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra
}
}
#else
-#if LIBAVFORMAT_VERSION_MAJOR >= 54
// Write video packet (older than FFmpeg 3.2)
- error_code = avcodec_encode_video2(video_codec, &pkt, frame_final, &got_packet_ptr);
+ error_code = avcodec_encode_video2(video_codec_ctx, &pkt, frame_final, &got_packet_ptr);
if (error_code != 0) {
- std::cerr << "Frame AVERROR_EOF" << "\n";
+ ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_video_packet ERROR [" + (std::string) av_err2str(error_code) + "]", "error_code", error_code);
}
if (got_packet_ptr == 0) {
- std::cerr << "Frame gotpacket error" << "\n";
+ ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_video_packet (Frame gotpacket error)");
}
-#else
- // Write video packet (even older versions of FFmpeg)
- int video_outbuf_size = 200000;
- video_outbuf = (uint8_t*) av_malloc(200000);
-
- /* encode the image */
- int out_size = avcodec_encode_video(video_codec, video_outbuf, video_outbuf_size, frame_final);
-
- /* if zero size, it means the image was buffered */
- if (out_size > 0) {
- if(video_codec->coded_frame->key_frame)
- pkt.flags |= AV_PKT_FLAG_KEY;
- pkt.data= video_outbuf;
- pkt.size= out_size;
-
- // got data back (so encode this frame)
- got_packet_ptr = 1;
- }
-#endif // LIBAVFORMAT_VERSION_MAJOR >= 54
#endif // IS_FFMPEG_3_2
/* if zero size, it means the image was buffered */
@@ -2106,25 +2185,21 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra
// set the timestamp
if (pkt.pts != AV_NOPTS_VALUE)
- pkt.pts = av_rescale_q(pkt.pts, video_codec->time_base, video_st->time_base);
+ pkt.pts = av_rescale_q(pkt.pts, video_codec_ctx->time_base, video_st->time_base);
if (pkt.dts != AV_NOPTS_VALUE)
- pkt.dts = av_rescale_q(pkt.dts, video_codec->time_base, video_st->time_base);
+ pkt.dts = av_rescale_q(pkt.dts, video_codec_ctx->time_base, video_st->time_base);
if (pkt.duration > 0)
- pkt.duration = av_rescale_q(pkt.duration, video_codec->time_base, video_st->time_base);
+ pkt.duration = av_rescale_q(pkt.duration, video_codec_ctx->time_base, video_st->time_base);
pkt.stream_index = video_st->index;
/* write the compressed frame in the media file */
- int error_code = av_interleaved_write_frame(oc, &pkt);
- if (error_code < 0) {
- ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_video_packet ERROR [" + (std::string) av_err2str(error_code) + "]", "error_code", error_code);
+ int result = av_interleaved_write_frame(oc, &pkt);
+ if (result < 0) {
+ ZmqLogger::Instance()->AppendDebugMethod("FFmpegWriter::write_video_packet ERROR [" + (std::string) av_err2str(result) + "]", "result", result);
return false;
}
}
- // Deallocate memory (if needed)
- if (video_outbuf)
- delete[] video_outbuf;
-
// Deallocate packet
AV_FREE_PACKET(&pkt);
#if HAVE_HW_ACCEL
@@ -2159,12 +2234,14 @@ void FFmpegWriter::InitScalers(int source_width, int source_height) {
// Init the software scaler from FFMpeg
#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);
+ 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 // 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);
+ 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);
}
// Add rescaler to vector
diff --git a/include/FFmpegWriter.h b/src/FFmpegWriter.h
similarity index 98%
rename from include/FFmpegWriter.h
rename to src/FFmpegWriter.h
index 37fa22dc..6d9da6e5 100644
--- a/include/FFmpegWriter.h
+++ b/src/FFmpegWriter.h
@@ -47,8 +47,6 @@
#include
#include
-#include
-#include
#include
#include "CacheMemory.h"
#include "Exceptions.h"
@@ -164,8 +162,8 @@ namespace openshot {
AVOutputFormat *fmt;
AVFormatContext *oc;
AVStream *audio_st, *video_st;
- AVCodecContext *video_codec;
- AVCodecContext *audio_codec;
+ AVCodecContext *video_codec_ctx;
+ AVCodecContext *audio_codec_ctx;
SwsContext *img_convert_ctx;
int16_t *samples;
uint8_t *audio_outbuf;
@@ -251,9 +249,11 @@ namespace openshot {
public:
- /// @brief Constructor for FFmpegWriter. Throws one of the following exceptions.
+ /// @brief Constructor for FFmpegWriter.
+ /// Throws an exception on failure to open path.
+ ///
/// @param path The file path of the video file you want to open and read
- FFmpegWriter(std::string path);
+ FFmpegWriter(const std::string& path);
/// Close the writer
void Close();
diff --git a/src/Fraction.cpp b/src/Fraction.cpp
index c9cdad55..869ef35f 100644
--- a/src/Fraction.cpp
+++ b/src/Fraction.cpp
@@ -28,7 +28,7 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/Fraction.h"
+#include "Fraction.h"
using namespace openshot;
diff --git a/include/Fraction.h b/src/Fraction.h
similarity index 99%
rename from include/Fraction.h
rename to src/Fraction.h
index 9ffcda1f..88c16fbc 100644
--- a/include/Fraction.h
+++ b/src/Fraction.h
@@ -31,7 +31,7 @@
#ifndef OPENSHOT_FRACTION_H
#define OPENSHOT_FRACTION_H
-#include
+#include
namespace openshot {
diff --git a/src/Frame.cpp b/src/Frame.cpp
index ae9f1a4b..3bae8854 100644
--- a/src/Frame.cpp
+++ b/src/Frame.cpp
@@ -28,62 +28,54 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/Frame.h"
+#include "Frame.h"
+#include "JuceHeader.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include // for std::this_thread::sleep_for
+#include // for std::chrono::milliseconds
using namespace std;
using namespace openshot;
-// Constructor - blank frame (300x200 blank image, 48kHz audio silence)
-Frame::Frame() : number(1), pixel_ratio(1,1), channels(2), width(1), height(1), color("#000000"),
- channel_layout(LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false),
- max_audio_sample(0)
-{
- // Init the image magic and audio buffer
- audio = std::shared_ptr(new juce::AudioSampleBuffer(channels, 0));
-
- // 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)
- : number(number), pixel_ratio(1,1), channels(2), width(width), height(height), color(color),
- channel_layout(LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false),
- max_audio_sample(0)
-{
- // Init the image magic and audio buffer
- audio = std::shared_ptr(new juce::AudioSampleBuffer(channels, 0));
-
- // initialize the audio samples to zero (silence)
- audio->clear();
-}
-
-// Constructor - audio only (300x200 blank image)
-Frame::Frame(int64_t number, int samples, int channels) :
- number(number), pixel_ratio(1,1), channels(channels), width(1), height(1), color("#000000"),
- channel_layout(LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false),
- max_audio_sample(0)
-{
- // Init the image magic and audio buffer
- audio = std::shared_ptr(new juce::AudioSampleBuffer(channels, samples));
-
- // 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)
- : number(number), pixel_ratio(1,1), channels(channels), width(width), height(height), color(color),
- channel_layout(LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false),
+ : audio(std::make_shared(channels, samples)),
+ number(number), width(width), height(height),
+ pixel_ratio(1,1), color(color), qbuffer(NULL),
+ channels(channels), channel_layout(LAYOUT_STEREO),
+ sample_rate(44100),
+ has_audio_data(false), has_image_data(false),
max_audio_sample(0)
{
- // Init the image magic and audio buffer
- audio = std::shared_ptr(new juce::AudioSampleBuffer(channels, samples));
-
- // initialize the audio samples to zero (silence)
+ // zero (fill with silence) the audio buffer
audio->clear();
}
+// Delegating Constructor - blank frame
+Frame::Frame() : Frame::Frame(1, 1, 1, "#000000", 0, 2) {};
+
+// Delegating Constructor - image only
+Frame::Frame(int64_t number, int width, int height, std::string color)
+ : Frame::Frame(number, width, height, color, 0, 2) {};
+
+// Delegating Constructor - audio only
+Frame::Frame(int64_t number, int samples, int channels)
+ : Frame::Frame(number, 1, 1, "#000000", samples, channels) {};
+
// Copy constructor
Frame::Frame ( const Frame &other )
@@ -117,11 +109,11 @@ void Frame::DeepCopy(const Frame& other)
max_audio_sample = other.max_audio_sample;
if (other.image)
- image = std::shared_ptr(new QImage(*(other.image)));
+ image = std::make_shared(*(other.image));
if (other.audio)
- audio = std::shared_ptr(new juce::AudioSampleBuffer(*(other.audio)));
+ audio = std::make_shared(*(other.audio));
if (other.wave_image)
- wave_image = std::shared_ptr(new QImage(*(other.wave_image)));
+ wave_image = std::make_shared(*(other.wave_image));
}
// Destructor
@@ -138,7 +130,7 @@ void Frame::Display()
// Only create the QApplication once
static int argc = 1;
static char* argv[1] = {NULL};
- previewApp = std::shared_ptr(new QApplication(argc, argv));
+ previewApp = std::make_shared(argc, argv);
}
// Get preview image
@@ -152,7 +144,8 @@ void Frame::Display()
int new_height = previewImage->size().height() * pixel_ratio.Reciprocal().ToDouble();
// Resize to fix DAR
- previewImage = std::shared_ptr(new QImage(previewImage->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
+ previewImage = std::make_shared(previewImage->scaled(
+ new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
}
// Create window
@@ -228,7 +221,8 @@ std::shared_ptr Frame::GetWaveform(int width, int height, int Red, int G
}
// Create blank image
- wave_image = std::shared_ptr(new QImage(total_width, total_height, QImage::Format_RGBA8888));
+ wave_image = std::make_shared(
+ total_width, total_height, QImage::Format_RGBA8888);
wave_image->fill(QColor(0,0,0,0));
// Load QPainter with wave_image device
@@ -253,13 +247,13 @@ std::shared_ptr Frame::GetWaveform(int width, int height, int Red, int G
// Resize Image (if requested)
if (width != total_width || height != total_height) {
QImage scaled_wave_image = wave_image->scaled(width, height, Qt::IgnoreAspectRatio, Qt::FastTransformation);
- wave_image = std::shared_ptr(new QImage(scaled_wave_image));
+ wave_image = std::make_shared(scaled_wave_image);
}
}
else
{
// No audio samples present
- wave_image = std::shared_ptr(new QImage(width, height, QImage::Format_RGBA8888));
+ wave_image = std::make_shared(width, height, QImage::Format_RGBA8888);
wave_image->fill(QColor(QString::fromStdString("#000000")));
}
@@ -294,7 +288,7 @@ void Frame::DisplayWaveform()
// Only create the QApplication once
static int argc = 1;
static char* argv[1] = {NULL};
- previewApp = std::shared_ptr(new QApplication(argc, argv));
+ previewApp = std::make_shared(argc, argv);
}
// Create window
@@ -480,6 +474,11 @@ const unsigned char* Frame::GetPixels()
// Get pixel data (for only a single scan-line)
const unsigned char* Frame::GetPixels(int row)
{
+ // Check for blank image
+ if (!image)
+ // Fill with black
+ AddColor(width, height, color);
+
// Return array of pixel packets
return image->constScanLine(row);
}
@@ -594,11 +593,15 @@ void Frame::Save(std::string path, float scale, std::string format, int quality)
int new_height = previewImage->size().height() * pixel_ratio.Reciprocal().ToDouble();
// Resize to fix DAR
- previewImage = std::shared_ptr(new QImage(previewImage->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
+ previewImage = std::make_shared(previewImage->scaled(
+ new_width, new_height,
+ Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
}
// Resize image
- previewImage = std::shared_ptr(new QImage(previewImage->scaled(new_width * scale, new_height * scale, Qt::KeepAspectRatio, Qt::SmoothTransformation)));
+ previewImage = std::make_shared(previewImage->scaled(
+ new_width * scale, new_height * scale,
+ Qt::KeepAspectRatio, Qt::SmoothTransformation));
}
// Save image
@@ -610,7 +613,8 @@ void Frame::Thumbnail(std::string path, int new_width, int new_height, std::stri
std::string background_color, bool ignore_aspect, std::string format, int quality, float rotate) {
// Create blank thumbnail image & fill background color
- std::shared_ptr thumbnail = std::shared_ptr(new QImage(new_width, new_height, QImage::Format_RGBA8888));
+ auto thumbnail = std::make_shared(
+ new_width, new_height, QImage::Format_RGBA8888);
thumbnail->fill(QColor(QString::fromStdString(background_color)));
// Create painter
@@ -628,16 +632,22 @@ void Frame::Thumbnail(std::string path, int new_width, int new_height, std::stri
int aspect_height = previewImage->size().height() * pixel_ratio.Reciprocal().ToDouble();
// Resize to fix DAR
- previewImage = std::shared_ptr(new QImage(previewImage->scaled(aspect_width, aspect_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
+ previewImage = std::make_shared(previewImage->scaled(
+ aspect_width, aspect_height,
+ Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
}
// Resize frame image
if (ignore_aspect)
// Ignore aspect ratio
- previewImage = std::shared_ptr(new QImage(previewImage->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
+ previewImage = std::make_shared(previewImage->scaled(
+ new_width, new_height,
+ Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
else
// Maintain aspect ratio
- previewImage = std::shared_ptr(new QImage(previewImage->scaled(new_width, new_height, Qt::KeepAspectRatio, Qt::SmoothTransformation)));
+ previewImage = std::make_shared(previewImage->scaled(
+ new_width, new_height,
+ Qt::KeepAspectRatio, Qt::SmoothTransformation));
// Composite frame image onto background (centered)
int x = (new_width - previewImage->size().width()) / 2.0; // center
@@ -661,14 +671,16 @@ void Frame::Thumbnail(std::string path, int new_width, int new_height, std::stri
// Overlay Image (if any)
if (overlay_path != "") {
// Open overlay
- std::shared_ptr overlay = std::shared_ptr(new QImage());
+ auto overlay = std::make_shared();
overlay->load(QString::fromStdString(overlay_path));
// Set pixel format
- overlay = std::shared_ptr(new QImage(overlay->convertToFormat(QImage::Format_RGBA8888)));
+ overlay = std::make_shared(
+ overlay->convertToFormat(QImage::Format_RGBA8888));
// Resize to fit
- overlay = std::shared_ptr(new QImage(overlay->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
+ overlay = std::make_shared(overlay->scaled(
+ new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
// Composite onto thumbnail
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
@@ -679,14 +691,16 @@ void Frame::Thumbnail(std::string path, int new_width, int new_height, std::stri
// Mask Image (if any)
if (mask_path != "") {
// Open mask
- std::shared_ptr mask = std::shared_ptr(new QImage());
+ auto mask = std::make_shared();
mask->load(QString::fromStdString(mask_path));
// Set pixel format
- mask = std::shared_ptr(new QImage(mask->convertToFormat(QImage::Format_RGBA8888)));
+ mask = std::make_shared(
+ mask->convertToFormat(QImage::Format_RGBA8888));
// Resize to fit
- mask = std::shared_ptr(new QImage(mask->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
+ mask = std::make_shared(mask->scaled(
+ new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
// Negate mask
mask->invertPixels();
@@ -739,7 +753,7 @@ void Frame::AddColor(int new_width, int new_height, std::string new_color)
const GenericScopedLock lock(addingImageSection);
#pragma omp critical (AddImage)
{
- image = std::shared_ptr(new QImage(new_width, new_height, QImage::Format_RGBA8888));
+ image = std::make_shared(new_width, new_height, QImage::Format_RGBA8888);
// Fill with solid color
image->fill(QColor(QString::fromStdString(color)));
@@ -751,30 +765,31 @@ void Frame::AddColor(int new_width, int new_height, std::string new_color)
}
// Add (or replace) pixel data to the frame
-void Frame::AddImage(int new_width, int new_height, int bytes_per_pixel, QImage::Format type, const unsigned char *pixels_)
+void Frame::AddImage(
+ int new_width, int new_height, int bytes_per_pixel,
+ QImage::Format type, const unsigned char *pixels_)
{
// Create new buffer
- const GenericScopedLock lock(addingImageSection);
- int buffer_size = new_width * new_height * bytes_per_pixel;
- qbuffer = new unsigned char[buffer_size]();
-
- // Copy buffer data
- memcpy((unsigned char*)qbuffer, pixels_, buffer_size);
-
- // Create new image object, and fill with pixel data
- #pragma omp critical (AddImage)
{
- image = std::shared_ptr(new QImage(qbuffer, new_width, new_height, new_width * bytes_per_pixel, type, (QImageCleanupFunction) &openshot::Frame::cleanUpBuffer, (void*) qbuffer));
+ const GenericScopedLock lock(addingImageSection);
+ int buffer_size = new_width * new_height * bytes_per_pixel;
+ qbuffer = new unsigned char[buffer_size]();
- // Always convert to RGBA8888 (if different)
- if (image->format() != QImage::Format_RGBA8888)
- *image = image->convertToFormat(QImage::Format_RGBA8888);
+ // Copy buffer data
+ memcpy((unsigned char*)qbuffer, pixels_, buffer_size);
- // Update height and width
- width = image->width();
- height = image->height();
- has_image_data = true;
- }
+ } // Release addingImageSection lock
+
+ // Create new image object from pixel data
+ auto new_image = std::make_shared(
+ qbuffer,
+ new_width, new_height,
+ new_width * bytes_per_pixel,
+ type,
+ (QImageCleanupFunction) &openshot::Frame::cleanUpBuffer,
+ (void*) qbuffer
+ );
+ AddImage(new_image);
}
// Add (or replace) pixel data to the frame
@@ -814,7 +829,6 @@ void Frame::AddImage(std::shared_ptr new_image, bool only_odd_lines)
AddImage(new_image);
} else {
-
// Ignore image of different sizes or formats
bool ret=false;
#pragma omp critical (AddImage)
@@ -823,7 +837,8 @@ void Frame::AddImage(std::shared_ptr new_image, bool only_odd_lines)
ret = true;
}
else if (new_image->format() != image->format()) {
- new_image = std::shared_ptr(new QImage(new_image->convertToFormat(image->format())));
+ new_image = std::make_shared(
+ new_image->convertToFormat(image->format()));
}
}
if (ret) {
@@ -933,7 +948,8 @@ std::shared_ptr Frame::GetMagickImage()
const QRgb *tmpBits = (const QRgb*)image->constBits();
// Create new image object, and fill with pixel data
- std::shared_ptr magick_image = std::shared_ptr(new Magick::Image(image->width(), image->height(),"RGBA", Magick::CharPixel, tmpBits));
+ auto magick_image = std::make_shared(
+ image->width(), image->height(),"RGBA", Magick::CharPixel, tmpBits);
// Give image a transparent background color
magick_image->backgroundColor(Magick::Color("none"));
@@ -962,7 +978,9 @@ void Frame::AddMagickImage(std::shared_ptr new_image)
MagickCore::ExportImagePixels(new_image->constImage(), 0, 0, new_image->columns(), new_image->rows(), "RGBA", Magick::CharPixel, buffer, &exception);
// Create QImage of frame data
- image = std::shared_ptr(new QImage(qbuffer, width, height, width * BPP, QImage::Format_RGBA8888, (QImageCleanupFunction) &cleanUpBuffer, (void*) qbuffer));
+ image = std::make_shared(
+ qbuffer, width, height, width * BPP, QImage::Format_RGBA8888,
+ (QImageCleanupFunction) &cleanUpBuffer, (void*) qbuffer);
// Update height and width
width = image->width();
@@ -987,7 +1005,7 @@ void Frame::Play()
// Output error (if any)
if (error.isNotEmpty()) {
- cout << "Error on initialise(): " << error.toStdString() << endl;
+ cout << "Error on initialise(): " << error << endl;
}
juce::AudioSourcePlayer audioSourcePlayer;
@@ -1023,7 +1041,7 @@ void Frame::Play()
while (transport1.isPlaying())
{
cout << "playing" << endl;
- usleep(1000000);
+ std::this_thread::sleep_for(std::chrono::seconds(1));
}
cout << "DONE!!!" << endl;
diff --git a/include/Frame.h b/src/Frame.h
similarity index 93%
rename from include/Frame.h
rename to src/Frame.h
index f4ff54d4..b3416d38 100644
--- a/include/Frame.h
+++ b/src/Frame.h
@@ -34,16 +34,8 @@
#include
#include
#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
+#include
+#include
#include
#include
#include "ZmqLogger.h"
@@ -73,17 +65,17 @@ namespace openshot
* There are many ways to create an instance of an openshot::Frame:
* @code
*
- * // Most basic: a blank frame (300x200 blank image, 48kHz audio silence)
+ * // Most basic: a blank frame (all default values)
* Frame();
*
- * // Image only settings (48kHz audio silence)
+ * // Image only settings
* Frame(1, // Frame number
* 720, // Width of image
* 480, // Height of image
* "#000000" // HTML color code of background color
* );
*
- * // Audio only (300x200 blank image)
+ * // Audio only
* Frame(number, // Frame number
* 44100, // Sample rate of audio stream
* 2 // Number of audio channels
@@ -99,7 +91,7 @@ namespace openshot
* );
*
* // Some methods require a shared pointer to an openshot::Frame object.
- * std::shared_ptr f(new Frame(1, 720, 480, "#000000", 44100, 2));
+ * auto f = std::make_shared(1, 720, 480, "#000000", 44100, 2);
*
* @endcode
*/
@@ -131,13 +123,13 @@ namespace openshot
bool has_image_data; ///< This frame has been loaded with pixel data
- /// Constructor - blank frame (300x200 blank image, 48kHz audio silence)
+ /// Constructor - blank frame
Frame();
- /// Constructor - image only (48kHz audio silence)
+ /// Constructor - image only
Frame(int64_t number, int width, int height, std::string color);
- /// Constructor - audio only (300x200 blank image)
+ /// Constructor - audio only
Frame(int64_t number, int samples, int channels);
/// Constructor - image & audio
diff --git a/src/FrameMapper.cpp b/src/FrameMapper.cpp
index 4b213f81..85c6bfdf 100644
--- a/src/FrameMapper.cpp
+++ b/src/FrameMapper.cpp
@@ -28,7 +28,7 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/FrameMapper.h"
+#include "FrameMapper.h"
using namespace std;
using namespace openshot;
@@ -61,9 +61,9 @@ FrameMapper::FrameMapper(ReaderBase *reader, Fraction target, PulldownType targe
// Destructor
FrameMapper::~FrameMapper() {
- if (is_open)
- // Auto Close if not already
- Close();
+
+ // Auto Close if not already
+ Close();
reader = NULL;
}
@@ -450,7 +450,8 @@ std::shared_ptr FrameMapper::GetFrame(int64_t requested_frame)
}
// Create a new frame
- std::shared_ptr frame = std::make_shared(frame_number, 1, 1, "#000000", samples_in_frame, channels_in_frame);
+ auto frame = std::make_shared(
+ frame_number, 1, 1, "#000000", samples_in_frame, channels_in_frame);
frame->SampleRate(mapped_frame->SampleRate());
frame->ChannelsLayout(mapped_frame->ChannelsLayout());
@@ -460,13 +461,14 @@ std::shared_ptr FrameMapper::GetFrame(int64_t requested_frame)
odd_frame = GetOrCreateFrame(mapped.Odd.Frame);
if (odd_frame)
- frame->AddImage(std::shared_ptr(new QImage(*odd_frame->GetImage())), true);
+ frame->AddImage(std::make_shared(*odd_frame->GetImage()), true);
if (mapped.Odd.Frame != mapped.Even.Frame) {
// Add even lines (if different than the previous image)
std::shared_ptr even_frame;
even_frame = GetOrCreateFrame(mapped.Even.Frame);
if (even_frame)
- frame->AddImage(std::shared_ptr(new QImage(*even_frame->GetImage())), false);
+ frame->AddImage(
+ std::make_shared(*even_frame->GetImage()), false);
}
// Resample audio on frame (if needed)
@@ -487,7 +489,7 @@ std::shared_ptr FrameMapper::GetFrame(int64_t requested_frame)
// includes some additional input samples on first iteration,
// and continues the offset to ensure that the sample rate
// converter isn't input limited.
- const int EXTRA_INPUT_SAMPLES = 20;
+ const int EXTRA_INPUT_SAMPLES = 100;
// Extend end sample count by an additional EXTRA_INPUT_SAMPLES samples
copy_samples.sample_end += EXTRA_INPUT_SAMPLES;
diff --git a/include/FrameMapper.h b/src/FrameMapper.h
similarity index 99%
rename from include/FrameMapper.h
rename to src/FrameMapper.h
index c98d7b71..e78401a9 100644
--- a/include/FrameMapper.h
+++ b/src/FrameMapper.h
@@ -33,7 +33,7 @@
#include
#include
-#include
+#include
#include
#include
#include "CacheMemory.h"
@@ -138,7 +138,6 @@ namespace openshot
*/
class FrameMapper : public ReaderBase {
private:
- bool is_open;
bool field_toggle; // Internal odd / even toggle (used when building the mapping)
Fraction original; // The original frame rate
Fraction target; // The target frame rate
diff --git a/src/ImageReader.cpp b/src/ImageReader.cpp
index 9ce3a70f..ad21be15 100644
--- a/src/ImageReader.cpp
+++ b/src/ImageReader.cpp
@@ -31,18 +31,11 @@
// Require ImageMagick support
#ifdef USE_IMAGEMAGICK
-#include "../include/ImageReader.h"
+#include "ImageReader.h"
using namespace openshot;
-ImageReader::ImageReader(std::string path) : path(path), is_open(false)
-{
- // Open and Close the reader, to populate its attributes (such as height, width, etc...)
- Open();
- Close();
-}
-
-ImageReader::ImageReader(std::string path, bool inspect_reader) : path(path), is_open(false)
+ImageReader::ImageReader(const std::string& path, bool inspect_reader) : path(path), is_open(false)
{
// Open and Close the reader, to populate its attributes (such as height, width, etc...)
if (inspect_reader) {
@@ -61,7 +54,7 @@ void ImageReader::Open()
try
{
// load image
- image = std::shared_ptr(new Magick::Image(path));
+ image = std::make_shared(path);
// Give image a transparent background color
image->backgroundColor(Magick::Color("none"));
@@ -126,7 +119,9 @@ std::shared_ptr ImageReader::GetFrame(int64_t requested_frame)
throw ReaderClosed("The FFmpegReader is closed. Call Open() before calling this method.", path);
// Create or get frame object
- std::shared_ptr image_frame(new Frame(requested_frame, image->size().width(), image->size().height(), "#000000", 0, 2));
+ auto image_frame = std::make_shared(
+ requested_frame, image->size().width(), image->size().height(),
+ "#000000", 0, 2);
// Add Image data to frame
image_frame->AddMagickImage(image);
diff --git a/include/ImageReader.h b/src/ImageReader.h
similarity index 89%
rename from include/ImageReader.h
rename to src/ImageReader.h
index 5aafcc8f..aa96272f 100644
--- a/include/ImageReader.h
+++ b/src/ImageReader.h
@@ -76,15 +76,15 @@ namespace openshot
bool is_open;
public:
-
- /// Constructor for ImageReader. This automatically opens the media file and loads
- /// frame 1, or it throws one of the following exceptions.
- ImageReader(std::string path);
-
- /// Constructor for ImageReader. This only opens the media file to inspect its properties
- /// if inspect_reader=true. When not inspecting the media file, it's much faster, and useful
- /// when you are inflating the object using JSON after instantiating it.
- ImageReader(std::string path, bool inspect_reader);
+ /// @brief Constructor for ImageReader.
+ ///
+ /// Opens the media file to inspect its properties and loads frame 1,
+ /// iff inspect_reader == true (the default). Pass a false value in
+ /// the optional parameter to defer this initial Open()/Close() cycle.
+ ///
+ /// When not inspecting the media file, it's much faster, and useful
+ /// when you are inflating the object using JSON after instantiation.
+ ImageReader(const std::string& path, bool inspect_reader=true);
/// Close File
void Close() override;
diff --git a/src/ImageWriter.cpp b/src/ImageWriter.cpp
index 1b7a01e9..275ea270 100644
--- a/src/ImageWriter.cpp
+++ b/src/ImageWriter.cpp
@@ -34,7 +34,7 @@
//Require ImageMagick support
#ifdef USE_IMAGEMAGICK
-#include "../include/ImageWriter.h"
+#include "ImageWriter.h"
using namespace openshot;
diff --git a/include/ImageWriter.h b/src/ImageWriter.h
similarity index 100%
rename from include/ImageWriter.h
rename to src/ImageWriter.h
diff --git a/src/Json.cpp b/src/Json.cpp
index 0c83d9d6..9d6d9ba4 100644
--- a/src/Json.cpp
+++ b/src/Json.cpp
@@ -28,7 +28,7 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/Json.h"
+#include "Json.h"
const Json::Value openshot::stringToJson(const std::string value) {
diff --git a/include/Json.h b/src/Json.h
similarity index 100%
rename from include/Json.h
rename to src/Json.h
diff --git a/src/KeyFrame.cpp b/src/KeyFrame.cpp
index 57e424cf..b113e632 100644
--- a/src/KeyFrame.cpp
+++ b/src/KeyFrame.cpp
@@ -28,7 +28,7 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/KeyFrame.h"
+#include "KeyFrame.h"
#include
#include
#include
diff --git a/include/KeyFrame.h b/src/KeyFrame.h
similarity index 99%
rename from include/KeyFrame.h
rename to src/KeyFrame.h
index ee58da8f..6424473a 100644
--- a/include/KeyFrame.h
+++ b/src/KeyFrame.h
@@ -33,7 +33,7 @@
#include
#include
-#include
+#include
#include
#include
#include "Exceptions.h"
diff --git a/include/MagickUtilities.h b/src/MagickUtilities.h
similarity index 93%
rename from include/MagickUtilities.h
rename to src/MagickUtilities.h
index 74365904..953b85ee 100644
--- a/include/MagickUtilities.h
+++ b/src/MagickUtilities.h
@@ -32,7 +32,11 @@
#ifdef USE_IMAGEMAGICK
+// Exclude a warning message with IM6 headers
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wignored-qualifiers"
#include "Magick++.h"
+#pragma GCC diagnostic pop
// Determine ImageMagick version, as IM7 isn't fully
// backwards compatible
diff --git a/include/OpenMPUtilities.h b/src/OpenMPUtilities.h
similarity index 83%
rename from include/OpenMPUtilities.h
rename to src/OpenMPUtilities.h
index 9810d636..b523bbd1 100644
--- a/include/OpenMPUtilities.h
+++ b/src/OpenMPUtilities.h
@@ -32,8 +32,8 @@
#define OPENSHOT_OPENMP_UTILITIES_H
#include
-#include
-#include
+#include
+#include
#include "Settings.h"
@@ -41,5 +41,12 @@
#define OPEN_MP_NUM_PROCESSORS (std::min(omp_get_num_procs(), std::max(2, openshot::Settings::Instance()->OMP_THREADS) ))
#define FF_NUM_PROCESSORS (std::min(omp_get_num_procs(), std::max(2, openshot::Settings::Instance()->FF_THREADS) ))
+// Set max-active-levels to the max supported, if possible
+// (supported_active_levels is OpenMP 5.0 (November 2018) or later, only.)
+#if (_OPENMP >= 201811)
+ #define OPEN_MP_MAX_ACTIVE openmp_get_supported_active_levels()
+#else
+ #define OPEN_MP_MAX_ACTIVE OPEN_MP_NUM_PROCESSORS
+#endif
#endif
diff --git a/include/OpenShot.h b/src/OpenShot.h
similarity index 100%
rename from include/OpenShot.h
rename to src/OpenShot.h
diff --git a/include/OpenShotVersion.h.in b/src/OpenShotVersion.h.in
similarity index 89%
rename from include/OpenShotVersion.h.in
rename to src/OpenShotVersion.h.in
index e15662b2..5e86e8ce 100644
--- a/include/OpenShotVersion.h.in
+++ b/src/OpenShotVersion.h.in
@@ -42,6 +42,15 @@
#define OPENSHOT_VERSION_SO @PROJECT_SO_VERSION@ /// Shared object version number. This increments any time the API and ABI changes (so old apps will no longer link)
+// Useful dependency versioning / feature availability
+#cmakedefine QT_VERSION_STR "@QT_VERSION_STR@"
+#cmakedefine AVCODEC_VERSION_STR "@AVCODEC_VERSION_STR@"
+#cmakedefine AVFORMAT_VERSION_STR "@AVFORMAT_VERSION_STR@"
+#cmakedefine AVUTIL_VERSION_STR "@AVUTIL_VERSION_STR@"
+#cmakedefine01 HAVE_IMAGEMAGICK
+#cmakedefine01 HAVE_RESVG
+#cmakedefine01 APPIMAGE_BUILD
+
#include
namespace openshot
@@ -67,4 +76,4 @@ namespace openshot
openshot::OpenShotVersion GetVersion();
}
-#endif // OPENSHOT_VERSION_H
\ No newline at end of file
+#endif // OPENSHOT_VERSION_H
diff --git a/src/PlayerBase.cpp b/src/PlayerBase.cpp
index f152fbbb..844b8d60 100644
--- a/src/PlayerBase.cpp
+++ b/src/PlayerBase.cpp
@@ -28,7 +28,7 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/PlayerBase.h"
+#include "PlayerBase.h"
using namespace openshot;
diff --git a/include/PlayerBase.h b/src/PlayerBase.h
similarity index 100%
rename from include/PlayerBase.h
rename to src/PlayerBase.h
diff --git a/src/Point.cpp b/src/Point.cpp
index 13679977..4636b437 100644
--- a/src/Point.cpp
+++ b/src/Point.cpp
@@ -28,7 +28,7 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/Point.h"
+#include "Point.h"
using namespace std;
using namespace openshot;
diff --git a/include/Point.h b/src/Point.h
similarity index 100%
rename from include/Point.h
rename to src/Point.h
diff --git a/src/Profiles.cpp b/src/Profiles.cpp
index 5351520e..167affe2 100644
--- a/src/Profiles.cpp
+++ b/src/Profiles.cpp
@@ -28,7 +28,7 @@
* along with OpenShot Library. If not, see .
*/
-#include "../include/Profiles.h"
+#include "Profiles.h"
using namespace openshot;
diff --git a/include/Profiles.h b/src/Profiles.h
similarity index 96%
rename from include/Profiles.h
rename to src/Profiles.h
index 3b5ebd0d..1de7c072 100644
--- a/include/Profiles.h
+++ b/src/Profiles.h
@@ -35,12 +35,12 @@
#include
#include
#include
-#include
-#include
-#include
+#include
+#include
+#include
#include
-#include
-#include
+#include
+#include
#include "Exceptions.h"
#include "Fraction.h"
#include "Json.h"
diff --git a/src/Qt/AudioPlaybackThread.cpp b/src/Qt/AudioPlaybackThread.cpp
index 44c837e7..178642f6 100644
--- a/src/Qt/AudioPlaybackThread.cpp
+++ b/src/Qt/AudioPlaybackThread.cpp
@@ -29,7 +29,10 @@
* along with OpenShot Library. If not, see .
*/
-#include "../../include/Qt/AudioPlaybackThread.h"
+#include "AudioPlaybackThread.h"
+
+#include // for std::this_thread::sleep_for
+#include // for std::chrono::milliseconds
namespace openshot
{
@@ -194,7 +197,7 @@ namespace openshot
transport.start();
while (!threadShouldExit() && transport.isPlaying() && is_playing)
- usleep(2500);
+ std::this_thread::sleep_for(std::chrono::milliseconds(2));
// Stop audio and shutdown transport
Stop();
diff --git a/include/Qt/AudioPlaybackThread.h b/src/Qt/AudioPlaybackThread.h
similarity index 100%
rename from include/Qt/AudioPlaybackThread.h
rename to src/Qt/AudioPlaybackThread.h
diff --git a/src/Qt/PlayerDemo.cpp b/src/Qt/PlayerDemo.cpp
index ba20e5cf..e8751a62 100644
--- a/src/Qt/PlayerDemo.cpp
+++ b/src/Qt/PlayerDemo.cpp
@@ -28,11 +28,20 @@
* along with OpenShot Library. If not, see .
*/
-#include "stdio.h"
-#include "../../include/QtPlayer.h"
-#include "../../include/Qt/PlayerDemo.h"
+#include
+
+#include "PlayerDemo.h"
+#include "../QtPlayer.h"
+
#include
#include
+#include
+#include
+#include
+#include