diff --git a/.gitattributes b/.gitattributes
index 73d0f2945..70661ac67 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -2,5 +2,7 @@
* text=auto
# Enforce LF line endings on source files
-*.h text eol=lf
-*.cpp text eol=lf
+*.h text eol=lf
+*.cpp text eol=lf
+*.sh text eol=lf
+*.desktop text eol=lf
diff --git a/.github/ISSUE_TEMPLATE/00-olive_unsupported.md b/.github/ISSUE_TEMPLATE/00-olive_unsupported.md
index 700c5c1cb..88e715ff8 100644
--- a/.github/ISSUE_TEMPLATE/00-olive_unsupported.md
+++ b/.github/ISSUE_TEMPLATE/00-olive_unsupported.md
@@ -5,7 +5,6 @@ title: "[UNSUPPORTED]"
labels: "Legacy (Unsupported)"
assignees: ''
---
-
# Olive 0.1 is unsupported
Unfortunately no one is supporting Olive 0.1 at this time. Any reports pertaining to it will be
diff --git a/.github/ISSUE_TEMPLATE/01-crash_issue.md b/.github/ISSUE_TEMPLATE/01-crash_issue.md
index c05544838..87e916dee 100644
--- a/.github/ISSUE_TEMPLATE/01-crash_issue.md
+++ b/.github/ISSUE_TEMPLATE/01-crash_issue.md
@@ -4,16 +4,26 @@ about: Report a fatal crash that resulted in Olive unexpectedly closing.
title: "[CRASH]"
labels: "Crash, Triage"
assignees: ''
-
---
-**Commit Hash**
+**Commit Hash**
+
**Platform**
+
**Summary**
+
**Steps to Reproduce**
-**Backtrace**
+1.
+2.
+3.
+
+Backtrace
+
+
+
+
**Additional Information**
diff --git a/.github/ISSUE_TEMPLATE/50-build_issue.md b/.github/ISSUE_TEMPLATE/50-build_issue.md
index f14b6c914..155c7c750 100644
--- a/.github/ISSUE_TEMPLATE/50-build_issue.md
+++ b/.github/ISSUE_TEMPLATE/50-build_issue.md
@@ -4,12 +4,14 @@ about: Report an issue related to compiling or packaging (including continuous i
title: "[BUILD]"
labels: "Building/Packaging, Triage"
assignees: ''
-
---
-**Commit Hash**
+**Commit Hash**
+
**Platform**
+
**Summary**
+
**Additional Information / Output**
diff --git a/.github/ISSUE_TEMPLATE/50-cache_issue.md b/.github/ISSUE_TEMPLATE/50-cache_issue.md
index 13b8bc14b..f346d8245 100644
--- a/.github/ISSUE_TEMPLATE/50-cache_issue.md
+++ b/.github/ISSUE_TEMPLATE/50-cache_issue.md
@@ -4,12 +4,14 @@ about: Report an issue related to the disk cache system, including failure to c
title: "[CACHE]"
labels: "Disk Cache, Triage"
assignees: ''
-
---
-**Commit Hash**
+**Commit Hash**
+
**Platform**
+
**Summary**
+
**Additional Information / Output**
diff --git a/.github/ISSUE_TEMPLATE/50-codec_issue.md b/.github/ISSUE_TEMPLATE/50-codec_issue.md
index a3a936915..09fc991ac 100644
--- a/.github/ISSUE_TEMPLATE/50-codec_issue.md
+++ b/.github/ISSUE_TEMPLATE/50-codec_issue.md
@@ -4,12 +4,14 @@ about: Report an issue related to codec handling, including importing footage or
title: "[CODEC]"
labels: "Codec, Triage"
assignees: ''
-
---
-**Commit Hash**
+**Commit Hash**
+
**Platform**
+
**Summary**
+
**Additional Information / Output**
diff --git a/.github/ISSUE_TEMPLATE/50-color_issue.md b/.github/ISSUE_TEMPLATE/50-color_issue.md
index 6a7464ea3..99e4c803c 100644
--- a/.github/ISSUE_TEMPLATE/50-color_issue.md
+++ b/.github/ISSUE_TEMPLATE/50-color_issue.md
@@ -4,12 +4,14 @@ about: Report an issue related to the management of pixels and color, including
title: "[COLOR]"
labels: "Color Management, Triage"
assignees: ''
-
---
-**Commit Hash**
+**Commit Hash**
+
**Platform**
+
**Summary**
+
**Additional Information / Output**
diff --git a/.github/ISSUE_TEMPLATE/50-editing_issue.md b/.github/ISSUE_TEMPLATE/50-editing_issue.md
index c731e221f..88d2f16c1 100644
--- a/.github/ISSUE_TEMPLATE/50-editing_issue.md
+++ b/.github/ISSUE_TEMPLATE/50-editing_issue.md
@@ -4,12 +4,14 @@ about: Report an issue related to the overall editing experience, including usa
title: "[EDIT]"
labels: "Timeline/Editing, Triage"
assignees: ''
-
---
-**Commit Hash**
+**Commit Hash**
+
**Platform**
+
**Summary**
+
**Additional Information / Output**
diff --git a/.github/ISSUE_TEMPLATE/50-export_issue.md b/.github/ISSUE_TEMPLATE/50-export_issue.md
index e21a79294..4e6e1f3a2 100644
--- a/.github/ISSUE_TEMPLATE/50-export_issue.md
+++ b/.github/ISSUE_TEMPLATE/50-export_issue.md
@@ -4,12 +4,14 @@ about: Report an issue related to exporting videos from Olive, including errors
title: "[EXPORT]"
labels: "Export, Triage"
assignees: ''
-
---
-**Commit Hash**
+**Commit Hash**
+
**Platform**
+
**Summary**
+
**Additional Information / Output**
diff --git a/.github/ISSUE_TEMPLATE/50-node_issue.md b/.github/ISSUE_TEMPLATE/50-node_issue.md
index 7749521ba..ec31c8989 100644
--- a/.github/ISSUE_TEMPLATE/50-node_issue.md
+++ b/.github/ISSUE_TEMPLATE/50-node_issue.md
@@ -4,12 +4,14 @@ about: Report an issue related to the node-based compositing system, including
title: "[NODES]"
labels: "Nodes/Compositing, Triage"
assignees: ''
-
---
-**Commit Hash**
+**Commit Hash**
+
**Platform**
+
**Summary**
+
**Additional Information / Output**
diff --git a/.github/ISSUE_TEMPLATE/50-playback_issue.md b/.github/ISSUE_TEMPLATE/50-playback_issue.md
index 00aa98149..32a3cc13b 100644
--- a/.github/ISSUE_TEMPLATE/50-playback_issue.md
+++ b/.github/ISSUE_TEMPLATE/50-playback_issue.md
@@ -4,12 +4,14 @@ about: Report an issue related to the playback of video or audio, including lag
title: "[PLAYBACK]"
labels: "Playback, Triage"
assignees: ''
-
---
-**Commit Hash**
+**Commit Hash**
+
**Platform**
+
**Summary**
+
**Additional Information / Output**
diff --git a/.github/ISSUE_TEMPLATE/50-project_issue.md b/.github/ISSUE_TEMPLATE/50-project_issue.md
index 9e151539c..ff9175354 100644
--- a/.github/ISSUE_TEMPLATE/50-project_issue.md
+++ b/.github/ISSUE_TEMPLATE/50-project_issue.md
@@ -4,12 +4,14 @@ about: Report an issue related to project management, including working with an
title: "[PROJECT]"
labels: "Project, Triage"
assignees: ''
-
---
-**Commit Hash**
+**Commit Hash**
+
**Platform**
+
**Summary**
+
**Additional Information / Output**
diff --git a/.github/ISSUE_TEMPLATE/50-renderer_issue.md b/.github/ISSUE_TEMPLATE/50-renderer_issue.md
index 3d6812004..e77316402 100644
--- a/.github/ISSUE_TEMPLATE/50-renderer_issue.md
+++ b/.github/ISSUE_TEMPLATE/50-renderer_issue.md
@@ -4,12 +4,14 @@ about: Report an issue related to rendering, including corrupted frames, incorr
title: "[RENDER]"
labels: "Renderer, Triage"
assignees: ''
-
---
-**Commit Hash**
+**Commit Hash**
+
**Platform**
+
**Summary**
+
**Additional Information / Output**
diff --git a/.github/ISSUE_TEMPLATE/50-ui_issue.md b/.github/ISSUE_TEMPLATE/50-ui_issue.md
index 12af9f863..f746024de 100644
--- a/.github/ISSUE_TEMPLATE/50-ui_issue.md
+++ b/.github/ISSUE_TEMPLATE/50-ui_issue.md
@@ -4,12 +4,14 @@ about: Report an issue related to general user interface usability, including b
title: "[UI]"
labels: "User Interface, Triage"
assignees: ''
-
---
-**Commit Hash**
+**Commit Hash**
+
**Platform**
+
**Summary**
+
**Additional Information / Output**
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 310a45902..8d3e873c1 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,4 +1,4 @@
-name: CMake
+name: CI
on:
push:
@@ -6,137 +6,405 @@ on:
- master
env:
- # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
- BUILD_TYPE: RelWithDebInfo
+ DOWNLOAD_TOOL: curl -fLOSs --retry 3
jobs:
- build:
- # The CMake configure and build commands are platform agnostic and should work equally
- # well on Windows or Mac. You can convert this to a matrix build if you need
- # cross-platform coverage.
- # See: https://docs.github.com/en/actions/configuring-and-managing-workflows/configuring-a-workflow#configuring-a-build-matrix
- runs-on: windows-latest
-
- env:
- PKGNAME: Olive-${GITHUB_SHA:8}-Windows-x86_64
+ linux:
+ strategy:
+ fail-fast: false
+ matrix:
+ build: [1, 2]
+ include:
+ - build: 1
+ build-type: RelWithDebInfo
+ cc-compiler: gcc
+ cxx-compiler: g++
+ compiler-name: GCC 9.3.1
+ cmake-gen: Ninja
+ os-name: Linux (CentOS 7)
+ vfx-cy: 2021
+ ci-common-version: 2
+ - build: 2
+ build-type: RelWithDebInfo
+ cc-compiler: clang
+ cxx-compiler: clang++
+ compiler-name: Clang 10.0.0
+ cmake-gen: Ninja
+ os-name: Linux (CentOS 7)
+ vfx-cy: 2021
+ ci-common-version: 2
+ name: |
+ ${{ matrix.os-name }}
+ <${{ matrix.compiler-name }},
+ ${{ matrix.build-type }},
+ ${{ matrix.cmake-gen }},
+ VFX CY${{ matrix.vfx-cy }},
+ CI Common Version ${{ matrix.ci-common-version }}>
+ runs-on: ubuntu-latest
+ container:
+ image: olivevideoeditor/ci-olive:${{ matrix.vfx-cy }}.${{ matrix.ci-common-version }}
steps:
- - name: Set package name
- shell: bash
- run: echo "::set-env name=PKGNAME::$(echo Olive-${GITHUB_SHA::8}-Windows-x86_64)"
+ - name: Checkout Source Code
+ uses: actions/checkout@v2
- - uses: actions/checkout@v2
+ - name: Configure CMake
+ run: |
+ mkdir build
+ cd build
+ cmake .. -G "${{ matrix.cmake-gen }}" \
+ -DCMAKE_BUILD_TYPE="${{ matrix.build-type }}" \
+ -DCMAKE_C_COMPILER="${{ matrix.cc-compiler }}" \
+ -DCMAKE_CXX_COMPILER="${{ matrix.cxx-compiler }}"
- - name: Create Build Environment
- # Some projects don't allow in-source building, so create a separate build directory
- # We'll use this as our working directory for all subsequent commands
- run: cmake -E make_directory ${{runner.workspace}}/build
+ - name: Build
+ working-directory: build
+ run: |
+ cmake --build .
- - name: Enable Developer Command Prompt
- uses: ilammy/msvc-dev-cmd@v1.3.0
+ - name: Test
+ working-directory: build
+ run: |
+ ctest -C ${{ matrix.build-type }}
- - name: Acquire Qt
- uses: jurplel/install-qt-action@v2.8.0
- with:
- version: 5.15.1
+ - name: Create Package
+ id: package
+ working-directory: build
+ env:
+ ARCH: x86_64
+ run: |
+ cmake --install app --prefix appdir/usr
+ VERSION=${GITHUB_SHA::8}
+ export VERSION
+ /usr/local/linuxdeployqt-x86_64.AppImage \
+ appdir/usr/share/applications/org.olivevideoeditor.Olive.desktop \
+ -appimage \
+ --appimage-extract-and-run
+ filename=$(echo Olive*.AppImage)
+ pkgname="${filename/x86_64/Linux-x86_64}"
+ mv "${filename}" "${pkgname}"
+ echo "::set-output name=pkgname::${pkgname}"
+ echo "::set-output name=artifact::${filename/x86_64/Linux-x86_64-${{ matrix.cc-compiler }}}"
- - name: Acquire FFmpeg
- run: curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/ffmpeg-win.7z && 7z x ffmpeg-win.7z
+ - name: Upload Artifact to GitHub
+ uses: actions/upload-artifact@v2
+ with:
+ name: ${{ steps.package.outputs.artifact }}
+ path: build/Olive*.AppImage
- - name: Acquire OpenColorIO
- run: curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/ocio-win.7z && 7z x ocio-win.7z
+ - name: Upload Packages to Nightly Page
+ working-directory: build
+ env:
+ GH_AUTH_KEY: ${{ secrets.GH_AUTH_KEY }}
+ PKGNAME: ${{ steps.package.outputs.pkgname }}
+ if: github.event_name == 'push' && matrix.cc-compiler == 'gcc'
+ run: |
+ curl -X POST \
+ -F file="@$PKGNAME" \
+ -F key="$GH_AUTH_KEY" \
+ -F hash="$GITHUB_SHA" \
+ -F time="$(git show -s --format=%ct)" \
+ -F comment="$(git show -s --format=%B)" \
+ https://olivevideoeditor.org/dl/push.php
- - name: Acquire OpenEXR
- run: curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/openexr-win.7z && 7z x openexr-win.7z
+ windows:
+ strategy:
+ matrix:
+ build: [1]
+ include:
+ - build: 1
+ build-type: RelWithDebInfo
+ compiler-name: MSVC 16.x
+ os-name: Windows
+ os-arch: x86_64
+ os: windows-latest
+ cmake-gen: Ninja
+ env:
+ EXTRACT_TOOL: 7z x
+ name: |
+ ${{ matrix.os-name }}
+ <${{ matrix.compiler-name }},
+ ${{ matrix.build-type }},
+ ${{ matrix.cmake-gen }}>
+ runs-on: ${{ matrix.os }}
- - name: Acquire OpenImageIO
- run: curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/oiio-win.7z && 7z x oiio-win.7z
+ steps:
+ - name: Checkout Source Code
+ uses: actions/checkout@v2
- - name: Acquire libpng
- run: curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/libpng-win.7z && 7z x libpng-win.7z
+ - name: Automatically Generate Package Name
+ shell: bash
+ env:
+ PLATFORM: ${{ matrix.os-name }}
+ ARCH: ${{ matrix.os-arch }}
+ run: |
+ echo "::set-env name=PKGNAME::$(echo Olive-${GITHUB_SHA::8}-${PLATFORM}-${ARCH})"
- - name: Acquire libtiff
- run: curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/libtiff-win.7z && 7z x libtiff-win.7z
+ - name: Create Build Folder
+ run: |
+ cmake -E make_directory ${{ runner.workspace }}/build
- - name: Acquire turbojpeg
- run: curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/turbojpeg-win.7z && 7z x turbojpeg-win.7z
+ - name: Enable Developer Command Prompt (Windows)
+ uses: ilammy/msvc-dev-cmd@v1.3.0
- - name: Acquire zlib
- run: curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/zlib-win.7z && 7z x zlib-win.7z
+ - name: Acquire Qt
+ uses: jurplel/install-qt-action@v2.8.0
+ with:
+ version: 5.15.1
- - name: Acquire Boost
- run: curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/boost-win.7z && 7z x boost-win.7z
+ - name: Acquire FFmpeg
+ shell: bash
+ run: |
+ $DOWNLOAD_TOOL http://olivevideoeditor.org/deps/ffmpeg-win.7z
+ $EXTRACT_TOOL ffmpeg-win.7z
- - name: Acquire Crashpad
- run: curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/crashpad-win.7z && 7z x crashpad-win.7z
+ - name: Acquire OpenColorIO
+ shell: bash
+ run: |
+ $DOWNLOAD_TOOL http://olivevideoeditor.org/deps/ocio-win.7z
+ $EXTRACT_TOOL ocio-win.7z
- - name: Configure CMake
- # Use a bash shell so we can use the same syntax for environment variable
- # access regardless of the host operating system
- shell: bash
- working-directory: ${{runner.workspace}}/build
- # Note the current convention is to use the -S and -B options here to specify source
- # and build directories, but this is only available with CMake 3.13 and higher.
- # The CMake binaries on the Github Actions machines are (as of this writing) 3.12
- run: PATH=$GITHUB_WORKSPACE:$PATH cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G "Ninja"
+ - name: Acquire OpenEXR
+ shell: bash
+ run: |
+ $DOWNLOAD_TOOL http://olivevideoeditor.org/deps/openexr-win.7z
+ $EXTRACT_TOOL openexr-win.7z
- - name: Build
- working-directory: ${{runner.workspace}}/build
- shell: bash
- # Execute the build. You can specify a specific target with "--target "
- run: cmake --build . --config $BUILD_TYPE
+ - name: Acquire OpenImageIO
+ shell: bash
+ run: |
+ $DOWNLOAD_TOOL http://olivevideoeditor.org/deps/oiio-win.7z
+ $EXTRACT_TOOL oiio-win.7z
- - name: Test
- working-directory: ${{runner.workspace}}/build
- shell: bash
- # Execute tests defined by the CMake configuration.
- # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
- run: ctest -C $BUILD_TYPE
+ - name: Acquire Crashpad
+ shell: bash
+ run: |
+ $DOWNLOAD_TOOL http://olivevideoeditor.org/deps/crashpad-win.7z
+ $EXTRACT_TOOL crashpad-win.7z
- - name: Create Package
- working-directory: ${{runner.workspace}}/build
- shell: cmd
- run: mkdir olive-editor && copy app\olive-editor.exe olive-editor && copy app\olive-editor.pdb olive-editor && windeployqt olive-editor\olive-editor.exe && copy %GITHUB_WORKSPACE%\bin\*.dll olive-editor && copy %GITHUB_WORKSPACE%\out\Default\crashpad_handler.exe olive-editor
+ - name: Configure CMake
+ shell: bash
+ working-directory: ${{ runner.workspace }}/build
+ run: |
+ PATH=$GITHUB_WORKSPACE:$PATH \
+ cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} -G "${{ matrix.cmake-gen }}"
- - name: Deploy Installer
- working-directory: ${{runner.workspace}}/build
- shell: cmd
- run: curl -fLOSs https://download.microsoft.com/download/3/2/2/3224B87F-CFA0-4E70-BDA3-3DE650EFEBA5/vcredist_x64.exe && copy %GITHUB_WORKSPACE%\app\packaging\windows\nsis\* . && copy %GITHUB_WORKSPACE%\LICENSE . && "C:/Program Files (x86)/NSIS/makensis.exe" -V4 -DX64 "-XOutFile %PKGNAME%.exe" olive.nsi
+ - name: Build
+ working-directory: ${{ runner.workspace }}/build
+ shell: bash
+ run: |
+ cmake --build .
- - name: Deploy Portable
- working-directory: ${{runner.workspace}}/build
- shell: cmd
- run: type NUL > olive-editor\portable && 7z a %PKGNAME%.zip olive-editor
+ - name: Test
+ working-directory: ${{ runner.workspace }}/build
+ shell: bash
+ run: |
+ ctest -C ${{ matrix.build-type }}
- - name: Export Crashpad Symbols and Upload To Server
- working-directory: ${{runner.workspace}}/build
- shell: cmd
- env:
- GH_AUTH_KEY: ${{secrets.GH_AUTH_KEY}}
- run: curl -fLSs --retry 3 https://github.com/google/breakpad/blob/master/src/tools/windows/binaries/dump_syms.exe?raw=true > dump_syms.exe && dump_syms app\olive-editor.pdb > olive-editor.sym && curl -X POST -F symfile=@olive-editor.sym -F key="%GH_AUTH_KEY%" https://olivevideoeditor.org/crashpad/symbols.php
+ - name: Create Package
+ working-directory: ${{ runner.workspace }}/build
+ shell: bash
+ run: |
+ mkdir olive-editor
+ cp app/olive-editor.exe olive-editor
+ cp app/olive-crashhandler.exe olive-editor
+ cp app/olive-editor.pdb olive-editor
+ windeployqt olive-editor/olive-crashhandler.exe
+ windeployqt olive-editor/olive-editor.exe
+ cp $(cygpath $GITHUB_WORKSPACE)/bin/*.dll olive-editor
+ cp $(cygpath $GITHUB_WORKSPACE)/out/Default/crashpad_handler.exe olive-editor
+ cp $(cygpath $GITHUB_WORKSPACE)/breakpad/bin/* olive-editor
- - name: Upload Installer to Nightly Page
- shell: bash
- working-directory: ${{runner.workspace}}/build
- env:
- GH_AUTH_KEY: ${{secrets.GH_AUTH_KEY}}
- run: curl -X POST -F file=@$PKGNAME.exe -F key="$GH_AUTH_KEY" -F hash="$GITHUB_SHA" -F time="$(git -C $GITHUB_WORKSPACE show -s --format=%ct)" -F comment="$(git -C $GITHUB_WORKSPACE show -s --format=%B)" https://olivevideoeditor.org/dl/push.php
+ - name: Deploy Packages
+ working-directory: ${{ runner.workspace }}/build
+ shell: bash
+ run: |
+ # Create Installer Executable
+ $DOWNLOAD_TOOL https://download.microsoft.com/download/3/2/2/3224B87F-CFA0-4E70-BDA3-3DE650EFEBA5/vcredist_x64.exe
+ cp $(cygpath $GITHUB_WORKSPACE)/app/packaging/windows/nsis/* .
+ cp $(cygpath $GITHUB_WORKSPACE)/LICENSE .
+ makensis -V4 -DX64 "-XOutFile $PKGNAME.exe" olive.nsi
- - name: Upload Portable to Nightly Page
- shell: bash
- working-directory: ${{runner.workspace}}/build
- env:
- GH_AUTH_KEY: ${{secrets.GH_AUTH_KEY}}
- run: curl -X POST -F file=@$PKGNAME.zip -F key="$GH_AUTH_KEY" -F hash="$GITHUB_SHA" -F time="$(git -C $GITHUB_WORKSPACE show -s --format=%ct)" -F comment="$(git -C $GITHUB_WORKSPACE show -s --format=%B)" https://olivevideoeditor.org/dl/push.php
+ # Create Portable ZIP
+ echo -n > olive-editor/portable
+ 7z a $PKGNAME.zip olive-editor
- - name: Upload Installer Artifact
- uses: actions/upload-artifact@v2
- with:
- name: ${{env.PKGNAME}}.exe
- path: ${{runner.workspace}}/build/${{env.PKGNAME}}.exe
+ - name: Upload Installer Artifact to GitHub
+ uses: actions/upload-artifact@v2
+ with:
+ name: ${{ env.PKGNAME }}.exe
+ path: ${{ runner.workspace }}/build/${{ env.PKGNAME }}.exe
- - name: Upload Portable Artifact
- uses: actions/upload-artifact@v2
- with:
- name: ${{env.PKGNAME}}.zip
- path: ${{runner.workspace}}/build/${{env.PKGNAME}}.zip
+ - name: Upload Portable Artifact to GitHub
+ uses: actions/upload-artifact@v2
+ with:
+ name: ${{ env.PKGNAME }}.zip
+ path: ${{ runner.workspace }}/build/${{ env.PKGNAME }}.zip
+
+ - name: Upload Packages to Nightly Page
+ shell: bash
+ working-directory: ${{ runner.workspace }}/build
+ env:
+ GH_AUTH_KEY: ${{ secrets.GH_AUTH_KEY }}
+ if: github.event_name == 'push'
+ run: |
+ curl -X POST \
+ -F file=@$PKGNAME.exe \
+ -F key="$GH_AUTH_KEY" \
+ -F hash="$GITHUB_SHA" \
+ -F time="$(git -C $GITHUB_WORKSPACE show -s --format=%ct)" \
+ -F comment="$(git -C $GITHUB_WORKSPACE show -s --format=%B)" \
+ https://olivevideoeditor.org/dl/push.php
+
+ curl -X POST \
+ -F file=@$PKGNAME.zip \
+ -F key="$GH_AUTH_KEY" \
+ -F hash="$GITHUB_SHA" \
+ -F time="$(git -C $GITHUB_WORKSPACE show -s --format=%ct)" \
+ -F comment="$(git -C $GITHUB_WORKSPACE show -s --format=%B)" \
+ https://olivevideoeditor.org/dl/push.php
+
+ - name: Export Crashpad Symbols and Upload To Server
+ working-directory: ${{ runner.workspace }}/build
+ shell: bash
+ env:
+ GH_AUTH_KEY: ${{ secrets.GH_AUTH_KEY }}
+ run: |
+ curl -fLSs --retry 3 https://github.com/google/breakpad/blob/master/src/tools/windows/binaries/dump_syms.exe?raw=true > dump_syms.exe
+ ./dump_syms app/olive-editor.pdb > olive-editor.sym
+ curl -X POST \
+ -F symfile=@olive-editor.sym \
+ -F key="$GH_AUTH_KEY" \
+ https://olivevideoeditor.org/crashpad/symbols.php
+
+ macos:
+ strategy:
+ matrix:
+ build: [1]
+ include:
+ - build: 1
+ build-type: RelWithDebInfo
+ compiler-name: Clang LLVM
+ os-name: macOS
+ os-arch: x86_64
+ os: macos-latest
+ cmake-gen: Unix Makefiles
+ env:
+ EXTRACT_TOOL: sudo unzip -d /usr/local -o
+ name: |
+ ${{ matrix.os-name }}
+ <${{ matrix.compiler-name }},
+ ${{ matrix.build-type }},
+ ${{ matrix.cmake-gen }}>
+ runs-on: ${{ matrix.os }}
+
+ steps:
+ - name: Checkout Source Code
+ uses: actions/checkout@v2
+
+ - name: Automatically Generate Package Name
+ shell: bash
+ env:
+ PLATFORM: ${{ matrix.os-name }}
+ ARCH: ${{ matrix.os-arch }}
+ run: |
+ echo "::set-env name=PKGNAME::$(echo Olive-${GITHUB_SHA::8}-${PLATFORM}-${ARCH})"
+
+ - name: Create Build Folder
+ run: |
+ cmake -E make_directory ${{ runner.workspace }}/build
+
+ - name: Acquire Qt
+ uses: jurplel/install-qt-action@v2.8.0
+ with:
+ version: 5.15.1
+
+ - name: Acquire FFmpeg
+ shell: bash
+ run: |
+ $DOWNLOAD_TOOL http://olivevideoeditor.org/deps/ffmpeg-mac.zip
+ $EXTRACT_TOOL ffmpeg-mac.zip
+
+ - name: Acquire OpenColorIO
+ shell: bash
+ run: |
+ $DOWNLOAD_TOOL http://olivevideoeditor.org/deps/ocio-mac.zip
+ $EXTRACT_TOOL ocio-mac.zip
+
+ - name: Acquire OpenEXR
+ shell: bash
+ run: |
+ $DOWNLOAD_TOOL http://olivevideoeditor.org/deps/openexr-mac.zip
+ $EXTRACT_TOOL openexr-mac.zip
+
+ - name: Acquire OpenImageIO
+ shell: bash
+ run: |
+ $DOWNLOAD_TOOL http://olivevideoeditor.org/deps/oiio-mac.zip
+ $EXTRACT_TOOL oiio-mac.zip
+
+ - name: Acquire Crashpad
+ shell: bash
+ run: |
+ $DOWNLOAD_TOOL http://olivevideoeditor.org/deps/crashpad-mac.zip
+ $EXTRACT_TOOL crashpad-mac.zip
+
+ - name: Configure CMake
+ shell: bash
+ working-directory: ${{ runner.workspace }}/build
+ run: |
+ PATH=$GITHUB_WORKSPACE:$PATH \
+ cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} -G "${{ matrix.cmake-gen }}"
+
+ - name: Build
+ working-directory: ${{ runner.workspace }}/build
+ shell: bash
+ run: |
+ cmake --build .
+
+ - name: Test
+ working-directory: ${{ runner.workspace }}/build
+ shell: bash
+ run: |
+ ctest -C ${{ matrix.build-type }}
+
+ - name: Create Package
+ working-directory: ${{ runner.workspace }}/build
+ shell: bash
+ run: |
+ export BUNDLE_NAME=Olive.app
+ mv app/$BUNDLE_NAME .
+
+ # HACK: OCIO doesn't link it's library properly so we need to manually correct
+ install_name_tool -change libOpenColorIO.1.dylib @rpath/libOpenColorIO.1.dylib $BUNDLE_NAME/Contents/MacOS/Olive
+
+ macdeployqt $BUNDLE_NAME
+ $DOWNLOAD_TOOL https://github.com/arl/macdeployqtfix/raw/master/macdeployqtfix.py
+ python2 macdeployqtfix.py $BUNDLE_NAME/Contents/MacOS/Olive /usr/local/Cellar/qt5/5.*/
+
+ - name: Deploy Packages
+ working-directory: ${{ runner.workspace }}/build
+ shell: bash
+ run: |
+ zip -r $PKGNAME.zip Olive.app
+
+ - name: Upload Artifact to GitHub
+ uses: actions/upload-artifact@v2
+ with:
+ name: ${{ env.PKGNAME }}.zip
+ path: ${{ runner.workspace }}/build/${{ env.PKGNAME }}.zip
+
+ - name: Upload Packages to Nightly Page
+ shell: bash
+ working-directory: ${{ runner.workspace }}/build
+ env:
+ GH_AUTH_KEY: ${{ secrets.GH_AUTH_KEY }}
+ if: github.event_name == 'push'
+ run: |
+ curl -X POST \
+ -F file=@$PKGNAME.zip \
+ -F key="$GH_AUTH_KEY" \
+ -F hash="$GITHUB_SHA" \
+ -F time="$(git -C $GITHUB_WORKSPACE show -s --format=%ct)" \
+ -F comment="$(git -C $GITHUB_WORKSPACE show -s --format=%B)" \
+ https://olivevideoeditor.org/dl/push.php
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2f8ab7419..06a282d31 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -29,6 +29,8 @@ set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
+set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13")
+
set(OLIVE_DEFINITIONS -DAPPVERSION="${PROJECT_VERSION}" -DQT_DEPRECATED_WARNINGS)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
diff --git a/README.md b/README.md
index 9b95c2b60..9291d1b77 100644
--- a/README.md
+++ b/README.md
@@ -1,19 +1,17 @@
-# Olive Video Editor [](https://travis-ci.org/olive-editor/olive) [](https://ci.appveyor.com/project/itsmattkc/olive)
+# Olive Video Editor [](https://github.com/olive-editor/olive/actions?query=branch%3Amaster)
Olive is a free non-linear video editor for Windows, macOS, and Linux.
-
+
-**Discover more:** [Website](https://www.olivevideoeditor.org/) | [Twitter](https://twitter.com/oliveteam) | [Discord](https://discord.gg/4Ae9KZn) | [Patreon](https://www.patreon.com/olivevideoeditor)
-
-## Getting Started with Olive:
-
-Binaries of Olive are available at: https://olivevideoeditor.org/download.php
-
-Some tutorials can be seen on the GitHub Wiki: https://github.com/olive-editor/olive/wiki/Overview-Guide
+**Discover more:** [Website](https://www.olivevideoeditor.org/) | [Binaries](https://olivevideoeditor.org/download.php) | [Twitter](https://twitter.com/oliveteam) | [Discord](https://discord.gg/4Ae9KZn) | [Patreon](https://www.patreon.com/olivevideoeditor) | [Tutorials](https://github.com/olive-editor/olive/wiki/Overview-Guide)
**NOTE: Olive is alpha software and is considered highly unstable. While we highly appreciate users testing and providing usage information, please use at your own risk.**
+## Binaries
+
+[Nightly binaries are available on the website.](https://olivevideoeditor.org/download.php)
+
## Support Olive:
Please consider supporting Olive:
diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
index a313bb3a2..089a41f04 100644
--- a/app/CMakeLists.txt
+++ b/app/CMakeLists.txt
@@ -178,13 +178,68 @@ if (GoogleCrashpad_FOUND)
${OLIVE_TARGET}
PRIVATE
${CRASHPAD_INCLUDE_DIRS}
- )
+ )
target_link_libraries(
${OLIVE_TARGET}
PRIVATE
${CRASHPAD_LIBRARIES}
+ )
+
+ set(OLIVE_CRASH_TARGET "olive-crashhandler")
+
+ set(OLIVE_CRASH_SOURCES
+ dialog/crashhandler/crashhandler.h
+ dialog/crashhandler/crashhandler.cpp
+ dialog/crashhandler/crashhandlermain.cpp
+ )
+
+ if (WIN32)
+ add_executable(
+ ${OLIVE_CRASH_TARGET}
+ WIN32
+ ${OLIVE_CRASH_SOURCES}
)
+ else()
+ add_executable(
+ ${OLIVE_CRASH_TARGET}
+ ${OLIVE_CRASH_SOURCES}
+ )
+ endif()
+
+ target_include_directories(
+ ${OLIVE_CRASH_TARGET}
+ PRIVATE
+ ${CRASHPAD_INCLUDE_DIRS}
+ )
+
+ target_link_libraries(
+ ${OLIVE_CRASH_TARGET}
+ PRIVATE
+ Qt5::Core
+ Qt5::Gui
+ Qt5::Widgets
+ Qt5::Network
+ ${CRASHPAD_LIBRARIES}
+ )
+
+ set(CRASHPAD_HANDLER "crashpad_handler${CMAKE_EXECUTABLE_SUFFIX}")
+ set(MINIDUMP_STACKWALK "minidump_stackwalk${CMAKE_EXECUTABLE_SUFFIX}")
+
+ if(UNIX AND NOT APPLE)
+ install(TARGETS ${OLIVE_TARGET} ${OLIVE_CRASH_TARGET} RUNTIME DESTINATION bin)
+ install(PROGRAMS ${CRASHPAD_LIBRARY_DIRS}/${CRASHPAD_HANDLER} DESTINATION bin)
+ install(PROGRAMS ${BREAKPAD_BIN_DIR}/${MINIDUMP_STACKWALK} DESTINATION bin)
+ endif()
+
+ if(APPLE)
+ # Move crash handler executables inside Mac app bundle
+ add_custom_command(TARGET ${OLIVE_CRASH_TARGET} POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different ${OLIVE_CRASH_TARGET} $
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CRASHPAD_LIBRARY_DIRS}/${CRASHPAD_HANDLER} $
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different ${BREAKPAD_BIN_DIR}/${MINIDUMP_STACKWALK} $
+ )
+ endif()
endif()
# Set compiler definitions
@@ -209,43 +264,3 @@ if(DOXYGEN_FOUND)
set(DOXYGEN_EXTRACT_PRIVATE "YES")
doxygen_add_docs(docs ALL ${OLIVE_SOURCES})
endif()
-
-set(OLIVE_CRASH_TARGET "olive-crashhandler")
-
-set(OLIVE_CRASH_SOURCES
- dialog/crashhandler/crashhandler.h
- dialog/crashhandler/crashhandler.cpp
- dialog/crashhandler/crashhandlermain.cpp
-)
-
-if (WIN32)
- add_executable(
- ${OLIVE_CRASH_TARGET}
- WIN32
- ${OLIVE_CRASH_SOURCES}
- )
-else()
- add_executable(
- ${OLIVE_CRASH_TARGET}
- ${OLIVE_CRASH_SOURCES}
- )
-endif()
-
-target_link_libraries(
- ${OLIVE_CRASH_TARGET}
- PRIVATE
- Qt5::Core
- Qt5::Gui
- Qt5::Widgets
-)
-
-if(UNIX AND NOT APPLE)
- install(TARGETS ${OLIVE_TARGET} ${OLIVE_CRASH_TARGET} RUNTIME DESTINATION bin)
-endif()
-
-if(APPLE)
- # Move crash handler program inside Mac app bundle
- add_custom_command(TARGET ${OLIVE_CRASH_TARGET} POST_BUILD
- COMMAND ${CMAKE_COMMAND} -E copy ${OLIVE_CRASH_TARGET} $
- )
-endif()
diff --git a/app/audio/audiovisualwaveform.cpp b/app/audio/audiovisualwaveform.cpp
index 08c6c27b0..9cba6f755 100644
--- a/app/audio/audiovisualwaveform.cpp
+++ b/app/audio/audiovisualwaveform.cpp
@@ -37,6 +37,7 @@ void AudioVisualWaveform::OverwriteSamples(SampleBufferPtr samples, int sample_r
{
if (!channels_) {
qWarning() << "Failed to write samples - channel count is zero";
+ return;
}
int start_index = time_to_samples(start);
diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp
index 096cfcd07..46cac10fc 100644
--- a/app/codec/ffmpeg/ffmpegdecoder.cpp
+++ b/app/codec/ffmpeg/ffmpegdecoder.cpp
@@ -477,14 +477,19 @@ bool FFmpegDecoder::Probe(Footage *f, const QAtomicInt* cancelled)
// Retrieve metadata about the media
avformat_find_stream_info(fmt_ctx, nullptr);
+ QVector streams(fmt_ctx->nb_streams);
+
// Dump it into the Footage object
for (unsigned int i=0;inb_streams;i++) {
AVStream* avstream = fmt_ctx->streams[i];
+ // Find decoder for this stream, if it exists we can proceed
+ AVCodec* decoder = avcodec_find_decoder(avstream->codecpar->codec_id);
+
StreamPtr str;
- if (avstream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
+ if (avstream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && decoder) {
bool image_is_still = false;
rational pixel_aspect_ratio;
@@ -564,7 +569,7 @@ bool FFmpegDecoder::Probe(Footage *f, const QAtomicInt* cancelled)
str = image_stream;
- } else if (avstream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
+ } else if (avstream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && decoder) {
// Create an audio stream object
AudioStreamPtr audio_stream = std::make_shared();
@@ -600,7 +605,7 @@ bool FFmpegDecoder::Probe(Footage *f, const QAtomicInt* cancelled)
str->set_type(Stream::kAttachment);
break;
default:
- // We should never realistically get here, but we make an "invalid" stream just in case
+ // Fallback to an unknown stream
str->set_type(Stream::kUnknown);
break;
}
@@ -611,11 +616,27 @@ bool FFmpegDecoder::Probe(Footage *f, const QAtomicInt* cancelled)
str->set_timebase(avstream->time_base);
str->set_duration(avstream->duration);
- f->add_stream(str);
+ streams[i] = str;
}
- // As long as we can open the container and retrieve information, this was a successful probe
- result = true;
+ // Check if we could pick up any streams in this file
+ bool found_valid_streams = false;
+
+ foreach (StreamPtr stream, streams) {
+ if (stream->type() != Stream::kUnknown) {
+ found_valid_streams = true;
+ break;
+ }
+ }
+
+ if (found_valid_streams) {
+ // Copy streams over
+ foreach (StreamPtr stream, streams) {
+ f->add_stream(stream);
+ }
+
+ result = true;
+ }
}
// Free all memory
@@ -1330,6 +1351,7 @@ void FFmpegDecoderInstance::RemoveFirstFrame()
FFmpegDecoderInstance::FFmpegDecoderInstance(const char *filename, int stream_index) :
fmt_ctx_(nullptr),
+ codec_ctx_(nullptr),
opts_(nullptr),
scale_ctx_(nullptr),
scale_divider_(0),
diff --git a/app/codec/oiio/oiiodecoder.cpp b/app/codec/oiio/oiiodecoder.cpp
index 395284c44..bfaf46960 100644
--- a/app/codec/oiio/oiiodecoder.cpp
+++ b/app/codec/oiio/oiiodecoder.cpp
@@ -68,11 +68,17 @@ bool OIIODecoder::Probe(Footage *f, const QAtomicInt *cancelled)
// Heuristically determine whether this file is part of an image sequence or not
if (GetImageSequenceDigitCount(f->filename()) > 0) {
+ QSize dim(in->spec().width, in->spec().height);
+
int64_t ind = GetImageSequenceIndex(f->filename());
// Check if files around exist around it with that follow a sequence
- if (QFileInfo::exists(TransformImageSequenceFileName(f->filename(), ind - 1))
- || QFileInfo::exists(TransformImageSequenceFileName(f->filename(), ind + 1))) {
+ QString previous_img_fn = TransformImageSequenceFileName(f->filename(), ind - 1);
+ QString next_img_fn = TransformImageSequenceFileName(f->filename(), ind + 1);
+
+ // GetImageDimensions will return a 0,0 size if the file doesn't exist, so it's safe to check
+ // both existence and matching size with this
+ if (GetImageDimensions(previous_img_fn) == dim || GetImageDimensions(next_img_fn) == dim) {
// We need user feedback here and since UI must occur in the UI thread (and we could be in any thread), we defer
// to the Core which will definitely be in the UI thread and block here until we get an answer from the user
QMetaObject::invokeMethod(Core::instance(),
@@ -316,6 +322,25 @@ bool OIIODecoder::FileTypeIsSupported(const QString& fn)
return true;
}
+QSize OIIODecoder::GetImageDimensions(const QString &fn)
+{
+ QSize sz;
+ auto in = OIIO::ImageInput::open(fn.toStdString());
+
+ if (in) {
+ sz.setWidth(in->spec().width);
+ sz.setHeight(in->spec().height);
+
+ in->close();
+
+#if OIIO_VERSION < 10903
+ OIIO::ImageInput::destroy(in);
+#endif
+ }
+
+ return sz;
+}
+
bool OIIODecoder::OpenImageHandler(const QString &fn)
{
image_ = OIIO::ImageInput::open(fn.toStdString());
diff --git a/app/codec/oiio/oiiodecoder.h b/app/codec/oiio/oiiodecoder.h
index ff278fa63..979923be2 100644
--- a/app/codec/oiio/oiiodecoder.h
+++ b/app/codec/oiio/oiiodecoder.h
@@ -68,6 +68,8 @@ private:
static bool FileTypeIsSupported(const QString& fn);
+ static QSize GetImageDimensions(const QString& fn);
+
bool OpenImageHandler(const QString& fn);
void CloseImageHandle();
diff --git a/app/common/CMakeLists.txt b/app/common/CMakeLists.txt
index ee07f43c4..99521453c 100644
--- a/app/common/CMakeLists.txt
+++ b/app/common/CMakeLists.txt
@@ -23,10 +23,9 @@ set(OLIVE_SOURCES
common/clamp.h
common/commandlineparser.h
common/commandlineparser.cpp
- common/crashhandler.h
- common/crashhandler.cpp
- common/crashpadinterface.cpp
common/crashpadinterface.h
+ common/crashpadinterface.cpp
+ common/crashpadutils.h
common/debug.h
common/debug.cpp
common/define.h
diff --git a/app/common/crashhandler.cpp b/app/common/crashhandler.cpp
deleted file mode 100644
index 469e45233..000000000
--- a/app/common/crashhandler.cpp
+++ /dev/null
@@ -1,150 +0,0 @@
-/***
-
- Olive - Non-Linear Video Editor
- Copyright (C) 2019 Olive Team
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program 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 General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
-***/
-
-#include "crashhandler.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#if defined(Q_OS_WINDOWS)
-#include
-#include
-#include
-#include
-#elif defined(Q_OS_MAC) || defined(Q_OS_LINUX)
-#include
-#endif
-
-#include "common/filefunctions.h"
-
-OLIVE_NAMESPACE_ENTER
-
-void crash_handler(int sig)
-{
- QString log_path = QDir(FileFunctions::GetTempFilePath()).filePath(QStringLiteral("olive_crash"));
- QFile output(log_path);
-
- output.open(QFile::WriteOnly);
- QTextStream ostream(&output);
-
-#ifdef GITHASH
- ostream << "Version: " << GITHASH << "\n";
-#endif
- ostream << "Signal: " << sig << "\n\n";
-
-#if defined(Q_OS_WINDOWS)
- // Use Windows stackwalk API
- HANDLE process = GetCurrentProcess();
- HANDLE thread = GetCurrentThread();
-
- CONTEXT context;
- memset(&context, 0, sizeof(CONTEXT));
- context.ContextFlags = CONTEXT_FULL;
- RtlCaptureContext(&context);
-
- SymInitialize(process, NULL, TRUE);
-
- DWORD image;
- STACKFRAME64 stackframe;
- ZeroMemory(&stackframe, sizeof(STACKFRAME64));
-
-#ifdef _M_IX86
- image = IMAGE_FILE_MACHINE_I386;
- stackframe.AddrPC.Offset = context.Eip;
- stackframe.AddrPC.Mode = AddrModeFlat;
- stackframe.AddrFrame.Offset = context.Ebp;
- stackframe.AddrFrame.Mode = AddrModeFlat;
- stackframe.AddrStack.Offset = context.Esp;
- stackframe.AddrStack.Mode = AddrModeFlat;
-#elif _M_X64
- image = IMAGE_FILE_MACHINE_AMD64;
- stackframe.AddrPC.Offset = context.Rip;
- stackframe.AddrPC.Mode = AddrModeFlat;
- stackframe.AddrFrame.Offset = context.Rsp;
- stackframe.AddrFrame.Mode = AddrModeFlat;
- stackframe.AddrStack.Offset = context.Rsp;
- stackframe.AddrStack.Mode = AddrModeFlat;
-#elif _M_IA64
- image = IMAGE_FILE_MACHINE_IA64;
- stackframe.AddrPC.Offset = context.StIIP;
- stackframe.AddrPC.Mode = AddrModeFlat;
- stackframe.AddrFrame.Offset = context.IntSp;
- stackframe.AddrFrame.Mode = AddrModeFlat;
- stackframe.AddrBStore.Offset = context.RsBSP;
- stackframe.AddrBStore.Mode = AddrModeFlat;
- stackframe.AddrStack.Offset = context.IntSp;
- stackframe.AddrStack.Mode = AddrModeFlat;
-#endif
-
- for (int i = 0; i < 50; i++) {
-
- BOOL result = StackWalk64(
- image, process, thread,
- &stackframe, &context, NULL,
- SymFunctionTableAccess64, SymGetModuleBase64, NULL);
-
- if (!result) { break; }
-
- char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
- PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
- symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
- symbol->MaxNameLen = MAX_SYM_NAME;
-
- DWORD64 displacement = 0;
-
- ostream << "[" << i << "] ";
-
- if (SymFromAddr(process, stackframe.AddrPC.Offset, &displacement, symbol)) {
- ostream << symbol->Name;
- //printf("[%i] %s\n", i, symbol->Name);
- } else {
- ostream << "???";
- //printf("[%i] ???\n", i);
- }
-
- ostream << "\n";
- }
-
- SymCleanup(process);
-#elif defined(Q_OS_MAC) || defined(Q_OS_LINUX)
- void *array[10];
- size_t size;
-
- // get void*'s for all entries on the stack
- size = backtrace(array, 10);
-
- // print out all the frames to stderr
- backtrace_symbols_fd(array, size, output.handle());
-#endif
-
- output.close();
-
- QString crash_handler_exe = QDir(qApp->applicationDirPath()).filePath(QStringLiteral("olive-crashhandler"));
- QProcess::startDetached(crash_handler_exe, {log_path});
-
- exit(1);
-}
-
-OLIVE_NAMESPACE_EXIT
diff --git a/app/common/crashpadinterface.cpp b/app/common/crashpadinterface.cpp
index 6c55ab45e..393cba4c6 100644
--- a/app/common/crashpadinterface.cpp
+++ b/app/common/crashpadinterface.cpp
@@ -23,71 +23,67 @@
#ifdef USE_CRASHPAD
#include
+#include
#include
#include
#include
+#include
+#include "crashpadutils.h"
#include "filefunctions.h"
-#ifdef Q_OS_WINDOWS
+#ifdef OS_WIN
#include
#endif
-// Copied from base::FilePath to match its macro
-#if defined(OS_POSIX)
-// On most platforms, native pathnames are char arrays, and the encoding
-// may or may not be specified. On Mac OS X, native pathnames are encoded
-// in UTF-8.
-#define TO_BASE_STRING_TYPE(x) x.toStdString()
-#elif defined(OS_WIN)
-// On Windows, for Unicode-aware applications, native pathnames are wchar_t
-// arrays encoded in UTF-16.
-#define TO_BASE_STRING_TYPE(x) x.toStdWString()
-#endif // OS_WIN
-
crashpad::CrashpadClient *client;
-bool ShowCrashConfirmation()
+QString GenerateReportPath()
{
- QString msg = QCoreApplication::translate("CrashReport",
- "We're sorry, Olive has crashed. "
- "Would you like to send an error report to "
- "help developers fix this issue?\n\n"
- "Crash reports are anonymous and only send "
- "non-specific details about your computer and"
- "how the crash occurred.");
-
- return (QMessageBox::critical(nullptr,
- QString(),
- msg,
- QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes);
+ return QDir(OLIVE_NAMESPACE::FileFunctions::GetTempFilePath()).filePath(QStringLiteral("reports"));
}
-#ifdef Q_OS_WINDOWS
+base::FilePath GenerateReportPathForCrashpad()
+{
+ return base::FilePath(QSTRING_TO_BASE_STRING(GenerateReportPath()));
+}
+
+#if defined(OS_WIN)
LONG WINAPI Win32ExceptionHandler(_EXCEPTION_POINTERS *ExceptionInfo)
{
- if (ShowCrashConfirmation()) {
- client->DumpAndCrash(ExceptionInfo);
- }
+ QString crash_handler_exe = QDir(qApp->applicationDirPath()).filePath(QStringLiteral("olive-crashhandler"));
+ QProcess::startDetached(crash_handler_exe, {GenerateReportPath(), QString::number(QDateTime::currentSecsSinceEpoch())});
+
+ client->DumpAndCrash(ExceptionInfo);
return EXCEPTION_CONTINUE_SEARCH;
}
+#elif defined(OS_LINUX)
+bool LinuxExceptionHandler(int, siginfo_t*, ucontext_t*)
+{
+ QString crash_handler_exe = QDir(qApp->applicationDirPath()).filePath(QStringLiteral("olive-crashhandler"));
+ QProcess::startDetached(crash_handler_exe, {GenerateReportPath(), QString::number(QDateTime::currentSecsSinceEpoch())});
+
+ // Returning false signals to Crashpad to proceed with its own exception handling
+ return false;
+}
#endif
bool InitializeCrashpad()
{
QString exe_dir = QCoreApplication::applicationDirPath();
+#ifdef OS_WIN
+ base::FilePath handler(QSTRING_TO_BASE_STRING(QDir(exe_dir).filePath(QStringLiteral("crashpad_handler.exe"))));
+#else
// FIXME: On Linux, probably should put this in a subdir so that it doesn't conflict with
// anything else in /usr/bin
+ base::FilePath handler(QSTRING_TO_BASE_STRING(QDir(exe_dir).filePath(QStringLiteral("crashpad_handler"))));
+#endif
- base::FilePath handler(TO_BASE_STRING_TYPE(QDir(exe_dir).filePath(QStringLiteral("crashpad_handler.exe"))));
+ base::FilePath reports_dir = GenerateReportPathForCrashpad();
- base::FilePath reports_dir(TO_BASE_STRING_TYPE(QDir(OLIVE_NAMESPACE::FileFunctions::GetTempFilePath()).filePath("reports")));
-
- base::FilePath metrics_dir(TO_BASE_STRING_TYPE(QDir(OLIVE_NAMESPACE::FileFunctions::GetTempFilePath()).filePath("metrics")));
-
- std::string url = "https://olivevideoeditor.org/crashpad/report.php";
+ base::FilePath metrics_dir(QSTRING_TO_BASE_STRING(QDir(OLIVE_NAMESPACE::FileFunctions::GetTempFilePath()).filePath(QStringLiteral("metrics"))));
// Metadata that will be posted to the server with the crash report map
std::map annotations;
@@ -101,19 +97,22 @@ bool InitializeCrashpad()
std::unique_ptr database = crashpad::CrashReportDatabase::Initialize(reports_dir);
if (database == NULL) return false;
- // Enable automated crash uploads
+ // Disable automated crash uploads
crashpad::Settings *settings = database->GetSettings();
if (settings == NULL) return false;
- settings->SetUploadsEnabled(true);
+ settings->SetUploadsEnabled(false);
// Start crash handler
client = new crashpad::CrashpadClient();
bool status = client->StartHandler(handler, reports_dir, metrics_dir,
- url, annotations, arguments, true, true);
+ "https://olivevideoeditor.org/crashpad/report.php",
+ annotations, arguments, true, true);
// Override Crashpad exception filter with our own
-#ifdef Q_OS_WINDOWS
+#if defined(OS_WIN)
SetUnhandledExceptionFilter(Win32ExceptionHandler);
+#elif defined(OS_LINUX)
+ crashpad::CrashpadClient::SetFirstChanceExceptionHandler(LinuxExceptionHandler);
#endif
return status;
diff --git a/app/common/crashpadinterface.h b/app/common/crashpadinterface.h
index 26bd17b65..2388ece96 100644
--- a/app/common/crashpadinterface.h
+++ b/app/common/crashpadinterface.h
@@ -23,7 +23,6 @@
#ifdef USE_CRASHPAD
-#include
#include
#include
diff --git a/app/common/crashpadutils.h b/app/common/crashpadutils.h
new file mode 100644
index 000000000..a2a36e49d
--- /dev/null
+++ b/app/common/crashpadutils.h
@@ -0,0 +1,40 @@
+/***
+
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2019 Olive Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+***/
+
+#ifndef CRASHPADUTILS_H
+#define CRASHPADUTILS_H
+
+#include
+
+// Copied from base::FilePath to match its macro
+#if defined(OS_POSIX)
+// On most platforms, native pathnames are char arrays, and the encoding
+// may or may not be specified. On Mac OS X, native pathnames are encoded
+// in UTF-8.
+#define QSTRING_TO_BASE_STRING(x) x.toStdString()
+#define BASE_STRING_TO_QSTRING(x) QString::fromStdString(x)
+#elif defined(OS_WIN)
+// On Windows, for Unicode-aware applications, native pathnames are wchar_t
+// arrays encoded in UTF-16.
+#define QSTRING_TO_BASE_STRING(x) x.toStdWString()
+#define BASE_STRING_TO_QSTRING(x) QString::fromStdWString(x)
+#endif // OS_WIN
+
+#endif // CRASHPADUTILS_H
diff --git a/app/core.cpp b/app/core.cpp
index 9790246ed..5ba51ee45 100644
--- a/app/core.cpp
+++ b/app/core.cpp
@@ -51,6 +51,7 @@
#include "render/pixelformat.h"
#include "render/shaderinfo.h"
#include "task/project/import/import.h"
+#include "task/project/import/importerrordialog.h"
#include "task/project/load/load.h"
#include "task/project/save/save.h"
#include "task/taskmanager.h"
@@ -473,7 +474,14 @@ void Core::AddOpenProjectFromTask(Task *task)
void Core::ImportTaskComplete(Task* task)
{
- QUndoCommand *command = static_cast(task)->GetCommand();
+ ProjectImportTask* import_task = static_cast(task);
+
+ QUndoCommand *command = import_task->GetCommand();
+
+ if (import_task->HasInvalidFiles()) {
+ ProjectImportErrorDialog d(import_task->GetInvalidFiles(), main_window_);
+ d.exec();
+ }
undo_stack_.pushIfHasChildren(command);
}
diff --git a/app/dialog/crashhandler/crashhandler.cpp b/app/dialog/crashhandler/crashhandler.cpp
index 7645eb5f9..e6120a5d9 100644
--- a/app/dialog/crashhandler/crashhandler.cpp
+++ b/app/dialog/crashhandler/crashhandler.cpp
@@ -20,66 +20,181 @@
#include "crashhandler.h"
+#include
+#include
#include
+#include
#include
-#include
+#include
+#include
+#include
+#include
#include
-#include
+#include
+#include
#include
+#include "common/crashpadutils.h"
+
OLIVE_NAMESPACE_ENTER
-CrashHandlerDialog::CrashHandlerDialog(const char *log_file)
+CrashHandlerDialog::CrashHandlerDialog(const char *report_dir, const char* crash_time)
{
setWindowTitle(tr("Olive"));
+ crash_time_ = QString(crash_time).toULongLong();
+ report_dir_ = report_dir;
QVBoxLayout* layout = new QVBoxLayout(this);
- layout->addWidget(new QLabel(tr("We're sorry, Olive has crashed. Please send the following log to the developers to "
- "help resolve this.")));
+ layout->addWidget(new QLabel(tr("We're sorry, Olive has crashed. Please help us fix it by "
+ "sending an error report.")));
- QTextEdit* edit = new QTextEdit();
- edit->setReadOnly(true);
- layout->addWidget(edit);
+ summary_edit_ = new QTextEdit();
+ summary_edit_->setPlaceholderText(tr("Describe what you were doing in as much detail as "
+ "possible. If you can, provide steps to reproduce this crash."));
- edit->append(QStringLiteral("Build Environment: %1 (%2)").arg(QSysInfo::buildCpuArchitecture(), QSysInfo::buildAbi()));
- edit->append(QStringLiteral("Run Environment: %1").arg(QSysInfo::currentCpuArchitecture()));
- edit->append(QStringLiteral("Kernel: %1 %2").arg(QSysInfo::kernelType(), QSysInfo::kernelVersion()));
- edit->append(QStringLiteral("System: %1 (%2 %3)").arg(QSysInfo::prettyProductName(), QSysInfo::productType(), QSysInfo::productVersion()));
- edit->append(QString());
+ layout->addWidget(summary_edit_);
- QDialogButtonBox* buttons = new QDialogButtonBox();
+ layout->addWidget(new QLabel(tr("Crash Report:")));
- // FIXME: Implement auto-reporting
- //buttons->addButton(tr("Send Error Report"), QDialogButtonBox::AcceptRole);
- //buttons->addButton(tr("Don't Send"), QDialogButtonBox::RejectRole);
- buttons->addButton(QDialogButtonBox::Ok);
+ crash_report_ = new QTextEdit();
+ crash_report_->setReadOnly(true);
+ crash_report_->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
+ layout->addWidget(crash_report_);
- connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
- connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
- layout->addWidget(buttons);
+ QHBoxLayout* btn_layout = new QHBoxLayout();
+ btn_layout->setMargin(0);
+ btn_layout->addStretch();
- QFile log(log_file);
- if (log.open(QFile::ReadOnly | QFile::Text)) {
- edit->append(log.readAll());
+ send_report_btn_ = new QPushButton(tr("Send Error Report"));
+ connect(send_report_btn_, &QPushButton::clicked, this, &CrashHandlerDialog::SendErrorReport);
+ btn_layout->addWidget(send_report_btn_);
- QMetaObject::invokeMethod(edit->verticalScrollBar(),
- "setValue",
- Qt::QueuedConnection,
- Q_ARG(int, 0));
+ dont_send_btn_ = new QPushButton(tr("Don't Send"));
+ connect(dont_send_btn_, &QPushButton::clicked, this, &CrashHandlerDialog::reject);
+ btn_layout->addWidget(dont_send_btn_);
- log.close();
+ layout->addLayout(btn_layout);
+
+ crash_report_->setEnabled(false);
+ send_report_btn_->setEnabled(false);
+
+ crash_report_->setText(tr("Waiting for crash report to be generated..."));
+
+ AttemptToFindReport();
+}
+
+void CrashHandlerDialog::SetGUIObjectsEnabled(bool e)
+{
+ summary_edit_->setEnabled(e);
+ crash_report_->setEnabled(e);
+ send_report_btn_->setEnabled(e);
+ dont_send_btn_->setEnabled(e);
+}
+
+void CrashHandlerDialog::GenerateReport()
+{
+ QProcess* p = new QProcess();
+
+ connect(p, QOverload::of(&QProcess::finished),
+ this, &CrashHandlerDialog::ReadProcessFinished);
+ connect(p, &QProcess::readyReadStandardOutput, this, &CrashHandlerDialog::ReadProcessHasData);
+
+ QString stackwalk_filename;
+
+#if defined(OS_WIN)
+ stackwalk_filename = QStringLiteral("minidump_stackwalk.exe");
+#else
+ stackwalk_filename = QStringLiteral("minidump_stackwalk");
+#endif
+
+ QString stackwalk_bin = QDir(qApp->applicationDirPath()).filePath(stackwalk_filename);
+ p->start(stackwalk_bin, {report_filename_});
+ crash_report_->setText(QStringLiteral("Trying to run: %1").arg(stackwalk_bin));
+}
+
+void CrashHandlerDialog::ReplyFinished(QNetworkReply* reply)
+{
+ if (reply->error() == QNetworkReply::NoError) {
+ // Close dialog
+ QDialog::accept();
+ } else {
+ QMessageBox::critical(this, tr("Upload Failed"),
+ tr("Failed to send error report. Please try again later."),
+ QMessageBox::Ok);
+ SetGUIObjectsEnabled(true);
}
}
-void CrashHandlerDialog::accept()
+void CrashHandlerDialog::AttemptToFindReport()
{
- QDialog::accept();
+ // Retrieve reports from Crashpad database
+ std::unique_ptr database = crashpad::CrashReportDatabase::Initialize(base::FilePath(QSTRING_TO_BASE_STRING(QString(report_dir_))));
+ std::vector reports;
+ database->GetCompletedReports(&reports);
+
+ // Find report that was made after the crash time
+ foreach (const crashpad::CrashReportDatabase::Report& report, reports) {
+ if (report.creation_time >= crash_time_) {
+ report_filename_ = BASE_STRING_TO_QSTRING(report.file_path.value());
+ break;
+ }
+ }
+
+ // If we found it, use it, otherwise wait a second and try again
+ if (report_filename_.isEmpty()) {
+ // Couldn't find report, try again in one second
+ QTimer::singleShot(500, this, &CrashHandlerDialog::AttemptToFindReport);
+ } else {
+ GenerateReport();
+ }
}
-void CrashHandlerDialog::reject()
+void CrashHandlerDialog::ReadProcessHasData()
{
- QDialog::reject();
+ report_data_.append(static_cast(sender())->readAllStandardOutput());
+}
+
+void CrashHandlerDialog::ReadProcessFinished()
+{
+ SetGUIObjectsEnabled(true);
+ crash_report_->setText(report_data_);
+ delete sender();
+}
+
+void CrashHandlerDialog::SendErrorReport()
+{
+ SetGUIObjectsEnabled(false);
+
+ QNetworkAccessManager* manager = new QNetworkAccessManager();
+ connect(manager, &QNetworkAccessManager::finished, this, &CrashHandlerDialog::ReplyFinished);
+
+ QNetworkRequest request;
+ request.setSslConfiguration(QSslConfiguration::defaultConfiguration());
+ request.setUrl(QStringLiteral("https://olivevideoeditor.org/crashpad/report.php"));
+
+ // Create HTTP form
+ QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
+
+ // Create description section
+ QHttpPart desc_part;
+ desc_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("text/plain"));
+ desc_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"description\""));
+ desc_part.setBody(summary_edit_->toPlainText().toUtf8());
+ multipart->append(desc_part);
+
+ // Create file section
+ QHttpPart file_part;
+ file_part.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/octet-stream"));
+ file_part.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"upload_file_minidump\"; filename=\"%1\"")
+ .arg(QFileInfo(report_filename_).fileName()));
+ QFile* file = new QFile(report_filename_);
+ file->open(QFile::ReadOnly);
+ file_part.setBodyDevice(file);
+ file->setParent(multipart); // Delete file with multipart
+ multipart->append(file_part);
+
+ manager->post(request, multipart);
}
OLIVE_NAMESPACE_EXIT
diff --git a/app/dialog/crashhandler/crashhandler.h b/app/dialog/crashhandler/crashhandler.h
index 485f1d543..7ec91669b 100644
--- a/app/dialog/crashhandler/crashhandler.h
+++ b/app/dialog/crashhandler/crashhandler.h
@@ -21,7 +21,12 @@
#ifndef CRASHHANDLERDIALOG_H
#define CRASHHANDLERDIALOG_H
+#include
#include
+#include
+#include
+#include
+#include
#include "common/define.h"
@@ -31,12 +36,39 @@ class CrashHandlerDialog : public QDialog
{
Q_OBJECT
public:
- CrashHandlerDialog(const char* log_file);
+ CrashHandlerDialog(const char* report_dir, const char* crash_time);
-public slots:
- virtual void accept() override;
+private:
+ void SetGUIObjectsEnabled(bool e);
- virtual void reject() override;
+ void GenerateReport();
+
+ QTextEdit* summary_edit_;
+
+ QTextEdit* crash_report_;
+
+ QPushButton* send_report_btn_;
+
+ QPushButton* dont_send_btn_;
+
+ QString report_filename_;
+
+ time_t crash_time_;
+
+ QString report_dir_;
+
+ QByteArray report_data_;
+
+private slots:
+ void ReplyFinished(QNetworkReply *reply);
+
+ void AttemptToFindReport();
+
+ void ReadProcessHasData();
+
+ void ReadProcessFinished();
+
+ void SendErrorReport();
};
diff --git a/app/dialog/crashhandler/crashhandlermain.cpp b/app/dialog/crashhandler/crashhandlermain.cpp
index 88032fff4..d0fe76368 100644
--- a/app/dialog/crashhandler/crashhandlermain.cpp
+++ b/app/dialog/crashhandler/crashhandlermain.cpp
@@ -24,13 +24,13 @@
int main(int argc, char *argv[])
{
- if (argc < 2) {
+ if (argc < 3) {
return 1;
}
QApplication a(argc, argv);
- OLIVE_NAMESPACE::CrashHandlerDialog chd(argv[1]);
+ OLIVE_NAMESPACE::CrashHandlerDialog chd(argv[1], argv[2]);
chd.open();
return a.exec();
diff --git a/app/node/block/block.cpp b/app/node/block/block.cpp
index 86daeca9d..85df9aa3b 100644
--- a/app/node/block/block.cpp
+++ b/app/node/block/block.cpp
@@ -51,6 +51,7 @@ Block::Block() :
speed_input_ = new NodeInput("speed_in", NodeParam::kFloat);
speed_input_->set_standard_value(1.0);
+ speed_input_->set_property(QStringLiteral("view"), QStringLiteral("percent"));
AddInput(speed_input_);
// A block's length must be greater than 0
diff --git a/app/node/math/math/mathbase.cpp b/app/node/math/math/mathbase.cpp
index 4a793af93..731a750da 100644
--- a/app/node/math/math/mathbase.cpp
+++ b/app/node/math/math/mathbase.cpp
@@ -248,8 +248,8 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
SampleBufferPtr mixed_samples = SampleBuffer::CreateAllocated(samples_a->audio_params(), max_samples);
- // Mix samples that are in both buffers
for (int i=0;iaudio_params().channel_count();i++) {
+ // Mix samples that are in both buffers
for (int j=0;jdata()[i][j] = PerformAll(operation, samples_a->data()[i][j], samples_b->data()[i][j]);
}
@@ -259,9 +259,11 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
// Fill in remainder space with 0s
int remainder = max_samples - min_samples;
+ SampleBufferPtr larger_buffer = (max_samples == samples_a->sample_count()) ? samples_a : samples_b;
+
for (int i=0;iaudio_params().channel_count();i++) {
- memset(mixed_samples->data()[i] + min_samples * sizeof(float),
- 0,
+ memcpy(&mixed_samples->data()[i][min_samples],
+ &larger_buffer->data()[i][min_samples],
remainder * sizeof(float));
}
}
diff --git a/app/project/item/sequence/sequence.cpp b/app/project/item/sequence/sequence.cpp
index fcf76c8dd..ede8f6f88 100644
--- a/app/project/item/sequence/sequence.cpp
+++ b/app/project/item/sequence/sequence.cpp
@@ -268,6 +268,10 @@ void Sequence::set_parameters_from_footage(const QList footage)
foreach (Footage* f, footage) {
foreach (StreamPtr s, f->streams()) {
+ if (!s->enabled()) {
+ continue;
+ }
+
switch (s->type()) {
case Stream::kVideo:
{
diff --git a/app/render/backend/renderbackend.cpp b/app/render/backend/renderbackend.cpp
index 9c72f47a9..8e5c0110b 100644
--- a/app/render/backend/renderbackend.cpp
+++ b/app/render/backend/renderbackend.cpp
@@ -375,13 +375,6 @@ void RenderBackend::RunNextJob()
return;
}
- // Check if params are valid
- if (!video_params_.is_valid()
- || !audio_params_.is_valid()) {
- qDebug() << "Failed to run job, parameters are invalid";
- return;
- }
-
// If we have a value update queued, check if all workers are available and proceed from there
if (autocache_enabled_ && !graph_update_queue_.isEmpty()) {
bool all_workers_available = true;
@@ -449,6 +442,8 @@ void RenderBackend::RunNextJob()
switch (ticket->GetType()) {
case RenderTicket::kTypeHash:
+ Q_ASSERT(video_params_.is_valid());
+
QtConcurrent::run(&thread_pool_,
worker,
&RenderWorker::Hash,
@@ -458,6 +453,8 @@ void RenderBackend::RunNextJob()
break;
case RenderTicket::kTypeVideo:
{
+ Q_ASSERT(video_params_.is_valid());
+
rational frame = ticket->GetTime().value();
QtConcurrent::run(&thread_pool_,
@@ -474,6 +471,8 @@ void RenderBackend::RunNextJob()
break;
}
case RenderTicket::kTypeAudio:
+ Q_ASSERT(audio_params_.is_valid());
+
QtConcurrent::run(&thread_pool_,
worker,
&RenderWorker::RenderAudio,
diff --git a/app/render/framehashcache.cpp b/app/render/framehashcache.cpp
index b65234dc5..387baa12e 100644
--- a/app/render/framehashcache.cpp
+++ b/app/render/framehashcache.cpp
@@ -358,12 +358,17 @@ void FrameHashCache::HashDeleted(const QString& s, const QByteArray &hash)
return;
}
+ TimeRangeList ranges_to_invalidate;
QMap::const_iterator i;
for (i=time_hash_map_.constBegin(); i!=time_hash_map_.constEnd(); i++) {
if (i.value() == hash) {
- Invalidate(TimeRange(i.key(), i.key() + timebase_));
+ ranges_to_invalidate.InsertTimeRange(TimeRange(i.key(), i.key() + timebase_));
}
}
+
+ foreach (const TimeRange& range, ranges_to_invalidate) {
+ Invalidate(range);
+ }
}
void FrameHashCache::ProjectInvalidated(Project *p)
diff --git a/app/task/project/import/CMakeLists.txt b/app/task/project/import/CMakeLists.txt
index 77b4f31a6..4a25a6b03 100644
--- a/app/task/project/import/CMakeLists.txt
+++ b/app/task/project/import/CMakeLists.txt
@@ -18,5 +18,7 @@ set(OLIVE_SOURCES
${OLIVE_SOURCES}
task/project/import/import.h
task/project/import/import.cpp
+ task/project/import/importerrordialog.h
+ task/project/import/importerrordialog.cpp
PARENT_SCOPE
)
diff --git a/app/task/project/import/import.cpp b/app/task/project/import/import.cpp
index 270e3a7d4..c8cd0a6c9 100644
--- a/app/task/project/import/import.cpp
+++ b/app/task/project/import/import.cpp
@@ -122,7 +122,10 @@ void ProjectImportTask::Import(Folder *folder, const QFileInfoList &import, int
f->set_project(nullptr);
- if (f->status() != Footage::kInvalid) {
+ if (f->status() == Footage::kInvalid) {
+ // Add to list so we can tell the user about it later
+ invalid_files_.append(file_info.absoluteFilePath());
+ } else {
// Create undoable command that adds the items to the model
new ProjectViewModel::AddItemCommand(model_,
folder,
@@ -132,7 +135,7 @@ void ProjectImportTask::Import(Folder *folder, const QFileInfoList &import, int
counter++;
- emit ProgressChanged((counter * 100) / file_count_);
+ emit ProgressChanged(static_cast(counter) / static_cast(file_count_));
}
}
diff --git a/app/task/project/import/import.h b/app/task/project/import/import.h
index d092fa192..2257fc8ea 100644
--- a/app/task/project/import/import.h
+++ b/app/task/project/import/import.h
@@ -42,6 +42,16 @@ public:
return command_;
}
+ const QStringList& GetInvalidFiles() const
+ {
+ return invalid_files_;
+ }
+
+ bool HasInvalidFiles() const
+ {
+ return !invalid_files_.isEmpty();
+ }
+
protected:
virtual bool Run() override;
@@ -58,6 +68,8 @@ private:
int file_count_;
+ QStringList invalid_files_;
+
};
OLIVE_NAMESPACE_EXIT
diff --git a/app/task/project/import/importerrordialog.cpp b/app/task/project/import/importerrordialog.cpp
new file mode 100644
index 000000000..c6b5e4d8c
--- /dev/null
+++ b/app/task/project/import/importerrordialog.cpp
@@ -0,0 +1,53 @@
+/***
+
+ Olive - Non-Linear Video Editor
+ Copyright (C) 2019 Olive Team
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+***/
+
+#include "importerrordialog.h"
+
+#include
+#include
+#include
+#include
+
+OLIVE_NAMESPACE_ENTER
+
+ProjectImportErrorDialog::ProjectImportErrorDialog(const QStringList& filenames, QWidget* parent) :
+ QDialog(parent)
+{
+ QVBoxLayout* layout = new QVBoxLayout(this);
+
+ setWindowTitle(tr("Import Error"));
+
+ layout->addWidget(new QLabel(tr("The following files failed to import. Olive likely does not "
+ "support their formats.")));
+
+ QListWidget* list_widget = new QListWidget();
+ foreach (const QString& s, filenames) {
+ list_widget->addItem(s);
+ }
+ layout->addWidget(list_widget);
+
+ QDialogButtonBox* buttons = new QDialogButtonBox();
+ buttons->setStandardButtons(QDialogButtonBox::Ok);
+ buttons->setCenterButtons(true);
+ connect(buttons, &QDialogButtonBox::accepted, this, &ProjectImportErrorDialog::accept);
+ layout->addWidget(buttons);
+}
+
+OLIVE_NAMESPACE_EXIT
diff --git a/app/common/crashhandler.h b/app/task/project/import/importerrordialog.h
similarity index 73%
rename from app/common/crashhandler.h
rename to app/task/project/import/importerrordialog.h
index 10ed30627..8ee6bf740 100644
--- a/app/common/crashhandler.h
+++ b/app/task/project/import/importerrordialog.h
@@ -18,15 +18,23 @@
***/
-#ifndef CRASHHANDLER_H
-#define CRASHHANDLER_H
+#ifndef PROJECTIMPORTERRORDIALOG_H
+#define PROJECTIMPORTERRORDIALOG_H
+
+#include
#include "common/define.h"
OLIVE_NAMESPACE_ENTER
-void crash_handler(int sig);
+class ProjectImportErrorDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ ProjectImportErrorDialog(const QStringList& filenames, QWidget* parent = nullptr);
+
+};
OLIVE_NAMESPACE_EXIT
-#endif // CRASHHANDLER_H
+#endif // PROJECTIMPORTERRORDIALOG_H
diff --git a/app/widget/nodeparamview/nodeparamview.cpp b/app/widget/nodeparamview/nodeparamview.cpp
index 51b332fe6..34fea246d 100644
--- a/app/widget/nodeparamview/nodeparamview.cpp
+++ b/app/widget/nodeparamview/nodeparamview.cpp
@@ -136,6 +136,9 @@ void NodeParamView::SelectNodes(const QList &nodes)
connect(item, &NodeParamViewItem::InputDoubleClicked, this, &NodeParamView::InputDoubleClicked);
connect(item, &NodeParamViewItem::RequestSelectNode, this, &NodeParamView::RequestSelectNode);
+ // Set time target
+ item->SetTimeTarget(GetTimeTarget());
+
items_.insert(n, item);
}
diff --git a/app/widget/slider/sliderlabel.cpp b/app/widget/slider/sliderlabel.cpp
index d100379a5..ffc07fb2c 100644
--- a/app/widget/slider/sliderlabel.cpp
+++ b/app/widget/slider/sliderlabel.cpp
@@ -51,10 +51,12 @@ SliderLabel::SliderLabel(QWidget *parent) :
void SliderLabel::mousePressEvent(QMouseEvent *e)
{
- if (e->modifiers() & Qt::AltModifier) {
- emit RequestReset();
- } else {
- emit LabelPressed();
+ if (e->button() == Qt::LeftButton) {
+ if (e->modifiers() & Qt::AltModifier) {
+ emit RequestReset();
+ } else {
+ emit LabelPressed();
+ }
}
}
diff --git a/app/widget/slider/sliderladder.cpp b/app/widget/slider/sliderladder.cpp
index 5a9c4d235..c5c0054ae 100644
--- a/app/widget/slider/sliderladder.cpp
+++ b/app/widget/slider/sliderladder.cpp
@@ -103,9 +103,7 @@ void SliderLadder::mouseReleaseEvent(QMouseEvent *event)
{
Q_UNUSED(event)
- drag_timer_.stop();
-
- emit Released();
+ this->close();
}
void SliderLadder::showEvent(QShowEvent *event)
@@ -115,6 +113,15 @@ void SliderLadder::showEvent(QShowEvent *event)
drag_timer_.start();
}
+void SliderLadder::closeEvent(QCloseEvent *event)
+{
+ Q_UNUSED(event)
+
+ drag_timer_.stop();
+
+ emit Released();
+}
+
void SliderLadder::TimerUpdate()
{
int32_t x_mvmt, y_mvmt;
diff --git a/app/widget/slider/sliderladder.h b/app/widget/slider/sliderladder.h
index ce0ae6193..9fc30e511 100644
--- a/app/widget/slider/sliderladder.h
+++ b/app/widget/slider/sliderladder.h
@@ -75,6 +75,8 @@ protected:
virtual void showEvent(QShowEvent *event) override;
+ virtual void closeEvent(QCloseEvent* event) override;
+
signals:
void DraggedByValue(int value, double multiplier);
diff --git a/app/widget/timelinewidget/view/timelineview.cpp b/app/widget/timelinewidget/view/timelineview.cpp
index 4b60ec0b7..99fd892a0 100644
--- a/app/widget/timelinewidget/view/timelineview.cpp
+++ b/app/widget/timelinewidget/view/timelineview.cpp
@@ -46,6 +46,7 @@ TimelineView::TimelineView(Qt::Alignment vertical_alignment, QWidget *parent) :
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
setBackgroundRole(QPalette::Window);
setContextMenuPolicy(Qt::CustomContextMenu);
+ setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
viewport()->setMouseTracking(true);
}
diff --git a/app/widget/viewer/footageviewer.cpp b/app/widget/viewer/footageviewer.cpp
index c4df185ce..5ca81ae1e 100644
--- a/app/widget/viewer/footageviewer.cpp
+++ b/app/widget/viewer/footageviewer.cpp
@@ -33,8 +33,10 @@ FootageViewerWidget::FootageViewerWidget(QWidget *parent) :
footage_(nullptr)
{
video_node_ = new VideoInput();
+ sequence_.AddNode(video_node_);
+
audio_node_ = new AudioInput();
- viewer_node_ = new ViewerOutput();
+ sequence_.AddNode(audio_node_);
connect(display_widget(), &ViewerDisplayWidget::DragStarted, this, &FootageViewerWidget::StartFootageDrag);
@@ -55,17 +57,29 @@ void FootageViewerWidget::SetFootage(Footage *footage)
ConnectViewerNode(nullptr);
- NodeParam::DisconnectEdge(video_node_->output(), viewer_node_->texture_input());
- NodeParam::DisconnectEdge(audio_node_->output(), viewer_node_->samples_input());
+ NodeParam::DisconnectEdge(video_node_->output(), sequence_.viewer_output()->texture_input());
+ NodeParam::DisconnectEdge(audio_node_->output(), sequence_.viewer_output()->samples_input());
}
footage_ = footage;
if (footage_) {
+ // Update sequence media name
+ sequence_.viewer_output()->set_media_name(footage_->name());
+
+ // Reset parameters and then attempt to set from footage
+ sequence_.set_default_parameters();
+ sequence_.set_parameters_from_footage({footage_});
+
+ // Use first of each stream
VideoStreamPtr video_stream = nullptr;
AudioStreamPtr audio_stream = nullptr;
foreach (StreamPtr s, footage_->streams()) {
+ if (!s->enabled()) {
+ continue;
+ }
+
if (!audio_stream && s->type() == Stream::kAudio) {
audio_stream = std::static_pointer_cast(s);
}
@@ -80,42 +94,17 @@ void FootageViewerWidget::SetFootage(Footage *footage)
}
}
- viewer_node_->set_media_name(footage_->name());
-
if (video_stream) {
video_node_->SetFootage(video_stream);
- viewer_node_->set_video_params(VideoParams(video_stream->width(),
- video_stream->height(),
- video_stream->frame_rate().flipped(),
- static_cast(Config::Current()["DefaultSequencePreviewFormat"].toInt()),
- video_stream->pixel_aspect_ratio(),
- video_stream->interlacing(),
- VideoParams::generate_auto_divider(video_stream->width(), video_stream->height())));
- NodeParam::ConnectEdge(video_node_->output(), viewer_node_->texture_input());
- } else {
- int width = Config::Current()["DefaultSequenceWidth"].toInt();
- int height = Config::Current()["DefaultSequenceHeight"].toInt();
-
- viewer_node_->set_video_params(VideoParams(width,
- height,
- Config::Current()["DefaultSequenceFrameRate"].value(),
- static_cast(Config::Current()["DefaultSequencePreviewFormat"].toInt()),
- Config::Current()["DefaultSequencePixelAspect"].value(),
- Config::Current()["DefaultSequenceInterlacing"].value(),
- VideoParams::generate_auto_divider(width, height)));
+ NodeParam::ConnectEdge(video_node_->output(), sequence_.viewer_output()->texture_input());
}
if (audio_stream) {
audio_node_->SetFootage(audio_stream);
- viewer_node_->set_audio_params(AudioParams(audio_stream->sample_rate(), audio_stream->channel_layout(), SampleFormat::kInternalFormat));
- NodeParam::ConnectEdge(audio_node_->output(), viewer_node_->samples_input());
- } else {
- viewer_node_->set_audio_params(AudioParams(Config::Current()["DefaultSequenceAudioFrequency"].toInt(),
- Config::Current()["DefaultSequenceAudioLayout"].toULongLong(),
- SampleFormat::kInternalFormat));
+ NodeParam::ConnectEdge(audio_node_->output(), sequence_.viewer_output()->samples_input());
}
- ConnectViewerNode(viewer_node_, footage_->project()->color_manager());
+ ConnectViewerNode(sequence_.viewer_output(), footage_->project()->color_manager());
SetTimestamp(cached_timestamps_.value(footage_, 0));
}
diff --git a/app/widget/viewer/footageviewer.h b/app/widget/viewer/footageviewer.h
index 36f688fa9..af093eb8f 100644
--- a/app/widget/viewer/footageviewer.h
+++ b/app/widget/viewer/footageviewer.h
@@ -47,12 +47,12 @@ private:
Footage* footage_;
+ Sequence sequence_;
+
VideoInput* video_node_;
AudioInput* audio_node_;
- ViewerOutput* viewer_node_;
-
QHash cached_timestamps_;
private slots:
diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp
index 4af7869b6..b3b52231a 100644
--- a/app/widget/viewer/viewer.cpp
+++ b/app/widget/viewer/viewer.cpp
@@ -566,7 +566,9 @@ QString ViewerWidget::GetCachedFilenameFromTime(const rational &time)
bool ViewerWidget::FrameExistsAtTime(const rational &time)
{
- return GetConnectedNode() && time >= 0 && time < GetConnectedNode()->video_frame_cache()->GetLength();
+ return GetConnectedNode()
+ && ((time >= 0 && time < GetConnectedNode()->video_frame_cache()->GetLength())
+ || GetConnectedNode()->video_frame_cache()->GetLength().isNull());
}
void ViewerWidget::SetDisplayImage(FramePtr frame, bool main_only)
diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp
index 4c010e963..cbb4b48da 100644
--- a/app/window/mainwindow/mainwindow.cpp
+++ b/app/window/mainwindow/mainwindow.cpp
@@ -349,6 +349,22 @@ void MainWindow::ProjectClose(Project *p)
CloseSequence(seq);
}
}
+
+ // Close any open footage in footage viewer
+ QList footage = p->get_items_of_type(Item::kFootage);
+ QList footage_in_viewer = footage_viewer_panel_->GetSelectedFootage();
+
+ if (!footage_in_viewer.isEmpty()) {
+ // FootageViewer only has the one footage item
+ Footage* f = footage_in_viewer.first();
+
+ foreach (ItemPtr i, footage) {
+ if (f == i.get()) {
+ footage_viewer_panel_->SetFootage(nullptr);
+ break;
+ }
+ }
+ }
}
void MainWindow::SetApplicationProgressStatus(ProgressStatus status)
diff --git a/cmake/FindGoogleCrashpad.cmake b/cmake/FindGoogleCrashpad.cmake
index 60ef6b849..0e56c35ec 100644
--- a/cmake/FindGoogleCrashpad.cmake
+++ b/cmake/FindGoogleCrashpad.cmake
@@ -18,69 +18,91 @@ include(FindPackageHandleStandardArgs)
# Try to find include files
find_path(CRASHPAD_CLIENT_INCLUDE_DIR
- client/crashpad_client.h
- HINTS
- "${CRASHPAD_LOCATION}"
- "$ENV{CRASHPAD_LOCATION}"
- "${CRASHPAD_BASE_DIR}"
+ client/crashpad_client.h
+ HINTS
+ "${CRASHPAD_LOCATION}"
+ "$ENV{CRASHPAD_LOCATION}"
+ "${CRASHPAD_BASE_DIR}"
)
list(APPEND CRASHPAD_INCLUDE_DIRS ${CRASHPAD_CLIENT_INCLUDE_DIR})
find_path(CRASHPAD_BASE_INCLUDE_DIR
- base/files/file_path.h
- HINTS
- "${CRASHPAD_LOCATION}"
- "$ENV{CRASHPAD_LOCATION}"
- "${CRASHPAD_BASE_DIR}"
- PATH_SUFFIXES
- "third_party/mini_chromium/mini_chromium"
+ base/files/file_path.h
+ HINTS
+ "${CRASHPAD_LOCATION}"
+ "$ENV{CRASHPAD_LOCATION}"
+ "${CRASHPAD_BASE_DIR}"
+ PATH_SUFFIXES
+ "third_party/mini_chromium/mini_chromium"
)
list(APPEND CRASHPAD_INCLUDE_DIRS ${CRASHPAD_BASE_INCLUDE_DIR})
# Try to find build files
if (WIN32)
find_path(CRASHPAD_LIBRARY_DIRS
- obj/client/client.lib
- HINTS
- "${CRASHPAD_LOCATION}"
- "$ENV{CRASHPAD_LOCATION}"
- "${CRASHPAD_BASE_DIR}"
- PATH_SUFFIXES
- "out/Default"
+ obj/client/client.lib
+ HINTS
+ "${CRASHPAD_LOCATION}"
+ "$ENV{CRASHPAD_LOCATION}"
+ "${CRASHPAD_BASE_DIR}"
+ PATH_SUFFIXES
+ "out/Default"
)
elseif(UNIX)
# Assuming macOS works this way, don't actually know
find_path(CRASHPAD_LIBRARY_DIRS
- obj/client/libclient.a
- HINTS
- "${CRASHPAD_LOCATION}"
- "$ENV{CRASHPAD_LOCATION}"
- "${CRASHPAD_BASE_DIR}"
- PATH_SUFFIXES
- "out/Default"
+ obj/client/libclient.a
+ HINTS
+ "${CRASHPAD_LOCATION}"
+ "$ENV{CRASHPAD_LOCATION}"
+ "${CRASHPAD_BASE_DIR}"
+ PATH_SUFFIXES
+ "out/Default"
)
endif()
# Find the libraries we need
set (_crashpad_components
- client/client
- util/util
- third_party/mini_chromium/mini_chromium/base/base)
+ client
+ util
+ third_party/mini_chromium/mini_chromium/base
+ compat)
foreach (COMPONENT ${_crashpad_components})
- string(REGEX MATCH "^(.*[\\\/])" SUBDIR ${COMPONENT})
- string(REGEX MATCH "([^\/]+$)" SHORT_COMPONENT ${COMPONENT})
- string(TOUPPER ${SHORT_COMPONENT} UPPERCOMPONENT)
+ get_filename_component(SHORT_COMPONENT ${COMPONENT} NAME)
+ string(TOUPPER ${SHORT_COMPONENT} UPPER_COMPONENT)
- find_library(CRASHPAD_${UPPERCOMPONENT}_LIB
- ${SHORT_COMPONENT}
- HINTS "${CRASHPAD_LIBRARY_DIRS}/obj/${SUBDIR}"
+ find_library(CRASHPAD_${UPPER_COMPONENT}_LIB
+ ${SHORT_COMPONENT}
+ HINTS
+ "${CRASHPAD_LIBRARY_DIRS}/obj/${COMPONENT}"
)
- list(APPEND CRASHPAD_LIBRARIES ${CRASHPAD_${UPPERCOMPONENT}_LIB})
+ list(APPEND CRASHPAD_LIBRARIES ${CRASHPAD_${UPPER_COMPONENT}_LIB})
endforeach()
+if (UNIX AND NOT APPLE)
+ list(APPEND CRASHPAD_LIBRARIES
+ ${CMAKE_DL_LIBS} # Crashpad compat lib needs libdl.so (-ldl)
+ Threads::Threads # Link against libpthread.so (-lpthread)
+ )
+endif()
+
+# Find Breakpad's minidump_stackwalk
+if (UNIX)
+ find_path(BREAKPAD_BIN_DIR
+ minidump_stackwalk
+ HINTS
+ "${BREAKPAD_LOCATION}"
+ "$ENV{BREAKPAD_LOCATION}"
+ "${BREAKPAD_BASE_DIR}"
+ PATH_SUFFIXES
+ breakpad/bin
+ )
+endif()
+
find_package_handle_standard_args(GoogleCrashpad
- REQUIRED_VARS
- CRASHPAD_LIBRARIES
- CRASHPAD_INCLUDE_DIRS
+ REQUIRED_VARS
+ CRASHPAD_LIBRARIES
+ CRASHPAD_INCLUDE_DIRS
+ BREAKPAD_BIN_DIR
)
diff --git a/docker/ci-common/Dockerfile b/docker/ci-common/Dockerfile
new file mode 100644
index 000000000..ce97cc8c6
--- /dev/null
+++ b/docker/ci-common/Dockerfile
@@ -0,0 +1,71 @@
+# Copyright (C) 2020 Olive Team
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# Build image (default):
+# docker build -t olivevideoeditor/ci-common:2 -f ci-common/Dockerfile .
+
+ARG OLIVE_ORG=olivevideoeditor
+ARG ASWF_PKG_ORG=aswftesting
+ARG CI_COMMON_VERSION=2
+ARG DTS_VERSION=9
+ARG CMAKE_VERSION=3.17.3
+
+FROM ${ASWF_PKG_ORG}/ci-package-clang:${CI_COMMON_VERSION} as ci-package-clang
+FROM ${ASWF_PKG_ORG}/ci-package-ninja:${CI_COMMON_VERSION} as ci-package-ninja
+
+FROM centos:7 as ci-common
+
+ARG OLIVE_ORG
+ARG CI_COMMON_VERSION
+ARG DTS_VERSION
+ARG CMAKE_VERSION
+
+LABEL maintainer="olivevideoeditor@gmail.com"
+
+LABEL org.opencontainers.image.name="$OLIVE_ORG/ci-common"
+LABEL org.opencontainers.image.description="CentOS CI Shared Image"
+LABEL org.opencontainers.image.url="http://olivevideoeditor.org"
+LABEL org.opencontainers.image.source="https://github.com/olive-editor/olive"
+LABEL org.opencontainers.image.vendor="Olive Team"
+LABEL org.opencontainers.image.version="1.0"
+
+USER root
+
+COPY scripts/${CI_COMMON_VERSION}/versions_common.sh \
+ scripts/common/install_yumpackages.sh \
+ scripts/common/before_build.sh \
+ scripts/common/copy_new_files.sh \
+ /tmp/
+
+RUN source /tmp/versions_common.sh && \
+ /tmp/install_yumpackages.sh
+
+RUN mkdir /opt/olive
+WORKDIR /opt/olive
+
+ENV OLIVE_ORG=${OLIVE_ORG} \
+ CI_COMMON_VERSION=${CI_COMMON_VERSION} \
+ DTS_VERSION=${DTS_VERSION} \
+ CMAKE_VERSION=${CMAKE_VERSION} \
+ LD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib64:/opt/rh/httpd24/root/usr/lib64:/opt/rh/devtoolset-${DTS_VERSION}/root/usr/lib64:/opt/rh/devtoolset-${DTS_VERSION}/root/usr/lib:${LD_LIBRARY_PATH} \
+ PATH=/opt/rh/rh-git218/root/usr/bin:/usr/local/bin:/opt/rh/devtoolset-${DTS_VERSION}/root/usr/bin:/opt/app-root/src/bin:/opt/rh/devtoolset-${DTS_VERSION}/root/usr/bin/:/usr/local/sbin:/usr/sbin:/usr/bin:/sbin:/bin
+
+#COPY scripts/common/install_sonar.sh \
+# scripts/common/install_ccache.sh \
+# /tmp/
+
+COPY --from=ci-package-clang /. /usr/local/
+COPY --from=ci-package-ninja /. /usr/local/
+
+#COPY scripts/common/setup_aswfuser.sh /tmp
+#RUN /tmp/setup_aswfuser.sh
+
+COPY scripts/base/install_cmake.sh \
+ /tmp/
+
+RUN export DOWNLOADS_DIR=/tmp/downloads && \
+ mkdir /tmp/downloads && \
+# source /tmp/versions_base.sh && \
+ /tmp/install_cmake.sh && \
+# /tmp/patchup.sh && \
+ rm -rf /tmp/downloads
diff --git a/docker/ci-crashpad/Dockerfile b/docker/ci-crashpad/Dockerfile
new file mode 100644
index 000000000..ad7edbe24
--- /dev/null
+++ b/docker/ci-crashpad/Dockerfile
@@ -0,0 +1,37 @@
+# Copyright (C) 2020 Olive Team
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# Build image (default):
+# docker build -t olivevideoeditor/ci-package-crashpad -f ci-crashpad/Dockerfile .
+
+ARG OLIVE_ORG=olivevideoeditor
+ARG CI_COMMON_VERSION=2
+
+FROM ${OLIVE_ORG}/ci-common:${CI_COMMON_VERSION} as ci-crashpad
+
+ARG OLIVE_ORG
+ARG CI_COMMON_VERSION
+
+LABEL maintainer="olivevideoeditor@gmail.com"
+
+LABEL org.opencontainers.image.name="$OLIVE_ORG/ci-crashpad"
+LABEL org.opencontainers.image.description="CentOS Crashpad Build Image"
+LABEL org.opencontainers.image.url="http://olivevideoeditor.org"
+LABEL org.opencontainers.image.source="https://github.com/olive-editor/olive"
+LABEL org.opencontainers.image.vendor="Olive Team"
+LABEL org.opencontainers.image.version="1.0"
+
+COPY scripts/build_crashpad.sh \
+ /tmp/
+
+ENV OLIVE_ORG=${OLIVE_ORG} \
+ CI_COMMON_VERSION=${CI_COMMON_VERSION} \
+ OLIVE_INSTALL_PREFIX=/usr/local
+
+RUN /tmp/before_build.sh && \
+ /tmp/build_crashpad.sh && \
+ /tmp/copy_new_files.sh
+
+FROM scratch as ci-package-crashpad
+
+COPY --from=ci-crashpad /package /
diff --git a/docker/ci-ffmpeg/Dockerfile b/docker/ci-ffmpeg/Dockerfile
new file mode 100644
index 000000000..6333ba164
--- /dev/null
+++ b/docker/ci-ffmpeg/Dockerfile
@@ -0,0 +1,57 @@
+# Copyright (C) 2020 Olive Team
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# Build image (default):
+# docker build -t olivevideoeditor/ci-package-ffmpeg:4.2.4 -f ci-ffmpeg/Dockerfile .
+
+ARG OLIVE_ORG=olivevideoeditor
+ARG CI_COMMON_VERSION=2
+ARG FFMPEG_VERSION=4.2.4
+ARG NASM_VERSION=2.15.03
+ARG YASM_VERSION=1.3.0
+ARG LAME_VERSION=3.100
+ARG OPUS_VERSION=1.3.1
+# TODO: Make version of x264, x265 and libvpx selectable?
+ARG NUM_JOBS=2
+
+FROM ${OLIVE_ORG}/ci-common:${CI_COMMON_VERSION} as ci-ffmpeg
+
+ARG OLIVE_ORG
+ARG CI_COMMON_VERSION
+ARG FFMPEG_VERSION
+ARG NASM_VERSION
+ARG YASM_VERSION
+ARG LAME_VERSION
+ARG OPUS_VERSION
+# TODO: Make version of x264, x265 and libvpx selectable?
+ARG NUM_JOBS=2
+
+LABEL maintainer="olivevideoeditor@gmail.com"
+
+LABEL org.opencontainers.image.name="$OLIVE_ORG/ci-ffmpeg"
+LABEL org.opencontainers.image.description="CentOS FFmpeg Build Image"
+LABEL org.opencontainers.image.url="http://olivevideoeditor.org"
+LABEL org.opencontainers.image.source="https://github.com/olive-editor/olive"
+LABEL org.opencontainers.image.vendor="Olive Team"
+LABEL org.opencontainers.image.version="1.0"
+
+COPY scripts/build_ffmpeg.sh \
+ /tmp/
+
+ENV OLIVE_ORG=${OLIVE_ORG} \
+ CI_COMMON_VERSION=${CI_COMMON_VERSION} \
+ FFMPEG_VERSION=${FFMPEG_VERSION} \
+ NASM_VERSION=${NASM_VERSION} \
+ YASM_VERSION=${YASM_VERSION} \
+ LAME_VERSION=${LAME_VERSION} \
+ OPUS_VERSION=${OPUS_VERSION} \
+ NUM_JOBS=${NUM_JOBS} \
+ OLIVE_INSTALL_PREFIX=/usr/local
+
+RUN /tmp/before_build.sh && \
+ /tmp/build_ffmpeg.sh && \
+ /tmp/copy_new_files.sh
+
+FROM scratch as ci-package-ffmpeg
+
+COPY --from=ci-ffmpeg /package/. /
diff --git a/docker/ci-olive/Dockerfile b/docker/ci-olive/Dockerfile
new file mode 100644
index 000000000..386aa98f8
--- /dev/null
+++ b/docker/ci-olive/Dockerfile
@@ -0,0 +1,52 @@
+# Copyright (C) 2020 Olive Team
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# Build image (default):
+# docker build -t olivevideoeditor/ci-olive:2021.2 -f ci-olive/Dockerfile .
+
+ARG OLIVE_ORG=olivevideoeditor
+ARG ASWF_PKG_ORG=aswftesting
+ARG CI_COMMON_VERSION=2
+ARG VFXPLATFORM_VERSION=2021
+ARG FFMPEG_VERSION=4.2.4
+
+FROM ${ASWF_PKG_ORG}/ci-package-qt:${VFXPLATFORM_VERSION} as ci-package-qt
+FROM ${ASWF_PKG_ORG}/ci-package-python:${VFXPLATFORM_VERSION} as ci-package-python
+FROM ${ASWF_PKG_ORG}/ci-package-boost:${VFXPLATFORM_VERSION} as ci-package-boost
+FROM ${ASWF_PKG_ORG}/ci-package-openexr:${VFXPLATFORM_VERSION} as ci-package-openexr
+FROM ${ASWF_PKG_ORG}/ci-package-oiio:${VFXPLATFORM_VERSION} as ci-package-oiio
+FROM ${ASWF_PKG_ORG}/ci-package-ocio:${VFXPLATFORM_VERSION} as ci-package-ocio
+FROM ${OLIVE_ORG}/ci-package-ffmpeg:${FFMPEG_VERSION} as ci-package-ffmpeg
+FROM ${OLIVE_ORG}/ci-package-crashpad:latest as ci-package-crashpad
+
+FROM ${OLIVE_ORG}/ci-common:${CI_COMMON_VERSION} as ci-olive
+
+ARG OLIVE_ORG
+ARG VFXPLATFORM_VERSION
+ARG PYTHON_VERSION=3.7
+
+LABEL maintainer="olivevideoeditor@gmail.com"
+
+LABEL org.opencontainers.image.name="olivevideoeditor/ci-olive"
+LABEL org.opencontainers.image.description="CentOS CI Olive Build Image"
+LABEL org.opencontainers.image.url="http://olivevideoeditor.org"
+LABEL org.opencontainers.image.source="https://github.com/olive-editor/olive"
+LABEL org.opencontainers.image.vendor="Olive Team"
+LABEL org.opencontainers.image.version="1.0"
+
+ENV PYTHONPATH=/usr/local/lib/python${PYTHON_VERSION}/site-packages:${PYTHONPATH} \
+ CRASHPAD_LOCATION=/usr/local/crashpad \
+ VFXPLATFORM_VERSION=${VFXPLATFORM_VERSION}
+
+COPY --from=ci-package-qt /. /usr/local/
+COPY --from=ci-package-python /. /usr/local/
+COPY --from=ci-package-boost /. /usr/local/
+COPY --from=ci-package-openexr /. /usr/local/
+COPY --from=ci-package-oiio /. /usr/local/
+COPY --from=ci-package-ocio /. /usr/local/
+COPY --from=ci-package-ffmpeg /. /usr/local/
+COPY --from=ci-package-crashpad /. /usr/local/
+
+RUN curl --location "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage" \
+ -o "/usr/local/linuxdeployqt-x86_64.AppImage" && \
+ chmod a+x "/usr/local/linuxdeployqt-x86_64.AppImage"
diff --git a/docker/scripts/2/versions_common.sh b/docker/scripts/2/versions_common.sh
new file mode 100644
index 000000000..834ac6960
--- /dev/null
+++ b/docker/scripts/2/versions_common.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+# Copyright (c) Contributors to the aswf-docker Project. All rights reserved.
+# SPDX-License-Identifier: Apache-2.0
+
+set -ex
+
+export DTS_VERSION=9
+export CLANG_VERSION=10.0.0
+export NINJA_VERSION=1.10.0
diff --git a/docker/scripts/2021/versions_base.sh b/docker/scripts/2021/versions_base.sh
new file mode 100644
index 000000000..a56e53747
--- /dev/null
+++ b/docker/scripts/2021/versions_base.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+# Copyright (c) Contributors to the aswf-docker Project. All rights reserved.
+# SPDX-License-Identifier: Apache-2.0
+
+set -ex
+
+export BOOST_VERSION=1.70.0
+export CMAKE_VERSION=3.17.3
+export NUMPY_VERSION=1.16
+export PYSIDE_VERSION=5.12.6
+export PYTHON_VERSION_FULL=3.7.3
+export PYTHON_VERSION=3.7
+export QT_VERSION=5.12.8
diff --git a/docker/scripts/base/install_cmake.sh b/docker/scripts/base/install_cmake.sh
new file mode 100644
index 000000000..2aeeb9f0e
--- /dev/null
+++ b/docker/scripts/base/install_cmake.sh
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+# Copyright (c) Contributors to the aswf-docker Project. All rights reserved.
+# SPDX-License-Identifier: Apache-2.0
+
+set -ex
+
+if [ ! -f "$DOWNLOADS_DIR/cmake-${CMAKE_VERSION}-Linux-x86_64.sh" ]; then
+ curl --location "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-Linux-x86_64.sh" -o "$DOWNLOADS_DIR/cmake-${CMAKE_VERSION}-Linux-x86_64.sh"
+fi
+
+sh "$DOWNLOADS_DIR/cmake-${CMAKE_VERSION}-Linux-x86_64.sh" --skip-license --prefix=/usr/local --exclude-subdir
diff --git a/docker/scripts/build_crashpad.sh b/docker/scripts/build_crashpad.sh
new file mode 100644
index 000000000..eece43c00
--- /dev/null
+++ b/docker/scripts/build_crashpad.sh
@@ -0,0 +1,82 @@
+#!/usr/bin/env bash
+# Copyright (C) 2020 Olive Team
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+set -ex
+
+# Get Google's build tools
+git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
+
+# HACK: Compile our own gn. The one included in depot_tools requires GLIBC_2.18,
+# but CentOS 7 only ships with GLIBC_2.17.
+git clone https://gn.googlesource.com/gn
+# NOTE: Don't clone with --depth 1, this will make build/gen.py fail!
+cd gn
+python build/gen.py
+ninja -C out
+cd ..
+# Put the path to our own gn build first
+PATH="$(pwd)/gn/out:$(pwd)/depot_tools:$PATH"
+export PATH
+
+# Build Crashpad with Clang (default)
+# Toolchain can be controlled with env vars CC, CXX and AR
+mkdir crashpad
+cd crashpad
+fetch crashpad
+cd crashpad
+# TODO: Do we want to set any special args here? For example:
+# gn gen --args="target_cpu=\"x64\" is_debug=true" out/Default
+gn gen out/Default
+ninja -C out/Default
+
+# Include list
+echo 'out/Default/crashpad_handler' > /tmp/crashpad_include_list.txt
+find . \( -type f -o -type l \) \
+ -name "*.h" -o \
+ -name "*.o" -o \
+ -name "*.a" | cut -c3- >> /tmp/crashpad_include_list.txt
+
+# Exclude list
+echo '**/.git/**
+compat/android/**
+compat/ios/**
+compat/mac/**
+compat/non_elf/**
+compat/win/**
+handler/mac/**
+handler/win/**
+infra/**
+minidump/test/**
+out/Default/**_test*
+snapshot/fuchsia/**
+snapshot/ios/**
+snapshot/mac/**
+snapshot/win/**
+test/**
+third_party/fuchsia/**
+third_party/gyp/gyp/test/**
+third_party/mini_chromium/mini_chromium/base/fuchsia/**
+third_party/mini_chromium/mini_chromium/testing/**
+tools/mac/**
+util/fuchsia/**
+util/ios/**
+util/mac/**
+util/win/**' > /tmp/crashpad_exclude_list.txt
+
+rsync -av \
+ --files-from=/tmp/crashpad_include_list.txt \
+ --exclude-from=/tmp/crashpad_exclude_list.txt \
+ --prune-empty-dirs \
+ . "${OLIVE_INSTALL_PREFIX}/crashpad"
+
+cd ../..
+
+# Build Breakpad for minidump_stackwalk
+mkdir breakpad
+cd breakpad
+fetch breakpad
+cd src
+./configure --prefix="${OLIVE_INSTALL_PREFIX}/breakpad"
+make
+make install -j$(nproc)
diff --git a/docker/scripts/build_ffmpeg.sh b/docker/scripts/build_ffmpeg.sh
new file mode 100644
index 000000000..1f2c36a91
--- /dev/null
+++ b/docker/scripts/build_ffmpeg.sh
@@ -0,0 +1,158 @@
+#!/usr/bin/env bash
+# Copyright (C) 2020 Olive Team
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# Largely copied from https://trac.ffmpeg.org/wiki/CompilationGuide/Centos
+#
+# Uses { command } & pattern for parallelism https://gist.github.com/thenadz/6c0584d42fb007582fbc
+#
+# TOOD: Use advanced options such as LTO? e.g. https://code.videolan.org/videolan/x264/-/blob/master/configure
+# TODO: Enable debug symbols? (Or is it opt-out?)
+# TODO: Add more ffmpeg libraries? See https://raw.githubusercontent.com/jrottenberg/ffmpeg/master/docker-images/4.2/centos7/Dockerfile
+
+set -ex
+
+# Set up recent NASM
+{
+ curl -fLsS -o nasm.tar.xz "https://www.nasm.us/pub/nasm/releasebuilds/${NASM_VERSION}/nasm-${NASM_VERSION}.tar.xz"
+ tar xf nasm.tar.xz
+ rm -f nasm.tar.xz
+ cd nasm*
+ ./autogen.sh
+ ./configure \
+ --prefix="${OLIVE_INSTALL_PREFIX}"
+ make -j${NUM_JOBS}
+ make install
+ cd ..
+ rm -rf nasm*
+} &
+
+# Set up recent YASM
+{
+ curl -fLsS -o yasm.tar.gz "http://www.tortall.net/projects/yasm/releases/yasm-${YASM_VERSION}.tar.gz"
+ tar xf yasm.tar.gz
+ rm -f yasm.tar.gz
+ cd yasm*
+ ./configure \
+ --prefix="${OLIVE_INSTALL_PREFIX}"
+ make -j${NUM_JOBS}
+ make install
+ cd ..
+ rm -rf yasm*
+} &
+
+# join jobs, some libs depend on NASM/YASM
+wait
+
+# Set up libx264
+{
+ git clone --depth 1 "https://code.videolan.org/videolan/x264.git"
+ cd x264
+ ./configure \
+ --prefix="${OLIVE_INSTALL_PREFIX}" \
+ --enable-shared \
+ --enable-pic \
+ --disable-cli
+ make -j${NUM_JOBS}
+ make install
+ cd ..
+ rm -rf x264
+} &
+
+# Set up libx265
+{
+ # BitBucket dropped support for Mercurial repos
+ #hg clone https://bitbucket.org/multicoreware/x265
+ git clone --depth 1 "https://bitbucket.org/multicoreware/x265_git.git" x265
+ cd x265/build/linux
+ cmake \
+ -G "Unix Makefiles" \
+ -DCMAKE_INSTALL_PREFIX="${OLIVE_INSTALL_PREFIX}" \
+ ../../source
+ make -j${NUM_JOBS}
+ make install
+ cd ../../..
+ rm -rf x265
+} &
+
+# Set up libmp3lame
+{
+ curl -fLsS -o lame.tar.gz "https://downloads.sourceforge.net/project/lame/lame/${LAME_VERSION}/lame-${LAME_VERSION}.tar.gz"
+ tar xf lame.tar.gz
+ rm -f lame.tar.gz
+ cd lame*
+ ./configure \
+ --prefix="${OLIVE_INSTALL_PREFIX}" \
+ --enable-shared \
+ --enable-nasm \
+ --disable-frontend
+ make -j${NUM_JOBS}
+ make install
+ cd ..
+ rm -rf lame*
+} &
+
+# Set up libopus
+{
+ curl -fLsS -o opus.tar.gz "https://archive.mozilla.org/pub/opus/opus-${OPUS_VERSION}.tar.gz"
+ tar xf opus.tar.gz
+ rm -f opus.tar.gz
+ cd opus*
+ ./configure \
+ --prefix="${OLIVE_INSTALL_PREFIX}" \
+ --enable-shared
+ make -j${NUM_JOBS}
+ make install
+ cd ..
+ rm -rf opus*
+} &
+
+# Set up libvpx
+{
+ git clone --depth 1 "https://chromium.googlesource.com/webm/libvpx.git"
+ cd libvpx
+ ./configure \
+ --prefix="${OLIVE_INSTALL_PREFIX}" \
+ --enable-shared \
+ --enable-pic \
+ --enable-vp9-highbitdepth \
+ --as=yasm \
+ --disable-examples \
+ --disable-unit-tests \
+ --disable-docs \
+ --disable-install-bins
+ make -j${NUM_JOBS}
+ make install
+ cd ..
+ rm -rf libvpx
+} &
+
+# join all jobs
+wait
+
+curl -fLsS -o ffmpeg.tar.xz "https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.xz"
+tar xf ffmpeg.tar.xz
+cd ffmpeg*
+
+# TODO: --enable-debug?
+PKG_CONFIG_PATH="/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH" ./configure \
+ --disable-doc \
+ --disable-ffplay \
+ --enable-gpl \
+ --enable-version3 \
+ --enable-shared \
+ --enable-libfreetype \
+ --enable-libmp3lame \
+ --enable-libopus \
+ --enable-libvpx \
+ --enable-libx264 \
+ --enable-libx265 \
+ --prefix="${OLIVE_INSTALL_PREFIX}" \
+ --extra-libs=-lpthread \
+ --extra-libs=-lm \
+ --extra-cflags="-I${OLIVE_INSTALL_PREFIX}/include" \
+ --extra-ldflags="-L${OLIVE_INSTALL_PREFIX}/lib"
+make -j${NUM_JOBS}
+make install
+cd ..
+rm -rf ffmpeg*
diff --git a/docker/scripts/common/before_build.sh b/docker/scripts/common/before_build.sh
new file mode 100644
index 000000000..fb7383c89
--- /dev/null
+++ b/docker/scripts/common/before_build.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+# Copyright (c) Contributors to the aswf-docker Project. All rights reserved.
+# SPDX-License-Identifier: Apache-2.0
+
+set -ex
+
+rm -rf /package
+
+cd "${OLIVE_INSTALL_PREFIX}"
+find . -type f -o -type l | cut -c3- > /tmp/previous-prefix-files.txt
diff --git a/docker/scripts/common/copy_new_files.sh b/docker/scripts/common/copy_new_files.sh
new file mode 100644
index 000000000..8c1009b4d
--- /dev/null
+++ b/docker/scripts/common/copy_new_files.sh
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+# Copyright (c) Contributors to the aswf-docker Project. All rights reserved.
+# SPDX-License-Identifier: Apache-2.0
+
+set -ex
+
+mkdir -p /package
+
+cd "${OLIVE_INSTALL_PREFIX}"
+find . -type l -o -type f | cut -c3- > /tmp/new-prefix-files.txt
+rsync -av --files-from=/tmp/new-prefix-files.txt --exclude-from=/tmp/previous-prefix-files.txt . /package/
diff --git a/docker/scripts/common/install_yumpackages.sh b/docker/scripts/common/install_yumpackages.sh
new file mode 100644
index 000000000..f5003675c
--- /dev/null
+++ b/docker/scripts/common/install_yumpackages.sh
@@ -0,0 +1,74 @@
+#!/usr/bin/env bash
+# Copyright (C) 2019 Olive Team
+# Copyright (c) Contributors to the aswf-docker Project. All rights reserved.
+# SPDX-License-Identifier: Apache-2.0 OR GPL-3.0-or-later
+
+set -ex
+
+# TODO: Check if this causes any problems. ASWF doesn't run a yum update.
+yum update -y
+
+# TODO: Add deps of deps which are explicitly listed in aswf-docker?
+yum install --setopt=tsflags=nodocs -y \
+ bzip2-devel \
+ cups-libs \
+ freetype-devel \
+ giflib-devel \
+ gstreamer1 gstreamer1-devel \
+ gstreamer1-plugins-bad-free gstreamer1-plugins-bad-free-devel \
+ libicu-devel \
+ libmng-devel \
+ LibRaw-devel \
+ libwebp-devel \
+ libXcomposite libXcomposite-devel \
+ libXcursor libXcursor-devel \
+ libxkbcommon libxkbcommon-devel \
+ libxkbcommon-x11-devel \
+ libXScrnSaver libXScrnSaver-devel \
+ mesa-libGL-devel \
+ openjpeg2-devel \
+ pciutils-devel \
+ pulseaudio-libs pulseaudio-libs-devel \
+ python3-tkinter \
+ zlib-devel
+
+# This is needed for Xvfb to function properly.
+dbus-uuidgen > /etc/machine-id
+
+yum -y groupinstall "Development Tools"
+
+# TODO: Below code installs the obsolete devtoolset-6.
+# Unclear which devtoolset it will be for VFX platform CY2021:
+# https://groups.google.com/forum/#!topic/vfx-platform-discuss/_-_CPw1fD3c
+
+yum install -y --setopt=tsflags=nodocs centos-release-scl-rh yum-utils
+
+if [[ $DTS_VERSION == 6 ]]; then
+ # Use the centos vault as the original devtoolset-6 is not part of CentOS-7 anymore
+ sed -i 's/7/7.6.1810/g; s|^#\s*\(baseurl=http://\)mirror|\1vault|g; /mirrorlist/d' /etc/yum.repos.d/CentOS-SCLo-*.repo
+fi
+
+yum install -y --setopt=tsflags=nodocs \
+ "devtoolset-$DTS_VERSION-toolchain"
+
+yum install -y epel-release
+
+# Additional package that are not found initially
+yum install -y \
+ rh-git218 \
+# lame-devel
+# libcaca-devel \
+# libdb4-devel \
+# libdc1394-devel \
+# p7zip \
+# yasm-devel \
+# zvbi-devel
+
+# TODO: Does clearing the cache have any negative side effects?
+yum clean all
+
+# HACK: Qt5GuiConfigExtras.cmake expects libGL.so in /usr/local/lib64 but it gets installed to /usr/lib64
+ln -s /usr/lib64/libGL.so /usr/local/lib64/
+# Alternatively, we could edit /usr/local/lib/cmake/Qt5Gui/Qt5GuiConfigExtras.cmake
+# - _qt5gui_find_extra_libs(OPENGL "/usr/local/lib64/libGL.so" "" "")
+# + _qt5gui_find_extra_libs(OPENGL "/usr/lib64/libGL.so" "" "")