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 [![Build Status](https://travis-ci.org/olive-editor/olive.svg?branch=master)](https://travis-ci.org/olive-editor/olive) [![Build Status](https://ci.appveyor.com/api/projects/status/5s4jabxayg51rv95?svg=true)](https://ci.appveyor.com/project/itsmattkc/olive) +# Olive Video Editor [![Build status](https://github.com/olive-editor/olive/workflows/CI/badge.svg?branch=master)](https://github.com/olive-editor/olive/actions?query=branch%3Amaster) Olive is a free non-linear video editor for Windows, macOS, and Linux. -![screen](https://www.olivevideoeditor.org/img/screenshot.jpg) +![screen](https://olivevideoeditor.org/img/020-2.png) -**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" "" "")