diff --git a/.github/ISSUE_TEMPLATE/01-crash_issue.md b/.github/ISSUE_TEMPLATE/01-crash_issue.md index c05544838..0b68b7202 100644 --- a/.github/ISSUE_TEMPLATE/01-crash_issue.md +++ b/.github/ISSUE_TEMPLATE/01-crash_issue.md @@ -6,7 +6,7 @@ labels: "Crash, Triage" assignees: '' --- -**Commit Hash** +**Commit Hash (8 character string of letters/numbers in title bar)** **Platform** diff --git a/.github/ISSUE_TEMPLATE/50-build_issue.md b/.github/ISSUE_TEMPLATE/50-build_issue.md index f14b6c914..8d93ad06d 100644 --- a/.github/ISSUE_TEMPLATE/50-build_issue.md +++ b/.github/ISSUE_TEMPLATE/50-build_issue.md @@ -6,7 +6,7 @@ labels: "Building/Packaging, Triage" assignees: '' --- -**Commit Hash** +**Commit Hash (8 character string of letters/numbers in title bar)** **Platform** diff --git a/.github/ISSUE_TEMPLATE/50-cache_issue.md b/.github/ISSUE_TEMPLATE/50-cache_issue.md index 13b8bc14b..0d088aa8c 100644 --- a/.github/ISSUE_TEMPLATE/50-cache_issue.md +++ b/.github/ISSUE_TEMPLATE/50-cache_issue.md @@ -6,7 +6,7 @@ labels: "Disk Cache, Triage" assignees: '' --- -**Commit Hash** +**Commit Hash (8 character string of letters/numbers in title bar)** **Platform** diff --git a/.github/ISSUE_TEMPLATE/50-codec_issue.md b/.github/ISSUE_TEMPLATE/50-codec_issue.md index a3a936915..04082833f 100644 --- a/.github/ISSUE_TEMPLATE/50-codec_issue.md +++ b/.github/ISSUE_TEMPLATE/50-codec_issue.md @@ -6,7 +6,7 @@ labels: "Codec, Triage" assignees: '' --- -**Commit Hash** +**Commit Hash (8 character string of letters/numbers in title bar)** **Platform** diff --git a/.github/ISSUE_TEMPLATE/50-color_issue.md b/.github/ISSUE_TEMPLATE/50-color_issue.md index 6a7464ea3..99889a56b 100644 --- a/.github/ISSUE_TEMPLATE/50-color_issue.md +++ b/.github/ISSUE_TEMPLATE/50-color_issue.md @@ -6,7 +6,7 @@ labels: "Color Management, Triage" assignees: '' --- -**Commit Hash** +**Commit Hash (8 character string of letters/numbers in title bar)** **Platform** diff --git a/.github/ISSUE_TEMPLATE/50-editing_issue.md b/.github/ISSUE_TEMPLATE/50-editing_issue.md index c731e221f..0465e88a5 100644 --- a/.github/ISSUE_TEMPLATE/50-editing_issue.md +++ b/.github/ISSUE_TEMPLATE/50-editing_issue.md @@ -6,7 +6,7 @@ labels: "Timeline/Editing, Triage" assignees: '' --- -**Commit Hash** +**Commit Hash (8 character string of letters/numbers in title bar)** **Platform** diff --git a/.github/ISSUE_TEMPLATE/50-export_issue.md b/.github/ISSUE_TEMPLATE/50-export_issue.md index e21a79294..7ccef22fc 100644 --- a/.github/ISSUE_TEMPLATE/50-export_issue.md +++ b/.github/ISSUE_TEMPLATE/50-export_issue.md @@ -6,7 +6,7 @@ labels: "Export, Triage" assignees: '' --- -**Commit Hash** +**Commit Hash (8 character string of letters/numbers in title bar)** **Platform** diff --git a/.github/ISSUE_TEMPLATE/50-node_issue.md b/.github/ISSUE_TEMPLATE/50-node_issue.md index 7749521ba..4d15ecebe 100644 --- a/.github/ISSUE_TEMPLATE/50-node_issue.md +++ b/.github/ISSUE_TEMPLATE/50-node_issue.md @@ -6,7 +6,7 @@ labels: "Nodes/Compositing, Triage" assignees: '' --- -**Commit Hash** +**Commit Hash (8 character string of letters/numbers in title bar)** **Platform** diff --git a/.github/ISSUE_TEMPLATE/50-playback_issue.md b/.github/ISSUE_TEMPLATE/50-playback_issue.md index 00aa98149..5e35446f5 100644 --- a/.github/ISSUE_TEMPLATE/50-playback_issue.md +++ b/.github/ISSUE_TEMPLATE/50-playback_issue.md @@ -6,7 +6,7 @@ labels: "Playback, Triage" assignees: '' --- -**Commit Hash** +**Commit Hash (8 character string of letters/numbers in title bar)** **Platform** diff --git a/.github/ISSUE_TEMPLATE/50-project_issue.md b/.github/ISSUE_TEMPLATE/50-project_issue.md index 9e151539c..fa65f393e 100644 --- a/.github/ISSUE_TEMPLATE/50-project_issue.md +++ b/.github/ISSUE_TEMPLATE/50-project_issue.md @@ -6,7 +6,7 @@ labels: "Project, Triage" assignees: '' --- -**Commit Hash** +**Commit Hash (8 character string of letters/numbers in title bar)** **Platform** diff --git a/.github/ISSUE_TEMPLATE/50-renderer_issue.md b/.github/ISSUE_TEMPLATE/50-renderer_issue.md index 3d6812004..f97a22079 100644 --- a/.github/ISSUE_TEMPLATE/50-renderer_issue.md +++ b/.github/ISSUE_TEMPLATE/50-renderer_issue.md @@ -6,7 +6,7 @@ labels: "Renderer, Triage" assignees: '' --- -**Commit Hash** +**Commit Hash (8 character string of letters/numbers in title bar)** **Platform** diff --git a/.github/ISSUE_TEMPLATE/50-ui_issue.md b/.github/ISSUE_TEMPLATE/50-ui_issue.md index 12af9f863..a0cfa3c02 100644 --- a/.github/ISSUE_TEMPLATE/50-ui_issue.md +++ b/.github/ISSUE_TEMPLATE/50-ui_issue.md @@ -6,7 +6,7 @@ labels: "User Interface, Triage" assignees: '' --- -**Commit Hash** +**Commit Hash (8 character string of letters/numbers in title bar)** **Platform** diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d3fda4bab..36f4fcfa8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,28 +15,44 @@ jobs: build-type: RelWithDebInfo compiler-name: MSVC 16.x os-name: Windows + os: windows-latest + dep-suffix: win.7z + extract-tool: 7z x + - build: 2 + build-type: RelWithDebInfo + compiler-name: Clang LLVM + os-name: macOS + os: macos-latest + dep-suffix: mac.zip + extract-tool: unzip name: | ${{ matrix.os-name }} <${{ matrix.compiler-name }}, ${{ matrix.build-type }}> - runs-on: windows-latest + runs-on: ${{ matrix.os }} steps: - - name: Set package name - shell: bash - run: | - echo "::set-env name=PKGNAME::$(echo Olive-${GITHUB_SHA::8}-Windows-x86_64)" + - if: matrix.os == 'windows-latest' + - name: Set package name + shell: bash + run: | + echo "::set-env name=PKGNAME::$(echo Olive-${GITHUB_SHA::8}-Windows-x86_64)" + + - if: matrix.os == 'macos-latest' + - name: Set package name + shell: bash + run: | + echo "::set-env name=PKGNAME::$(echo Olive-${GITHUB_SHA::8}-macOS)" - uses: actions/checkout@v2 - 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: Enable Developer Command Prompt - uses: ilammy/msvc-dev-cmd@v1.3.0 + - if: matrix.os == 'windows-latest' + - name: Enable Developer Command Prompt + uses: ilammy/msvc-dev-cmd@v1.3.0 - name: Acquire Qt uses: jurplel/install-qt-action@v2.8.0 @@ -44,63 +60,99 @@ jobs: version: 5.15.1 - name: Acquire FFmpeg + shell: bash + env: + DEP_SUFFIX: ${{ matrix.dep-suffix }} + EXTRACT_TOOL: ${{ matrix.extract-tool }} run: | - curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/ffmpeg-win.7z - 7z x ffmpeg-win.7z + curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/ffmpeg-$DEP_SUFFIX + 7z x ffmpeg-$DEP_SUFFIX - name: Acquire OpenColorIO + shell: bash + env: + DEP_SUFFIX: ${{ matrix.dep-suffix }} + EXTRACT_TOOL: ${{ matrix.extract-tool }} run: | - curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/ocio-win.7z - 7z x ocio-win.7z + curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/ocio-$DEP_SUFFIX + 7z x ocio-$DEP_SUFFIX - name: Acquire OpenEXR + shell: bash + env: + DEP_SUFFIX: ${{ matrix.dep-suffix }} + EXTRACT_TOOL: ${{ matrix.extract-tool }} run: | - curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/openexr-win.7z - 7z x openexr-win.7z + curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/openexr-$DEP_SUFFIX + 7z x openexr-$DEP_SUFFIX - name: Acquire OpenImageIO + shell: bash + env: + DEP_SUFFIX: ${{ matrix.dep-suffix }} + EXTRACT_TOOL: ${{ matrix.extract-tool }} run: | - curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/oiio-win.7z - 7z x oiio-win.7z + curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/oiio-$DEP_SUFFIX + 7z x oiio-$DEP_SUFFIX - name: Acquire libpng + shell: bash + env: + DEP_SUFFIX: ${{ matrix.dep-suffix }} + EXTRACT_TOOL: ${{ matrix.extract-tool }} run: | - curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/libpng-win.7z - 7z x libpng-win.7z + curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/libpng-$DEP_SUFFIX + 7z x libpng-$DEP_SUFFIX - name: Acquire libtiff + shell: bash + env: + DEP_SUFFIX: ${{ matrix.dep-suffix }} + EXTRACT_TOOL: ${{ matrix.extract-tool }} run: | - curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/libtiff-win.7z - 7z x libtiff-win.7z + curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/libtiff-$DEP_SUFFIX + 7z x libtiff-$DEP_SUFFIX - name: Acquire turbojpeg + shell: bash + env: + DEP_SUFFIX: ${{ matrix.dep-suffix }} + EXTRACT_TOOL: ${{ matrix.extract-tool }} run: | - curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/turbojpeg-win.7z - 7z x turbojpeg-win.7z + curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/turbojpeg-$DEP_SUFFIX + 7z x turbojpeg-$DEP_SUFFIX - - name: Acquire zlib - run: | - curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/zlib-win.7z - 7z x zlib-win.7z + - if: matrix.os == 'windows-latest' + - name: Acquire zlib + shell: bash + env: + DEP_SUFFIX: ${{ matrix.dep-suffix }} + EXTRACT_TOOL: ${{ matrix.extract-tool }} + run: | + curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/zlib-$DEP_SUFFIX + 7z x zlib-$DEP_SUFFIX - name: Acquire Boost + shell: bash + env: + DEP_SUFFIX: ${{ matrix.dep-suffix }} + EXTRACT_TOOL: ${{ matrix.extract-tool }} run: | - curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/boost-win.7z - 7z x boost-win.7z + curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/boost-$DEP_SUFFIX + 7z x boost-$DEP_SUFFIX - name: Acquire Crashpad + shell: bash + env: + DEP_SUFFIX: ${{ matrix.dep-suffix }} + EXTRACT_TOOL: ${{ matrix.extract-tool }} run: | - curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/crashpad-win.7z - 7z x crashpad-win.7z + curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/crashpad-$DEP_SUFFIX + 7z x crashpad-$DEP_SUFFIX - 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=${{ matrix.build-type }} -G "Ninja" @@ -108,95 +160,130 @@ jobs: - name: Build working-directory: ${{ runner.workspace }}/build shell: bash - # Execute the build. You can specify a specific target with "--target " run: | cmake --build . - 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 ${{ matrix.build-type }} - - 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 + - if: matrix.os == 'windows-latest' + - 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: Deploy Installer - working-directory: ${{ runner.workspace }}/build - shell: cmd - run: | - copy %GITHUB_WORKSPACE%\app\packaging\windows\nsis\* . - copy %GITHUB_WORKSPACE%\LICENSE . - makensis -V4 -DX64 "-XOutFile %PKGNAME%.exe" olive.nsi + - name: Deploy Installer + working-directory: ${{ runner.workspace }}/build + shell: cmd + run: | + copy %GITHUB_WORKSPACE%\app\packaging\windows\nsis\* . + copy %GITHUB_WORKSPACE%\LICENSE . + makensis -V4 -DX64 "-XOutFile %PKGNAME%.exe" olive.nsi - - name: Deploy Portable - working-directory: ${{ runner.workspace }}/build - shell: cmd - run: | - type NUL > olive-editor\portable - 7z a %PKGNAME%.zip olive-editor + - name: Deploy Portable + working-directory: ${{ runner.workspace }}/build + shell: cmd + run: | + type NUL > olive-editor\portable + 7z a %PKGNAME%.zip olive-editor - - 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: 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 + if: github.event_name != 'pull_request' - - 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 - if: github.event_name != 'pull_request' + - 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 + if: github.event_name != 'pull_request' - - 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 - if: github.event_name != 'pull_request' + - 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 Installer Artifact - uses: actions/upload-artifact@v2 - with: - name: ${{ env.PKGNAME }}.exe - path: ${{ runner.workspace }}/build/${{ env.PKGNAME }}.exe + - 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 Portable Artifact - uses: actions/upload-artifact@v2 - with: - name: ${{ env.PKGNAME }}.zip - path: ${{ runner.workspace }}/build/${{ env.PKGNAME }}.zip + - 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 + + - if: matrix.os == 'macos-latest' + - name: Create Package + working-directory: ${{ runner.workspace }}/build + env: + BUNDLE_NAME: Olive.app + run: | + mv app/$BUNDLE_NAME . + macdeployqt $BUNDLE_NAME + curl -fLOSs --retry 3 https://github.com/arl/macdeployqtfix/raw/master/macdeployqtfix.py + python2 macdeployqtfix.py $BUNDLE_NAME/Contents/MacOS/Olive /usr/local/Cellar/qt5/5.*/ + + - name: Deploy Package + working-directory: ${{ runner.workspace }}/build + run: | + zip -r $PKGNAME.zip $BUNDLE_NAME + + - name: Upload Artifact 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 + if: github.event_name != 'pull_request' + + - name: Upload Artifact to GitHub + uses: actions/upload-artifact@v2 + with: + name: ${{ env.PKGNAME }}.zip + path: ${{ runner.workspace }}/build/${{ env.PKGNAME }}.zip 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/common/crashpadinterface.cpp b/app/common/crashpadinterface.cpp index 85cf37fad..6c55ab45e 100644 --- a/app/common/crashpadinterface.cpp +++ b/app/common/crashpadinterface.cpp @@ -25,21 +25,55 @@ #include #include #include +#include #include "filefunctions.h" +#ifdef Q_OS_WINDOWS +#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() +// 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() +// 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 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); +} + +#ifdef Q_OS_WINDOWS +LONG WINAPI Win32ExceptionHandler(_EXCEPTION_POINTERS *ExceptionInfo) +{ + if (ShowCrashConfirmation()) { + client->DumpAndCrash(ExceptionInfo); + } + + return EXCEPTION_CONTINUE_SEARCH; +} +#endif + bool InitializeCrashpad() { QString exe_dir = QCoreApplication::applicationDirPath(); @@ -73,9 +107,15 @@ bool InitializeCrashpad() settings->SetUploadsEnabled(true); // Start crash handler - crashpad::CrashpadClient *client = new crashpad::CrashpadClient(); + client = new crashpad::CrashpadClient(); bool status = client->StartHandler(handler, reports_dir, metrics_dir, url, annotations, arguments, true, true); + + // Override Crashpad exception filter with our own +#ifdef Q_OS_WINDOWS + SetUnhandledExceptionFilter(Win32ExceptionHandler); +#endif + return status; } 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/packaging/windows/nsis/olive.nsi b/app/packaging/windows/nsis/olive.nsi index 9e9d5e4b9..0aa27c8b6 100644 --- a/app/packaging/windows/nsis/olive.nsi +++ b/app/packaging/windows/nsis/olive.nsi @@ -32,35 +32,35 @@ InstallDir "$PROGRAMFILES32\${APP_NAME}" !insertmacro MUI_LANGUAGE "English" -Section "Olive (required)" - - SectionIn RO - - SetOutPath $INSTDIR - - File /r olive-editor\* - - WriteUninstaller "$INSTDIR\uninstall.exe" +Section "Olive" + SectionIn RO + SetOutPath $INSTDIR + File /r olive-editor\* + WriteUninstaller "$INSTDIR\uninstall.exe" + # Install Visual C++ 2010 Redistributable + File "vcredist_x64.exe" + ExecWait '"$INSTDIR\vcredist_x64.exe" /quiet' + Delete "$INSTDIR\vcredist_x64.exe" SectionEnd Section "Create Desktop shortcut" - CreateShortCut "$DESKTOP\${APP_NAME}.lnk" "$INSTDIR\${APP_TARGET}.exe" + CreateShortCut "$DESKTOP\${APP_NAME}.lnk" "$INSTDIR\${APP_TARGET}.exe" SectionEnd Section "Create Start Menu shortcut" - CreateDirectory "$SMPROGRAMS\${APP_NAME}" - CreateShortCut "$SMPROGRAMS\${APP_NAME}\${APP_NAME}.lnk" "$INSTDIR\${APP_TARGET}.exe" - CreateShortCut "$SMPROGRAMS\${APP_NAME}\Uninstall ${APP_NAME}.lnk" "$INSTDIR\uninstall.exe" + CreateDirectory "$SMPROGRAMS\${APP_NAME}" + CreateShortCut "$SMPROGRAMS\${APP_NAME}\${APP_NAME}.lnk" "$INSTDIR\${APP_TARGET}.exe" + CreateShortCut "$SMPROGRAMS\${APP_NAME}\Uninstall ${APP_NAME}.lnk" "$INSTDIR\uninstall.exe" SectionEnd Section "Associate *.ove files with Olive" - WriteRegStr HKCR ".ove" "" "OliveEditor.OVEFile" - WriteRegStr HKCR ".ove" "Content Type" "application/vnd.olive-project" - WriteRegStr HKCR "OliveEditor.OVEFile" "" "Olive project file" - WriteRegStr HKCR "OliveEditor.OVEFile\DefaultIcon" "" "$INSTDIR\olive-editor.exe,1" - WriteRegStr HKCR "OliveEditor.OVEFile\shell\open\command" "" "$\"$INSTDIR\olive-editor.exe$\" $\"%1$\"" - System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)' + WriteRegStr HKCR ".ove" "" "OliveEditor.OVEFile" + WriteRegStr HKCR ".ove" "Content Type" "application/vnd.olive-project" + WriteRegStr HKCR "OliveEditor.OVEFile" "" "Olive project file" + WriteRegStr HKCR "OliveEditor.OVEFile\DefaultIcon" "" "$INSTDIR\olive-editor.exe,1" + WriteRegStr HKCR "OliveEditor.OVEFile\shell\open\command" "" "$\"$INSTDIR\olive-editor.exe$\" $\"%1$\"" + System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)' SectionEnd UninstPage uninstConfirm @@ -68,18 +68,18 @@ UninstPage instfiles Section "uninstall" - rmdir /r "$INSTDIR" + rmdir /r "$INSTDIR" - Delete "$DESKTOP\${APP_NAME}.lnk" - rmdir /r "$SMPROGRAMS\${APP_NAME}" - - DeleteRegKey HKCR ".ove" - DeleteRegKey HKCR "OliveEditor.OVEFile" - DeleteRegKey HKCR "OliveEditor.OVEFile\DefaultIcon" "" - DeleteRegKey HKCR "OliveEditor.OVEFile\shell\open\command" "" - System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)' + Delete "$DESKTOP\${APP_NAME}.lnk" + rmdir /r "$SMPROGRAMS\${APP_NAME}" + + DeleteRegKey HKCR ".ove" + DeleteRegKey HKCR "OliveEditor.OVEFile" + DeleteRegKey HKCR "OliveEditor.OVEFile\DefaultIcon" "" + DeleteRegKey HKCR "OliveEditor.OVEFile\shell\open\command" "" + System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)' SectionEnd Function LaunchOlive - ExecShell "" "$INSTDIR\${APP_TARGET}.exe" + ExecShell "" "$INSTDIR\${APP_TARGET}.exe" FunctionEnd 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/backend/renderworker.cpp b/app/render/backend/renderworker.cpp index 28e8a50f5..2db075c25 100644 --- a/app/render/backend/renderworker.cpp +++ b/app/render/backend/renderworker.cpp @@ -126,7 +126,7 @@ void RenderWorker::RenderFrame(RenderTicketPtr ticket, ViewerOutput* viewer, con FramePtr frame = Frame::Create(); frame->set_timestamp(time); - if (video_force_download_resolution_ || texture.isNull()) { + if (video_force_download_resolution_) { // If we're setting the resolution ourselves or we're zeroing it out, allocate the frame now frame->set_video_params(VideoParams(video_params_.width(), video_params_.height(), 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/task/project/import/importerrordialog.h b/app/task/project/import/importerrordialog.h new file mode 100644 index 000000000..8ee6bf740 --- /dev/null +++ b/app/task/project/import/importerrordialog.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 PROJECTIMPORTERRORDIALOG_H +#define PROJECTIMPORTERRORDIALOG_H + +#include + +#include "common/define.h" + +OLIVE_NAMESPACE_ENTER + +class ProjectImportErrorDialog : public QDialog +{ + Q_OBJECT +public: + ProjectImportErrorDialog(const QStringList& filenames, QWidget* parent = nullptr); + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // PROJECTIMPORTERRORDIALOG_H diff --git a/app/ui/style/style.cpp b/app/ui/style/style.cpp index 9de184cb1..26ee7d86f 100644 --- a/app/ui/style/style.cpp +++ b/app/ui/style/style.cpp @@ -39,6 +39,7 @@ QMap StyleManager::available_themes_; void StyleManager::UseOSNativeStyling(QWidget *widget) { +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) || QT_VERSION > QT_VERSION_CHECK(5, 15, 1) #if defined(Q_OS_WINDOWS) QStyle* s = QStyleFactory::create(QStringLiteral("windowsvista")); widget->setStyle(s); @@ -48,6 +49,7 @@ void StyleManager::UseOSNativeStyling(QWidget *widget) widget->setStyle(s); widget->setPalette(s->standardPalette()); #endif +#endif } QPalette StyleManager::ParsePalette(const QString& ini_path) 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/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index 983f422e1..25f13ac58 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -99,7 +99,11 @@ void ViewerDisplayWidget::SetSignalCursorColorEnabled(bool e) void ViewerDisplayWidget::SetImage(FramePtr in_buffer) { - last_loaded_buffer_ = in_buffer; + if (in_buffer && in_buffer->is_allocated()) { + last_loaded_buffer_ = in_buffer; + } else { + last_loaded_buffer_ = nullptr; + } if (last_loaded_buffer_) { makeCurrent(); 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)