Merge branch 'master' into otio
This commit is contained in:
@@ -6,7 +6,7 @@ labels: "Crash, Triage"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
**Commit Hash**
|
||||
**Commit Hash (8 character string of letters/numbers in title bar)**
|
||||
|
||||
**Platform**
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ labels: "Building/Packaging, Triage"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
**Commit Hash**
|
||||
**Commit Hash (8 character string of letters/numbers in title bar)**
|
||||
|
||||
**Platform**
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ labels: "Disk Cache, Triage"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
**Commit Hash**
|
||||
**Commit Hash (8 character string of letters/numbers in title bar)**
|
||||
|
||||
**Platform**
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ labels: "Codec, Triage"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
**Commit Hash**
|
||||
**Commit Hash (8 character string of letters/numbers in title bar)**
|
||||
|
||||
**Platform**
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ labels: "Color Management, Triage"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
**Commit Hash**
|
||||
**Commit Hash (8 character string of letters/numbers in title bar)**
|
||||
|
||||
**Platform**
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ labels: "Timeline/Editing, Triage"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
**Commit Hash**
|
||||
**Commit Hash (8 character string of letters/numbers in title bar)**
|
||||
|
||||
**Platform**
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ labels: "Export, Triage"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
**Commit Hash**
|
||||
**Commit Hash (8 character string of letters/numbers in title bar)**
|
||||
|
||||
**Platform**
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ labels: "Nodes/Compositing, Triage"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
**Commit Hash**
|
||||
**Commit Hash (8 character string of letters/numbers in title bar)**
|
||||
|
||||
**Platform**
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ labels: "Playback, Triage"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
**Commit Hash**
|
||||
**Commit Hash (8 character string of letters/numbers in title bar)**
|
||||
|
||||
**Platform**
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ labels: "Project, Triage"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
**Commit Hash**
|
||||
**Commit Hash (8 character string of letters/numbers in title bar)**
|
||||
|
||||
**Platform**
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ labels: "Renderer, Triage"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
**Commit Hash**
|
||||
**Commit Hash (8 character string of letters/numbers in title bar)**
|
||||
|
||||
**Platform**
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ labels: "User Interface, Triage"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
**Commit Hash**
|
||||
**Commit Hash (8 character string of letters/numbers in title bar)**
|
||||
|
||||
**Platform**
|
||||
|
||||
|
||||
+199
-95
@@ -1,39 +1,62 @@
|
||||
name: CMake
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: RelWithDebInfo
|
||||
|
||||
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
|
||||
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
|
||||
dep-suffix: win.7z
|
||||
extract-tool: 7z x
|
||||
- build: 2
|
||||
build-type: RelWithDebInfo
|
||||
compiler-name: Clang LLVM
|
||||
os-name: macOS
|
||||
os-arch: x86_64
|
||||
os: macos-latest
|
||||
cmake-gen: Unix Makefiles
|
||||
dep-suffix: mac.zip
|
||||
extract-tool: sudo unzip -d /usr/local -o
|
||||
name: |
|
||||
${{ matrix.os-name }}
|
||||
<${{ matrix.compiler-name }},
|
||||
${{ matrix.build-type }}>
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
env:
|
||||
PKGNAME: Olive-${GITHUB_SHA:8}-Windows-x86_64
|
||||
DOWNLOAD_TOOL: curl -fLOSs --retry 3
|
||||
|
||||
steps:
|
||||
- name: Set package name
|
||||
- name: Checkout Source Code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Automatically Generate Package Name
|
||||
shell: bash
|
||||
run: echo "::set-env name=PKGNAME::$(echo Olive-${GITHUB_SHA::8}-Windows-x86_64)"
|
||||
env:
|
||||
PLATFORM: ${{ matrix.os-name }}
|
||||
ARCH: ${{ matrix.os-arch }}
|
||||
run: |
|
||||
echo "::set-env name=PKGNAME::$(echo Olive-${GITHUB_SHA::8}-$PLATFORM-$ARCH)"
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- name: Create Build Folder
|
||||
run: |
|
||||
cmake -E make_directory ${{ runner.workspace }}/build
|
||||
|
||||
- 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
|
||||
- name: Enable Developer Command Prompt (Windows)
|
||||
uses: ilammy/msvc-dev-cmd@v1.3.0
|
||||
if: matrix.os == 'windows-latest'
|
||||
|
||||
- name: Acquire Qt
|
||||
uses: jurplel/install-qt-action@v2.8.0
|
||||
@@ -41,102 +64,183 @@ jobs:
|
||||
version: 5.15.1
|
||||
|
||||
- name: Acquire FFmpeg
|
||||
run: curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/ffmpeg-win.7z && 7z x ffmpeg-win.7z
|
||||
shell: bash
|
||||
env:
|
||||
DEP_SUFFIX: ${{ matrix.dep-suffix }}
|
||||
EXTRACT_TOOL: ${{ matrix.extract-tool }}
|
||||
run: |
|
||||
$DOWNLOAD_TOOL http://olivevideoeditor.org/deps/ffmpeg-$DEP_SUFFIX
|
||||
$EXTRACT_TOOL ffmpeg-$DEP_SUFFIX
|
||||
|
||||
- name: Acquire OpenColorIO
|
||||
run: curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/ocio-win.7z && 7z x ocio-win.7z
|
||||
shell: bash
|
||||
env:
|
||||
DEP_SUFFIX: ${{ matrix.dep-suffix }}
|
||||
EXTRACT_TOOL: ${{ matrix.extract-tool }}
|
||||
run: |
|
||||
$DOWNLOAD_TOOL http://olivevideoeditor.org/deps/ocio-$DEP_SUFFIX
|
||||
$EXTRACT_TOOL ocio-$DEP_SUFFIX
|
||||
|
||||
- name: Acquire OpenEXR
|
||||
run: curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/openexr-win.7z && 7z x openexr-win.7z
|
||||
shell: bash
|
||||
env:
|
||||
DEP_SUFFIX: ${{ matrix.dep-suffix }}
|
||||
EXTRACT_TOOL: ${{ matrix.extract-tool }}
|
||||
run: |
|
||||
$DOWNLOAD_TOOL http://olivevideoeditor.org/deps/openexr-$DEP_SUFFIX
|
||||
$EXTRACT_TOOL openexr-$DEP_SUFFIX
|
||||
|
||||
- name: Acquire OpenImageIO
|
||||
run: curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/oiio-win.7z && 7z x oiio-win.7z
|
||||
|
||||
- name: Acquire libpng
|
||||
run: curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/libpng-win.7z && 7z x libpng-win.7z
|
||||
|
||||
- name: Acquire libtiff
|
||||
run: curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/libtiff-win.7z && 7z x libtiff-win.7z
|
||||
|
||||
- name: Acquire turbojpeg
|
||||
run: curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/turbojpeg-win.7z && 7z x turbojpeg-win.7z
|
||||
|
||||
- name: Acquire zlib
|
||||
run: curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/zlib-win.7z && 7z x zlib-win.7z
|
||||
|
||||
- name: Acquire Boost
|
||||
run: curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/boost-win.7z && 7z x boost-win.7z
|
||||
shell: bash
|
||||
env:
|
||||
DEP_SUFFIX: ${{ matrix.dep-suffix }}
|
||||
EXTRACT_TOOL: ${{ matrix.extract-tool }}
|
||||
run: |
|
||||
$DOWNLOAD_TOOL http://olivevideoeditor.org/deps/oiio-$DEP_SUFFIX
|
||||
$EXTRACT_TOOL oiio-$DEP_SUFFIX
|
||||
|
||||
- name: Acquire Crashpad
|
||||
run: curl -fLOSs --retry 3 http://olivevideoeditor.org/deps/crashpad-win.7z && 7z x crashpad-win.7z
|
||||
shell: bash
|
||||
env:
|
||||
DEP_SUFFIX: ${{ matrix.dep-suffix }}
|
||||
EXTRACT_TOOL: ${{ matrix.extract-tool }}
|
||||
run: |
|
||||
$DOWNLOAD_TOOL http://olivevideoeditor.org/deps/crashpad-$DEP_SUFFIX
|
||||
$EXTRACT_TOOL 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=$BUILD_TYPE -G "Ninja"
|
||||
working-directory: ${{ runner.workspace }}/build
|
||||
env:
|
||||
CMAKE_GEN: ${{ matrix.cmake-gen }}
|
||||
run: |
|
||||
PATH=$GITHUB_WORKSPACE:$PATH \
|
||||
cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} -G "$CMAKE_GEN"
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
working-directory: ${{ runner.workspace }}/build
|
||||
shell: bash
|
||||
# Execute the build. You can specify a specific target with "--target <NAME>"
|
||||
run: cmake --build . --config $BUILD_TYPE
|
||||
run: |
|
||||
cmake --build .
|
||||
|
||||
- name: Test
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
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
|
||||
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
|
||||
working-directory: ${{ runner.workspace }}/build
|
||||
shell: bash
|
||||
env:
|
||||
MATRIX_OS: ${{ matrix.os }}
|
||||
run: |
|
||||
if [ "$MATRIX_OS" == "windows-latest" ]
|
||||
then
|
||||
mkdir olive-editor
|
||||
cp app/olive-editor.exe olive-editor
|
||||
cp app/olive-editor.pdb olive-editor
|
||||
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
|
||||
elif [ "$MATRIX_OS" == "macos-latest" ]
|
||||
then
|
||||
export BUNDLE_NAME=Olive.app
|
||||
mv app/$BUNDLE_NAME .
|
||||
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.*/
|
||||
fi
|
||||
|
||||
- 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: Deploy Packages
|
||||
working-directory: ${{ runner.workspace }}/build
|
||||
shell: bash
|
||||
env:
|
||||
MATRIX_OS: ${{ matrix.os }}
|
||||
run: |
|
||||
if [ "$MATRIX_OS" == "windows-latest" ]
|
||||
then
|
||||
# Create Installer Executable
|
||||
curl -fLOSs 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: Deploy Portable
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: cmd
|
||||
run: type NUL > olive-editor\portable && 7z a %PKGNAME%.zip olive-editor
|
||||
# Create Portable ZIP
|
||||
echo -n > olive-editor/portable
|
||||
7z a $PKGNAME.zip olive-editor
|
||||
elif [ "$MATRIX_OS" == "macos-latest" ]
|
||||
then
|
||||
zip -r $PKGNAME.zip Olive.app
|
||||
fi
|
||||
|
||||
- name: Upload Packages to Nightly Page
|
||||
shell: bash
|
||||
working-directory: ${{ runner.workspace }}/build
|
||||
env:
|
||||
GH_AUTH_KEY: ${{ secrets.GH_AUTH_KEY }}
|
||||
MATRIX_OS: ${{ matrix.os }}
|
||||
run: |
|
||||
if [ "$MATRIX_OS" == "windows-latest" ]
|
||||
then
|
||||
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
|
||||
elif [ "$MATRIX_OS" == "macos-latest" ]
|
||||
then
|
||||
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
|
||||
fi
|
||||
if: github.event_name != 'pull_request'
|
||||
|
||||
- name: Upload Installer Artifact to GitHub (Windows)
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ env.PKGNAME }}.exe
|
||||
path: ${{ runner.workspace }}/build/${{ env.PKGNAME }}.exe
|
||||
if: matrix.os == 'windows-latest'
|
||||
|
||||
- name: Upload Portable Artifact to GitHub (Windows)
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ env.PKGNAME }}.zip
|
||||
path: ${{ runner.workspace }}/build/${{ env.PKGNAME }}.zip
|
||||
if: matrix.os == 'windows-latest'
|
||||
|
||||
- name: Upload Artifact to GitHub (macOS)
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ env.PKGNAME }}.zip
|
||||
path: ${{ runner.workspace }}/build/${{ env.PKGNAME }}.zip
|
||||
if: matrix.os == 'macos-latest'
|
||||
|
||||
- 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
|
||||
working-directory: ${{ runner.workspace }}/build
|
||||
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: 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
|
||||
|
||||
- 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
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{env.PKGNAME}}.zip
|
||||
path: ${{runner.workspace}}/build/${{env.PKGNAME}}.zip
|
||||
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 == 'windows-latest'
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<StreamPtr> streams(fmt_ctx->nb_streams);
|
||||
|
||||
// Dump it into the Footage object
|
||||
for (unsigned int i=0;i<fmt_ctx->nb_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<AudioStream>();
|
||||
@@ -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),
|
||||
|
||||
@@ -81,7 +81,11 @@ bool InitializeCrashpad()
|
||||
// FIXME: On Linux, probably should put this in a subdir so that it doesn't conflict with
|
||||
// anything else in /usr/bin
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
base::FilePath handler(TO_BASE_STRING_TYPE(QDir(exe_dir).filePath(QStringLiteral("crashpad_handler.exe"))));
|
||||
#else
|
||||
base::FilePath handler(TO_BASE_STRING_TYPE(QDir(exe_dir).filePath(QStringLiteral("crashpad_handler"))));
|
||||
#endif
|
||||
|
||||
base::FilePath reports_dir(TO_BASE_STRING_TYPE(QDir(OLIVE_NAMESPACE::FileFunctions::GetTempFilePath()).filePath("reports")));
|
||||
|
||||
|
||||
+9
-1
@@ -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<ProjectImportTask*>(task)->GetCommand();
|
||||
ProjectImportTask* import_task = static_cast<ProjectImportTask*>(task);
|
||||
|
||||
QUndoCommand *command = import_task->GetCommand();
|
||||
|
||||
if (import_task->HasInvalidFiles()) {
|
||||
ProjectImportErrorDialog d(import_task->GetInvalidFiles(), main_window_);
|
||||
d.exec();
|
||||
}
|
||||
|
||||
undo_stack_.pushIfHasChildren(command);
|
||||
}
|
||||
|
||||
@@ -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;i<mixed_samples->audio_params().channel_count();i++) {
|
||||
// Mix samples that are in both buffers
|
||||
for (int j=0;j<min_samples;j++) {
|
||||
mixed_samples->data()[i][j] = PerformAll<float, float>(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;i<mixed_samples->audio_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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,6 +268,10 @@ void Sequence::set_parameters_from_footage(const QList<Footage *> footage)
|
||||
|
||||
foreach (Footage* f, footage) {
|
||||
foreach (StreamPtr s, f->streams()) {
|
||||
if (!s->enabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (s->type()) {
|
||||
case Stream::kVideo:
|
||||
{
|
||||
|
||||
@@ -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<rational>();
|
||||
|
||||
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,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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<double>(counter) / static_cast<double>(file_count_));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "importerrordialog.h"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QLabel>
|
||||
#include <QListWidget>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
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
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef PROJECTIMPORTERRORDIALOG_H
|
||||
#define PROJECTIMPORTERRORDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#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
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -75,6 +75,8 @@ protected:
|
||||
|
||||
virtual void showEvent(QShowEvent *event) override;
|
||||
|
||||
virtual void closeEvent(QCloseEvent* event) override;
|
||||
|
||||
signals:
|
||||
void DraggedByValue(int value, double multiplier);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<AudioStream>(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<PixelFormat::Format>(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<rational>(),
|
||||
static_cast<PixelFormat::Format>(Config::Current()["DefaultSequencePreviewFormat"].toInt()),
|
||||
Config::Current()["DefaultSequencePixelAspect"].value<rational>(),
|
||||
Config::Current()["DefaultSequenceInterlacing"].value<VideoParams::Interlacing>(),
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -47,12 +47,12 @@ private:
|
||||
|
||||
Footage* footage_;
|
||||
|
||||
Sequence sequence_;
|
||||
|
||||
VideoInput* video_node_;
|
||||
|
||||
AudioInput* audio_node_;
|
||||
|
||||
ViewerOutput* viewer_node_;
|
||||
|
||||
QHash<Footage*, int64_t> cached_timestamps_;
|
||||
|
||||
private slots:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -349,6 +349,22 @@ void MainWindow::ProjectClose(Project *p)
|
||||
CloseSequence(seq);
|
||||
}
|
||||
}
|
||||
|
||||
// Close any open footage in footage viewer
|
||||
QList<ItemPtr> footage = p->get_items_of_type(Item::kFootage);
|
||||
QList<Footage*> 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)
|
||||
|
||||
Reference in New Issue
Block a user