Merge remote-tracking branch 'origin/main'

This commit is contained in:
2026-07-13 17:05:22 +08:00
606 changed files with 135087 additions and 138186 deletions
+214 -28
View File
@@ -14,7 +14,7 @@ jobs:
# Windows Installer (MSYS2)
# ------------------------------------------------------------------
windows:
runs-on: warp-windows-latest-x64-16x
runs-on: warp-windows-latest-x64-32x
steps:
- uses: actions/checkout@v4
with:
@@ -57,21 +57,29 @@ jobs:
- name: Deploy dependencies
shell: msys2 {0}
run: |
mkdir -p app/packaging/windows/nsis/olive-editor
cp build/app/olive-editor.exe app/packaging/windows/nsis/olive-editor/
windeployqt6 app/packaging/windows/nsis/olive-editor/olive-editor.exe
# Copy all non-Qt MSYS2 DLLs recursively
cd app/packaging/windows/nsis/olive-editor
for l in $(ntldd -R olive-editor.exe | grep -E 'mingw64|ucrt64|clang64' | sed 's/^[ \t]*//' | cut -d' ' -f3); do
mkdir -p app/packaging/windows/nsis/oak-editor
cp build/app/oak-editor.exe app/packaging/windows/nsis/oak-editor/
cp build/app/oak-render-worker.exe app/packaging/windows/nsis/oak-editor/
cp build/app/oakgl.dll app/packaging/windows/nsis/oak-editor/
if [ -f build/app/oakvulkan.dll ]; then
cp build/app/oakvulkan.dll app/packaging/windows/nsis/oak-editor/
fi
windeployqt6 app/packaging/windows/nsis/oak-editor/oak-editor.exe
# Copy all non-Qt MSYS2 DLLs recursively for every binary we ship
cd app/packaging/windows/nsis/oak-editor
for binary in oak-editor.exe oak-render-worker.exe oakgl.dll oakvulkan.dll; do
[ -f "$binary" ] || continue
for l in $(ntldd -R "$binary" | grep -E 'mingw64|ucrt64|clang64' | sed 's/^[ \t]*//' | cut -d' ' -f3); do
cp -v "$l" .
done
done
- name: Build installer
shell: msys2 {0}
run: |
cd app/packaging/windows/nsis
cp "${GITHUB_WORKSPACE}"/LICENSE .
makensis olive.nsi
makensis oak.nsi
mv setup.exe "${GITHUB_WORKSPACE}"/Oak-Video-Editor-Windows-x64.exe
- name: Upload artifact
@@ -122,21 +130,37 @@ jobs:
- name: Deploy Qt dependencies
run: |
export PATH="$(brew --prefix qt@6)/bin:$PATH"
macdeployqt build/app/Olive.app
macdeployqt build/app/Oak.app \
-verbose=2 \
-executable=build/app/Oak.app/Contents/MacOS/oak-render-worker
# Re-sign after macdeployqt (it breaks signatures when modifying libs)
codesign --force --deep --sign - build/app/Olive.app
codesign --force --deep --sign - build/app/Oak.app
- name: Verify macOS bundle plugins
run: |
ls -la build/app/Oak.app/Contents/PlugIns/platforms/ || (echo "Missing platform plugins!" && exit 1)
ls -la build/app/Oak.app/Contents/PlugIns/imageformats/ || true
echo "=== otool -L main binary ==="
otool -L build/app/Oak.app/Contents/MacOS/Oak | head -30
echo "=== otool -L worker ==="
otool -L build/app/Oak.app/Contents/MacOS/oak-render-worker | head -30
- name: Bundle non-Qt libraries
run: |
mkdir -p build/app/Olive.app/Contents/Frameworks
mkdir -p build/app/Oak.app/Contents/Frameworks
python3 << 'PYEOF'
import os, shutil, subprocess
APP = "build/app/Olive.app"
BINARY = f"{APP}/Contents/MacOS/Olive"
APP = "build/app/Oak.app"
MACOS_DIR = f"{APP}/Contents/MacOS"
DEST = f"{APP}/Contents/Frameworks"
os.makedirs(DEST, exist_ok=True)
QT_PREFIXES = ("Qt", "libQt")
def is_qt_lib(name):
return name.startswith(QT_PREFIXES)
def get_rpaths(binary):
out = subprocess.run(["otool", "-l", binary], capture_output=True, text=True).stdout
rpaths = []
@@ -178,42 +202,55 @@ jobs:
return p
return None
EXEC_RPATHS = get_rpaths(BINARY)
PROCESSED = set()
def process(target):
def process(target, exec_rpaths):
tdir = os.path.dirname(target)
print(f"Processing: {target}")
for dep in get_deps(target):
resolved = resolve(dep, EXEC_RPATHS if target == BINARY else [], tdir)
# Skip Qt frameworks and Qt dylibs; macdeployqt already deploys them.
dep_base = os.path.basename(dep)
if is_qt_lib(dep_base):
continue
resolved = resolve(dep, exec_rpaths, tdir)
if not resolved or not os.path.isfile(resolved):
continue
resolved = os.path.realpath(resolved)
if not (resolved.startswith("/opt/homebrew/") or resolved.startswith("/usr/local/") or resolved.startswith(os.path.realpath(os.getcwd()))):
continue
base = os.path.basename(resolved)
if is_qt_lib(base):
continue
if base in PROCESSED:
subprocess.run(["install_name_tool", "-change", dep, f"@rpath/{base}", target], capture_output=True)
continue
PROCESSED.add(base)
dst = os.path.join(DEST, base)
if os.path.abspath(resolved) == os.path.abspath(dst):
# Already in Frameworks (likely copied by macdeployqt), just fix paths
subprocess.run(["install_name_tool", "-id", f"@rpath/{base}", dst], capture_output=True)
subprocess.run(["install_name_tool", "-change", dep, f"@rpath/{base}", target], capture_output=True)
process(dst)
process(dst, exec_rpaths)
continue
print(f" Copying: {resolved} -> {dst}")
shutil.copy2(resolved, dst)
subprocess.run(["install_name_tool", "-id", f"@rpath/{base}", dst], capture_output=True)
subprocess.run(["install_name_tool", "-change", dep, f"@rpath/{base}", target], capture_output=True)
process(dst)
process(dst, exec_rpaths)
process(BINARY)
binaries = [os.path.join(MACOS_DIR, f) for f in os.listdir(MACOS_DIR)
if os.path.isfile(os.path.join(MACOS_DIR, f))]
if not binaries:
raise RuntimeError(f"No binaries found in {MACOS_DIR}")
for rp in EXEC_RPATHS:
subprocess.run(["install_name_tool", "-delete_rpath", rp, BINARY], capture_output=True)
subprocess.run(["install_name_tool", "-add_rpath", "@executable_path/../Frameworks", BINARY], capture_output=True)
main_binary = os.path.join(MACOS_DIR, "Oak")
main_rpaths = get_rpaths(main_binary)
for binary in binaries:
process(binary, main_rpaths)
# Ensure every binary can find libraries in Frameworks
for rp in get_rpaths(binary):
subprocess.run(["install_name_tool", "-delete_rpath", rp, binary], capture_output=True)
subprocess.run(["install_name_tool", "-add_rpath", "@executable_path/../Frameworks", binary], capture_output=True)
subprocess.run(["codesign", "--force", "--deep", "--sign", "-", APP], check=True)
print("Done. Frameworks:")
@@ -224,16 +261,19 @@ jobs:
- name: Debug bundle contents
run: |
echo "=== otool -L ==="
otool -L build/app/Olive.app/Contents/MacOS/Olive
otool -L build/app/Oak.app/Contents/MacOS/Oak
echo "=== Frameworks dir ==="
ls -la build/app/Olive.app/Contents/Frameworks/ || echo "(empty)"
ls -la build/app/Oak.app/Contents/Frameworks/ || echo "(empty)"
echo "=== App bundle size ==="
du -sh build/app/Olive.app
du -sh build/app/Oak.app
- name: Create DMG
run: |
mkdir -p build/app/dmg-staging
cp -R build/app/Oak.app build/app/dmg-staging/
ln -s /Applications build/app/dmg-staging/Applications
hdiutil create \
-srcfolder build/app/Olive.app \
-srcfolder build/app/dmg-staging \
-volname "Oak Video Editor" \
-fs HFS+ \
-format UDZO \
@@ -317,11 +357,157 @@ jobs:
name: oak-linux-appimage
path: Oak_Video_Editor-*.AppImage
# ------------------------------------------------------------------
# Debian package (.deb)
# ------------------------------------------------------------------
deb:
runs-on: warp-ubuntu-latest-x64-16x
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
cmake ninja-build pkg-config \
qt6-base-dev qt6-base-dev-tools qt6-base-private-dev qt6-tools-dev qt6-tools-dev-tools \
libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libavfilter-dev \
libopencolorio-dev libopenimageio-dev libopenexr-dev libexpat1-dev \
portaudio19-dev libgl1-mesa-dev libxkbcommon-dev
- name: Build
run: |
cmake -S . -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr \
-DBUILD_QT6=ON \
-DCMAKE_DISABLE_FIND_PACKAGE_Vulkan=ON
cmake --build build
- name: Create DEB package
run: |
cd build
cpack -G DEB
mv *.deb "${GITHUB_WORKSPACE}/"
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: oak-deb-package
path: oak-video-editor_*.deb
# ------------------------------------------------------------------
# RPM package (.rpm)
# ------------------------------------------------------------------
rpm:
runs-on: warp-ubuntu-latest-x64-16x
container:
image: fedora:latest
steps:
- name: Install Git
run: dnf install -y git
- uses: actions/checkout@v4
with:
submodules: true
- name: Install dependencies
run: |
dnf install -y \
cmake ninja-build pkgconf-pkg-config \
qt6-qtbase-devel qt6-qtbase-private-devel qt6-qttools-devel \
ffmpeg-free-devel \
OpenImageIO-devel \
OpenColorIO-devel \
openexr-devel \
expat-devel \
portaudio-devel \
mesa-libGL-devel \
vulkan-headers \
vulkan-loader-devel \
libxkbcommon-devel \
gcc-c++ \
bzip2-devel \
rpm-build
- name: Build
run: |
cmake -S . -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr \
-DBUILD_QT6=ON \
-DCMAKE_DISABLE_FIND_PACKAGE_Vulkan=ON
cmake --build build
- name: Create RPM package
run: |
cd build
cpack -G RPM
mv *.rpm "${GITHUB_WORKSPACE}/"
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: oak-rpm-package
path: oak-video-editor-*.rpm
# ------------------------------------------------------------------
# Arch Linux package (.pkg.tar.zst)
# ------------------------------------------------------------------
archlinux:
runs-on: warp-ubuntu-latest-x64-16x
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Prepare source tarball and PKGBUILD
run: |
VERSION="${GITHUB_REF_NAME#v}"
# Arch pkgver cannot contain hyphens
PKGVER="${VERSION//-/_}"
TARBALL="oak-video-editor-${PKGVER}.tar.gz"
tar czf "/tmp/${TARBALL}" \
--transform "s,^,oak-video-editor-${PKGVER}/," \
--exclude='.git' \
--exclude='build' \
--exclude='*.tar.gz' \
.
mv "/tmp/${TARBALL}" "${TARBALL}"
sed "s/@VERSION@/${PKGVER}/g" app/packaging/arch/PKGBUILD.in > PKGBUILD
- name: Build package in Arch Linux container
run: |
docker run --rm \
-v "${PWD}:/build" \
-w /build \
archlinux:base-devel \
bash -c '
set -e
pacman-key --init
pacman -Syu --noconfirm
pacman -S --noconfirm --needed \
cmake ninja git pkgconf \
qt6-base qt6-tools ffmpeg openimageio opencolorio openexr expat portaudio \
mesa vulkan-icd-loader libxkbcommon fmt
useradd -m builder
chown -R builder /build
su builder -c "makepkg -s --noconfirm"
'
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: oak-arch-package
path: oak-video-editor-*.pkg.tar.zst
# ------------------------------------------------------------------
# Create Draft Release
# ------------------------------------------------------------------
release:
needs: [windows, macos, appimage]
needs: [windows, macos, appimage, deb, rpm, archlinux]
runs-on: ubuntu-latest
steps:
- name: Download all artifacts
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [warp-ubuntu-latest-x64-16x, warp-macos-15-arm64-12x, warp-windows-latest-x64-16x]
os: [warp-ubuntu-latest-x64-16x, warp-macos-15-arm64-12x, warp-windows-latest-x64-32x]
env:
CMAKE_BUILD_TYPE: Release
# Enable OFX integration tests that require external plugin bundles
+1 -1
View File
@@ -1,5 +1,5 @@
# CMake artifacts
/build*/
/cmake-build-*
build
# Doxygen
@@ -1,25 +0,0 @@
From 6b0ef44eb189411d36c739ccde8a081a3e62034a Mon Sep 17 00:00:00 2001
From: Mike Solar <iam@mikesolar.com>
Date: Mon, 24 Nov 2025 20:57:12 +0800
Subject: [PATCH] Fix include path for Window_p.h in qtcommon directory
---
src/qtcommon/Window_p.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/qtcommon/Window_p.h b/src/qtcommon/Window_p.h
index f29b209a5..d1d6b47ad 100644
--- a/src/qtcommon/Window_p.h
+++ b/src/qtcommon/Window_p.h
@@ -11,7 +11,7 @@
#pragma once
-#include "core/Window_p.h"
+#include "../core/Window_p.h"
#include "Screen_p.h"
#include <QPointer>
--
2.52.0
+30 -2
View File
@@ -17,8 +17,8 @@
cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
project(olive-editor VERSION 0.3.0 LANGUAGES CXX)
set(PROJECT_VERSION "0.3.0-alpha")
project(olive-editor VERSION 0.4.0 LANGUAGES CXX)
set(PROJECT_VERSION "0.4.0-alpha")
set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -DOFX_SUPPORTS_OPENGLRENDER)
@@ -337,3 +337,31 @@ if (BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
# ------------------------------------------------------------------------------
# CPack / system package configuration
# ------------------------------------------------------------------------------
set(CPACK_PACKAGE_NAME "oak-video-editor")
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
set(CPACK_PACKAGE_VENDOR "Oak Video Editor Team")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Oak - Non-linear video editor")
set(CPACK_PACKAGE_HOMEPAGE_URL "https://oakvideoeditor.org")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
set(CPACK_PACKAGING_INSTALL_PREFIX "/usr")
# Debian
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Oak Video Editor Team")
set(CPACK_DEBIAN_FILE_NAME "DEB-DEFAULT")
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS OFF)
set(CPACK_DEBIAN_PACKAGE_DEPENDS
"libqt6core6, libqt6gui6, libqt6widgets6, libqt6opengl6, libqt6openglwidgets6, libqt6network6, libqt6concurrent6, libavcodec60, libavformat60, libavutil58, libswscale7, libswresample4, libavfilter9, libopenimageio2.4, libopencolorio2, libopenexr-3-1-30, libexpat1, libportaudio2, libgl1, libxkbcommon0")
# RPM
set(CPACK_RPM_PACKAGE_LICENSE "GPLv3")
set(CPACK_RPM_PACKAGE_GROUP "Applications/Multimedia")
set(CPACK_RPM_PACKAGE_URL "https://oakvideoeditor.org")
set(CPACK_RPM_PACKAGE_REQUIRES
"qt6-qtbase >= 6.0, qt6-qtbase-gui >= 6.0, qt6-qttools, ffmpeg-libs >= 6.0, OpenImageIO >= 2.4, OpenColorIO >= 2.0, openexr >= 3.1, expat, portaudio, mesa-libGL, libxkbcommon")
include(CPack)
+7 -12
View File
@@ -1,12 +1,6 @@
# Contributing to Olive
# Contributing to Oak
Thank you for your interest in contributing to Olive!
## Reporting issues
Bug reports help to make the software more stable and usable.
Please read the pinned [issue #1175](https://github.com/olive-editor/olive/issues/1175)
for guidelines before you create a new issue.
Thank you for your interest in contributing to Oak Video Editor!
## Writing code
@@ -22,9 +16,11 @@ In order to keep the code as readable and maintainable as possible, code
submitted should abide by the following standards:
* The code style generally follows the
[Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html)
including, but not limited to:
* Indentation is 4 spaces wide, spaces only (no tabs)
[Linux Kernel Coding Style](https://www.kernel.org/doc/html/latest/process/coding-style.html)
with the following project-specific exceptions and notes:
* Indentation uses **tabs**, not spaces.
* Documentation comments should use **Javadoc-style** (`/** ... */`) where appropriate.
* The naming rules below are retained from the original Olive codebase:
* `lowercase_underscored_variable_names`
* `lowercase_underscored_functions()` or `SentenceCaseFunctions()`
* `class SentenceCaseClassesAndStructs {}`
@@ -33,4 +29,3 @@ submitted should abide by the following standards:
* `class_member_variables_` end with a `_`
* 100 column limit (where it doesn't impair readability)
* Unix line endings (only LF no CRLF)
* Javadoc documentation where appropriate
+5 -6
View File
@@ -1,10 +1,8 @@
# Oak 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)
# Oak Video Editor[![CI](https://github.com/OakVideoEditorCommunity/oak/actions/workflows/ci.yml/badge.svg)](https://github.com/OakVideoEditorCommunity/oak/actions/workflows/ci.yml)
[中文](docs/zh/README.md)
Oak Video Editor is a free non-linear video editor for Windows, macOS, and Linux.
Unfortunately, the original author has not submitted code updates for over 7 months, and no public contact information (email or otherwise) is available to reach them directly.
This project is a community-maintained fork of Olive Video Editor.
![screen](https://olivevideoeditor.org/img/020-2.png)
@@ -12,9 +10,10 @@ This project is a community-maintained fork of Olive Video Editor.
**NOTE: Oak Video Editor 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
The original author compiled following binaries:
- [0.1.0 alpha](https://github.com/olive-editor/olive/releases/tag/0.1.0)
- [0.2.0 unstable development build](https://github.com/olive-editor/olive/releases/tag/0.2.0-nightly)
The binary can be downloaded here:
[v0.3.0](https://github.com/OakVideoEditorCommunity/oak/releases/tag/v0.3.0-alpha)
## Building from Source
-90
View File
@@ -1,90 +0,0 @@
# TODO
## 目标
- 实现 2–3 秒预渲染的 LRU 缓存和代理剪辑功能,并以“小步快跑”的方式在现有架构中逐步落地,确保每一步都可编译。
## 现有架构中的落点
- 播放/渲染调度:`app/render/renderprocessor.cpp`, `app/render/plugin/pluginrenderer.cpp`, `app/node/traverser.cpp`
- 插件节点输入/默认值:`app/node/plugins/Plugin.cpp`
- Clip 图像/纹理获取:`app/pluginSupport/OliveClip.cpp`, `app/pluginSupport/OliveClip.h`
- 节点与值系统:`app/node/node.h`, `app/node/node.cpp`, `app/node/value.h`
- 工程序列化:`app/node/project/serializer/*`
## LRU 缓存计划(代码改动 + 集成点)
### 步骤 1(可编译):新增缓存类型但不接入逻辑
- 新增缓存模块,例如 `app/render/cache/framecache.h/.cpp`
- 定义:
- `FrameCacheKey`(图哈希/版本、时间、参数、代理模式、渲染缩放)。
- `FrameCacheEntry`AVFrame 或 Texture + 元信息 + 字节数 + 最近访问时间)。
- `FrameCache` API`get(key)``put(key, entry)``invalidateByVersion(version)`
- 先只编译通过,不改变行为。
### 步骤 2(可编译):图版本号/失效机制
-`Node` 或渲染入口维护图版本号。
- 当参数变化、连线变化时递增。
- 渲染侧可读取版本号用于缓存失效。
### 步骤 3(小行为):仅缓存当前帧
-`renderprocessor.cpp` 播放路径上:
- 先查缓存,命中则直接显示。
- 未命中则正常渲染,并写入缓存。
- 缓存预算先设很小,风险低。
### 步骤 4(小行为):预渲染窗口
- 增加队列,渲染 [now, now+N]N=23 秒。
- 并发限制(例如 2–3 个任务),避免抢 UI。
- 优先级:当前帧 > 近未来。
- Seek 时取消/丢弃过期任务。
### 步骤 5(行为):LRU 淘汰
- 按内存预算/帧数上限淘汰最久未使用。
### 步骤 6(行为):CPU/GPU 策略
- 默认缓存 CPU 帧,播放时再上传 GPU。
- GPU 缓存可作为后续优化开关。
### 步骤 7(可观测性)
- 统计命中率、平均渲染耗时、掉帧。
- Debug 构建下输出日志。
## 代理剪辑计划(代码改动 + 集成点)
### 步骤 1(可编译):数据模型与序列化
- 在 clip 元数据里增加:
- `proxy_path``proxy_width``proxy_height``proxy_codec``proxy_fps`
-`app/node/project/serializer/*` 写入/读取。
### 步骤 2(小行为):代理选择策略
- 增加全局/每 clip 的代理模式:
- `Auto``ForceProxy``ForceOriginal`
- 在媒体解析层根据模式决定用原片还是代理。
### 步骤 3(行为):代理生成
- 新增后台转码任务(复用现有渲染/导出流程)。
- 生成完成后更新元数据。
### 步骤 4(行为):UI 接入
- 增加“生成代理”“重链接代理”入口。
- 在剪辑或预览上显示代理标识。
### 步骤 5(验证)
- 对比代理与原片的时间精度、音画同步。
- 导出默认使用原片。
## 小步快跑执行顺序(每步可编译)
1) 新增缓存模块/类型(不接入)。
2) 增加图版本号与失效接口。
3) 播放路径只缓存当前帧。
4) 预渲染 2–3 秒窗口 + 并发限制。
5) LRU 淘汰策略。
6) 图版本变更触发失效。
7) 统计与日志。
8) 代理元数据字段 + 序列化。
9) 代理选择策略(Auto/Force)。
10) 代理生成任务 + UI 入口。
## 待确认问题
- 缓存预算默认值(按硬件分级)。
- 代理文件默认存储路径。
- 是否做 GPU 纹理缓存。
-92
View File
@@ -1,92 +0,0 @@
# TODO
## Goal
- Add an LRU prerender cache (23 seconds ahead) and proxy clip support, implemented as incremental, compile-safe steps within the current architecture.
## Where the Changes Live (Current Architecture)
- Playback/render scheduling: `app/render/renderprocessor.cpp`, `app/render/plugin/pluginrenderer.cpp`, `app/node/traverser.cpp`
- Plugin node inputs/defaults: `app/node/plugins/Plugin.cpp`
- Clip image/texture fetch: `app/pluginSupport/OliveClip.cpp`, `app/pluginSupport/OliveClip.h`
- Node graph and values: `app/node/node.h`, `app/node/node.cpp`, `app/node/value.h`
- Project/serialization: `app/node/project/serializer/*`
## LRU Cache Plan (Code Changes + Integration Points)
### Step 1 (compile-safe): Introduce cache data types (no behavior yet)
- Add a small cache module, e.g. `app/render/cache/framecache.h/.cpp`.
- Define:
- `FrameCacheKey` (graph version/hash, time, params, proxy mode, render scale).
- `FrameCacheEntry` (AVFrame or Texture + metadata + byte size + last-used).
- `FrameCache` API: `get(key)`, `put(key, entry)`, `invalidateByVersion(version)`.
- Wire in a compile-only stub with no runtime usage.
### Step 2 (compile-safe): Define graph/version invalidation hook
- Add a lightweight “graph version” counter to `Node` or a render pipeline owner.
- Increment on param changes and graph edits.
- Expose a read-only version getter for the render pipeline.
### Step 3 (small behavior): Cache current frame only
- In `renderprocessor.cpp` playback path, check cache before rendering:
- If hit, present cached frame.
- If miss, render normally and `put` into cache.
- Keep budget small (few frames) to minimize risk.
### Step 4 (small behavior): Pre-render window scheduling
- Add a render queue for time range [now, now+N] (N = 23s).
- Limit worker count (e.g., 23 tasks) to avoid UI starvation.
- Prioritize current frame > near future.
- On seek, cancel or drop stale tasks.
### Step 5 (behavior): LRU eviction policy
- Enforce memory budget and frame count cap.
- Evict least-recently-used entries.
### Step 6 (behavior): GPU/CPU policy
- Cache CPU frames by default for safety.
- For GL outputs, upload from cached CPU frame when displayed.
- Optionally add GPU caching later behind a feature flag.
### Step 7 (observability)
- Add counters for hit rate, average render time, and drops.
- Log only in debug builds.
## Proxy Clip Plan (Code Changes + Integration Points)
### Step 1 (compile-safe): Data model + serialization
- Extend clip metadata with:
- `proxy_path`, `proxy_width`, `proxy_height`, `proxy_codec`, `proxy_fps`.
- Add read/write in `app/node/project/serializer/*`.
### Step 2 (small behavior): Proxy selection policy
- Add project-level and clip-level proxy mode:
- `Auto`, `ForceProxy`, `ForceOriginal`.
- Add a simple resolver in clip/media source code that picks proxy if enabled.
### Step 3 (behavior): Proxy generation pipeline
- Add a background task to build proxies (using existing render/export tasks).
- Store output path and metadata on success.
### Step 4 (behavior): UI wiring
- Add “Generate Proxy” action + proxy indicator.
- Add “Relink Proxy” dialog.
### Step 5 (validation)
- Compare proxy vs original for timing and sync.
- Ensure proxies are ignored for export unless explicitly enabled.
## Small-Step Implementation Plan (Each Step Builds)
1) Add cache module + types (no references).
2) Add graph version counter (increment on changes).
3) Wire cache lookup for current frame only.
4) Add prerender queue (23 seconds) with limited concurrency.
5) Add LRU eviction + memory budget.
6) Add cache invalidation on graph version change.
7) Add basic metrics/logging.
8) Add proxy metadata fields + serialization.
9) Add proxy selection policy (Auto/Force modes).
10) Add proxy generation task + UI entry points.
## Open Questions
- Default cache size per hardware tier.
- Where to store proxy files on disk.
- Whether to cache GPU textures or CPU frames only.
+60 -30
View File
@@ -47,10 +47,10 @@ add_subdirectory(window)
qt_add_translation(OLIVE_QM_FILES ${OLIVE_TS_FILES})
set(QRC_BODY "")
foreach(QM_FILE ${OLIVE_QM_FILES})
foreach (QM_FILE ${OLIVE_QM_FILES})
get_filename_component(QM_FILENAME_COMPONENT ${QM_FILE} NAME_WE)
string(APPEND QRC_BODY "<file alias=\"${QM_FILENAME_COMPONENT}\">${QM_FILE}</file>\n")
endforeach()
endforeach ()
configure_file(ts/translations.qrc.in ts/translations.qrc @ONLY)
set(OLIVE_RESOURCES
@@ -69,7 +69,7 @@ add_library(olive-version-obj
version.h
)
target_link_libraries(olive-version-obj PRIVATE Qt${QT_VERSION_MAJOR}::Core)
target_compile_options(olive-version-obj PRIVATE -DAPPVERSION="${PROJECT_VERSION}" -DAPPVERSIONLONG="${PROJECT_LONG_VERSION}" )
target_compile_options(olive-version-obj PRIVATE -DAPPVERSION="${PROJECT_VERSION}" -DAPPVERSIONLONG="${PROJECT_LONG_VERSION}")
# Add main library
add_library(libolive-editor
@@ -92,11 +92,11 @@ if (OAK_ENABLE_DYNAMIC_RENDER_BACKEND)
set_target_properties(libolive-editor PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_compile_definitions(libolive-editor PRIVATE OAK_ENABLE_DYNAMIC_RENDER_BACKEND)
foreach(target olivecore kddockwidgets)
foreach (target olivecore kddockwidgets)
if (TARGET ${target})
set_target_properties(${target} PROPERTIES POSITION_INDEPENDENT_CODE ON)
endif()
endforeach()
endif ()
endforeach ()
# Render core library: the minimal set of code required by the OpenGL/Vulkan backend
# libraries. Keeping this separate from libolive-editor prevents the backends from
@@ -155,13 +155,16 @@ if (OAK_ENABLE_DYNAMIC_RENDER_BACKEND)
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
if (WIN32)
set_target_properties(oakgl PROPERTIES PREFIX "")
endif ()
install(TARGETS oakgl
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
if(Vulkan_FOUND)
if (Vulkan_FOUND)
add_library(oakvulkan SHARED
render/vulkan/vulkanbackend_c.cpp
render/vulkan/vulkanrenderer.cpp
@@ -170,24 +173,27 @@ if (OAK_ENABLE_DYNAMIC_RENDER_BACKEND)
target_link_libraries(oakvulkan PRIVATE libolive-rendercore)
target_link_libraries(oakvulkan PRIVATE Vulkan::Vulkan)
target_compile_definitions(oakvulkan PRIVATE OAK_HAS_VULKAN)
if(SHADERC_FOUND)
if (SHADERC_FOUND)
target_link_libraries(oakvulkan PRIVATE ${SHADERC_LIBRARIES})
target_include_directories(oakvulkan PRIVATE ${SHADERC_INCLUDE_DIRS})
target_compile_definitions(oakvulkan PRIVATE OAK_HAS_SHADERC)
endif()
endif ()
set_target_properties(oakvulkan PROPERTIES
OUTPUT_NAME oakvulkan
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
if (WIN32)
set_target_properties(oakvulkan PROPERTIES PREFIX "")
endif ()
install(TARGETS oakvulkan
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
endif()
endif()
endif ()
endif ()
add_library(oakgl-cabi-check OBJECT
render/opengl/openglbackend_c.cpp
@@ -202,7 +208,7 @@ target_link_libraries(oakgl-cabi-check PRIVATE ${OLIVE_LIBRARIES} OfxHost)
target_compile_definitions(oakgl-cabi-check PRIVATE ${OLIVE_DEFINITIONS})
target_compile_options(oakgl-cabi-check PRIVATE ${OLIVE_COMPILE_OPTIONS})
if(Vulkan_FOUND)
if (Vulkan_FOUND)
add_library(oakvulkan-cabi-check OBJECT
render/vulkan/vulkanbackend_c.cpp
render/vulkan/vulkanrenderer.cpp
@@ -217,14 +223,14 @@ if(Vulkan_FOUND)
target_link_libraries(oakvulkan-cabi-check PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets)
target_link_libraries(oakvulkan-cabi-check PRIVATE Vulkan::Vulkan)
target_compile_definitions(oakvulkan-cabi-check PRIVATE OAK_HAS_VULKAN)
if(SHADERC_FOUND)
if (SHADERC_FOUND)
target_link_libraries(oakvulkan-cabi-check PRIVATE ${SHADERC_LIBRARIES})
target_include_directories(oakvulkan-cabi-check PRIVATE ${SHADERC_INCLUDE_DIRS})
target_compile_definitions(oakvulkan-cabi-check PRIVATE OAK_HAS_SHADERC)
endif()
endif ()
target_compile_definitions(oakvulkan-cabi-check PRIVATE ${OLIVE_DEFINITIONS})
target_compile_options(oakvulkan-cabi-check PRIVATE ${OLIVE_COMPILE_OPTIONS})
endif()
endif ()
# Add application
add_executable(olive-editor
@@ -239,10 +245,14 @@ if (OAK_ENABLE_DYNAMIC_RENDER_BACKEND)
add_dependencies(olive-editor oakgl)
if (TARGET oakvulkan)
add_dependencies(olive-editor oakvulkan)
endif()
endif()
endif ()
endif ()
# Ensure the render worker is always built together with the editor on every platform.
add_dependencies(olive-editor olive-render-worker)
# Add render worker process (olive-render-worker).
set_target_properties(olive-editor PROPERTIES OUTPUT_NAME "oak-editor")
# Add render worker process (oak-render-worker).
# Reuses the libolive-editor object library so the worker shares the exact same render/node/codec
# code as the editor. It is a headless app that owns its own offscreen GL context. The link set is
# currently the full OLIVE_LIBRARIES for simplicity; trimming UI-only dependencies is a later-phase
@@ -252,6 +262,11 @@ add_executable(olive-render-worker
$<TARGET_OBJECTS:libolive-editor>
$<TARGET_OBJECTS:olive-version-obj>
)
set_target_properties(olive-render-worker PROPERTIES OUTPUT_NAME "oak-render-worker")
if (APPLE)
target_sources(olive-render-worker PRIVATE render/worker/workermain_mac.mm)
target_link_libraries(olive-render-worker PRIVATE "-framework Cocoa")
endif ()
target_include_directories(olive-render-worker PUBLIC pluginSupport)
target_link_libraries(olive-render-worker PUBLIC OfxHost)
if (OAK_ENABLE_DYNAMIC_RENDER_BACKEND)
@@ -259,16 +274,16 @@ if (OAK_ENABLE_DYNAMIC_RENDER_BACKEND)
add_dependencies(olive-render-worker oakgl)
if (TARGET oakvulkan)
add_dependencies(olive-render-worker oakvulkan)
endif()
endif()
endif ()
endif ()
# Create docs if doxygen was found
if(DOXYGEN_FOUND)
if (DOXYGEN_FOUND)
set(DOXYGEN_PROJECT_NAME "Oak Video Editor")
set(DOXYGEN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/docs")
set(DOXYGEN_EXTRACT_ALL "YES")
set(DOXYGEN_EXTRACT_PRIVATE "YES")
doxygen_add_docs(docs ALL ${OLIVE_SOURCES})
endif()
endif ()
# Platform-specific deployment preferences
if (WIN32)
@@ -278,9 +293,9 @@ if (WIN32)
# Preserve folder structure in visual studio
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${OLIVE_SOURCES})
elseif(APPLE)
elseif (APPLE)
# Set Mac application icon
set(OLIVE_ICON packaging/macos/olive.icns)
set(OLIVE_ICON packaging/macos/oak.icns)
target_sources(olive-editor PRIVATE ${OLIVE_ICON})
# Set Mac bundle properties
@@ -288,19 +303,34 @@ elseif(APPLE)
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/packaging/macos/MacOSXBundleInfo.plist.in
MACOSX_BUNDLE_GUI_IDENTIFIER org.oakvideoeditor.Oak
MACOSX_BUNDLE_ICON_FILE olive.icns
MACOSX_BUNDLE_ICON_FILE oak.icns
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION}
MACOSX_BUNDLE_BUNDLE_NAME "Oak Video Editor"
MACOSX_BUNDLE_INFO_STRING "Oak Video Editor ${PROJECT_LONG_VERSION}"
MACOSX_BUNDLE_COPYRIGHT "©2018-2021 Olive Studios LLC and others. Fork maintained by Oak Video Editor Team."
RESOURCE "${OLIVE_ICON}"
OUTPUT_NAME "Olive"
OUTPUT_NAME "Oak"
)
elseif(UNIX)
# Copy the render worker and dynamic render backends into the app bundle.
# They are looked up in QCoreApplication::applicationDirPath(), which on
# macOS points to Oak.app/Contents/MacOS.
add_custom_command(TARGET olive-editor POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory $<TARGET_BUNDLE_DIR:olive-editor>/Contents/MacOS
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:olive-render-worker> $<TARGET_BUNDLE_DIR:olive-editor>/Contents/MacOS/
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:oakgl> $<TARGET_BUNDLE_DIR:olive-editor>/Contents/MacOS/
COMMENT "Copying oak-render-worker and render backends into Oak.app"
)
if (TARGET oakvulkan)
add_custom_command(TARGET olive-editor POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:oakvulkan> $<TARGET_BUNDLE_DIR:olive-editor>/Contents/MacOS/
)
endif ()
elseif (UNIX)
# Set Linux-specific properties for application
install(TARGETS olive-editor RUNTIME DESTINATION bin)
endif()
endif ()
# Set link libraries
target_link_libraries(olive-editor PRIVATE ${OLIVE_LIBRARIES})
@@ -325,9 +355,9 @@ target_include_directories(olive-render-worker PRIVATE ${OLIVE_INCLUDE_DIRS})
# Install the render worker alongside the editor on Linux.
if (UNIX AND NOT APPLE)
install(TARGETS olive-render-worker RUNTIME DESTINATION bin)
endif()
endif ()
# Add crash handler
if (GoogleCrashpad_FOUND AND Qt${QT_VERSION_MAJOR}Network_FOUND)
add_subdirectory(crashhandler)
endif()
endif ()
+4 -3
View File
@@ -57,7 +57,8 @@ AudioLevelMeter::AnalyzeSampleBuffer(const core::SampleBuffer &samples)
square_sum += value * value;
}
const double mean_square = square_sum / static_cast<double>(sample_count);
const double mean_square =
square_sum / static_cast<double>(sample_count);
const double rms = std::sqrt(mean_square);
ChannelStats channel_stats;
@@ -74,8 +75,8 @@ AudioLevelMeter::AnalyzeSampleBuffer(const core::SampleBuffer &samples)
}
stats.silence = qFuzzyIsNull(stats.max_peak_linear);
stats.integrated_lufs = PowerToLufs(
total_square / static_cast<double>(total_samples));
stats.integrated_lufs =
PowerToLufs(total_square / static_cast<double>(total_samples));
return stats;
}
+4 -5
View File
@@ -28,8 +28,7 @@ AudioSynchronizer::Placement AudioSynchronizer::PlaceBySourceTime(
const core::rational &reference_timeline_in)
{
Placement placement;
if (!reference.has_source_start_time ||
!candidate.has_source_start_time ||
if (!reference.has_source_start_time || !candidate.has_source_start_time ||
reference.source_start_time.isNaN() ||
candidate.source_start_time.isNaN()) {
return placement;
@@ -55,9 +54,9 @@ AudioSynchronizer::Placement AudioSynchronizer::PlaceByWaveformOffset(
return placement;
}
placement.timeline_in =
reference_timeline_in +
core::rational::fromDouble(static_cast<double>(candidate_offset_samples) /
placement.timeline_in = reference_timeline_in +
core::rational::fromDouble(
static_cast<double>(candidate_offset_samples) /
static_cast<double>(sample_rate));
placement.valid = !placement.timeline_in.isNaN();
return placement;
+4 -4
View File
@@ -41,12 +41,12 @@ public:
bool valid = false;
};
static Placement PlaceBySourceTime(const SourceClip &reference,
const SourceClip &candidate,
static Placement
PlaceBySourceTime(const SourceClip &reference, const SourceClip &candidate,
const core::rational &reference_timeline_in);
static Placement PlaceByWaveformOffset(
const core::rational &reference_timeline_in,
static Placement
PlaceByWaveformOffset(const core::rational &reference_timeline_in,
int64_t candidate_offset_samples, int sample_rate);
};
+3 -3
View File
@@ -96,7 +96,8 @@ AudioWaveformSync::OffsetResult AudioWaveformSync::EstimateEnvelopeOffset(
int64_t best_lag = 0;
for (int64_t lag = -max_offset_windows; lag <= max_offset_windows; lag++) {
const int reference_start = static_cast<int>(std::max<int64_t>(0, -lag));
const int reference_start =
static_cast<int>(std::max<int64_t>(0, -lag));
const int candidate_start = static_cast<int>(std::max<int64_t>(0, lag));
const int overlap = std::min(reference.size() - reference_start,
candidate.size() - candidate_start);
@@ -142,8 +143,7 @@ AudioWaveformSync::OffsetResult AudioWaveformSync::EstimateEnvelopeOffset(
if (best_score > -2.0) {
result.valid = true;
result.confidence = std::max(0.0, best_score);
result.offset_samples =
best_lag * static_cast<int64_t>(window_samples);
result.offset_samples = best_lag * static_cast<int64_t>(window_samples);
}
return result;
+2 -2
View File
@@ -38,8 +38,8 @@ public:
bool valid = false;
};
static QVector<double>
ExtractRmsEnvelope(const core::SampleBuffer &samples, size_t window_samples);
static QVector<double> ExtractRmsEnvelope(const core::SampleBuffer &samples,
size_t window_samples);
static OffsetResult EstimateOffset(const core::SampleBuffer &reference,
const core::SampleBuffer &candidate,
+1 -1
View File
@@ -32,7 +32,7 @@ extern "C" {
#include <QWaitCondition>
#include <stdint.h>
#include "codec/frame.h"
#include "codec/frame.h"
#include "node/block/block.h"
#include "node/project/footage/footagedescription.h"
#include "render/cancelatom.h"
+81 -64
View File
@@ -25,11 +25,11 @@ extern "C" {
#include <libavutil/pixdesc.h>
}
namespace olive {
namespace olive
{
static FramePtr CopyPackedAVFrameToFrame(const AVFramePtr &src,
PixelFormat format,
int channel_count,
PixelFormat format, int channel_count,
const rational &timestamp)
{
if (!src || !src->data[0]) {
@@ -48,8 +48,7 @@ static FramePtr CopyPackedAVFrameToFrame(const AVFramePtr &src,
VideoParams::GetBytesPerPixel(format, channel_count);
for (int y = 0; y < frame->height(); y++) {
memcpy(frame->data() + y * frame->linesize_bytes(),
src->data[0] + y * src->linesize[0],
size_t(row_bytes));
src->data[0] + y * src->linesize[0], size_t(row_bytes));
}
return frame;
@@ -97,7 +96,8 @@ namespace olive
QVariant Yuv2RgbShader;
QVariant DeinterlaceShader;
namespace {
namespace
{
constexpr int64_t kAnalyzeDurationUs = 5000000;
constexpr int64_t kProbeSizeBytes = 20000000;
@@ -133,8 +133,9 @@ void DiscardSubtitleStreams(AVFormatContext *ctx)
}
}
TimecodeMetadata::SourceTime ExtractSourceStartTime(
AVDictionary *metadata, const rational &timebase, int sample_rate)
TimecodeMetadata::SourceTime ExtractSourceStartTime(AVDictionary *metadata,
const rational &timebase,
int sample_rate)
{
if (!metadata) {
return TimecodeMetadata::SourceTime();
@@ -423,7 +424,7 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
// Perform any CPU processing required
AVFramePtr ptr = PreProcessFrame(f, p);
f=std::move(ptr);
f = std::move(ptr);
if (!f) {
qWarning() << "PreProcessFrame failed";
return nullptr;
@@ -458,14 +459,15 @@ FramePtr FFmpegDecoder::RetrieveVideoFrameInternal(const RetrieveVideoParams &p)
AVFramePtr dest = CreateAVFramePtr();
dest->width = f->width;
dest->height = f->height;
dest->format = p.maximum_format == PixelFormat::U8
? AV_PIX_FMT_RGBA
: AV_PIX_FMT_RGBA64;
dest->format = p.maximum_format == PixelFormat::U8 ? AV_PIX_FMT_RGBA :
AV_PIX_FMT_RGBA64;
dest->color_range = f->color_range;
dest->colorspace = f->colorspace;
if (p.divider > 1) {
dest->width = VideoParams::GetScaledDimension(dest->width, p.divider);
dest->height = VideoParams::GetScaledDimension(dest->height, p.divider);
dest->width =
VideoParams::GetScaledDimension(dest->width, p.divider);
dest->height =
VideoParams::GetScaledDimension(dest->height, p.divider);
}
int r = av_frame_get_buffer(dest.get(), 0);
@@ -504,8 +506,8 @@ FramePtr FFmpegDecoder::RetrieveVideoFrameInternal(const RetrieveVideoParams &p)
// zero-initializes the destination, leaving alpha at 0. The color
// management shader later multiplies RGB by alpha, producing black.
// Ensure alpha is opaque for source formats that have no alpha.
const AVPixFmtDescriptor *src_desc = av_pix_fmt_desc_get(
static_cast<AVPixelFormat>(f->format));
const AVPixFmtDescriptor *src_desc =
av_pix_fmt_desc_get(static_cast<AVPixelFormat>(f->format));
if (src_desc && !(src_desc->flags & AV_PIX_FMT_FLAG_ALPHA)) {
const int bpc = (dest->format == AV_PIX_FMT_RGBA) ? 1 : 2;
const int stride = dest->linesize[0];
@@ -522,11 +524,10 @@ FramePtr FFmpegDecoder::RetrieveVideoFrameInternal(const RetrieveVideoParams &p)
}
return CopyPackedAVFrameToFrame(dest,
dest->format == AV_PIX_FMT_RGBA
? PixelFormat::U8
: PixelFormat::U16,
VideoParams::kRGBAChannelCount,
p.time);
dest->format == AV_PIX_FMT_RGBA ?
PixelFormat::U8 :
PixelFormat::U16,
VideoParams::kRGBAChannelCount, p.time);
}
return nullptr;
@@ -580,7 +581,8 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename,
AVFormatContext *fmt_ctx = nullptr;
AVDictionary *format_opts = nullptr;
ApplyFormatOpenOptions(&format_opts);
error_code = avformat_open_input(&fmt_ctx, filename_c, nullptr, &format_opts);
error_code =
avformat_open_input(&fmt_ctx, filename_c, nullptr, &format_opts);
av_dict_free(&format_opts);
TuneFormatContext(fmt_ctx);
DiscardSubtitleStreams(fmt_ctx);
@@ -591,9 +593,8 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename,
avformat_find_stream_info(fmt_ctx, nullptr);
int64_t footage_duration = fmt_ctx->duration;
TimecodeMetadata::SourceTime source_start_time =
ExtractSourceStartTime(fmt_ctx->metadata, rational(1, AV_TIME_BASE),
0);
TimecodeMetadata::SourceTime source_start_time = ExtractSourceStartTime(
fmt_ctx->metadata, rational(1, AV_TIME_BASE), 0);
bool duration_guessed_from_bitrate =
(fmt_ctx->duration_estimation_method ==
@@ -629,12 +630,14 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename,
AVPacket *pkt = av_packet_alloc();
AVFrame *frame = av_frame_alloc();
VideoParams::Interlacing interlacing = VideoParams::kInterlaceNone;
AVRational pixel_aspect_ratio = {1, 1};
VideoParams::Interlacing interlacing =
VideoParams::kInterlaceNone;
AVRational pixel_aspect_ratio = { 1, 1 };
AVRational frame_rate = avstream->avg_frame_rate;
AVPixelFormat compatible_pix_fmt =
FFmpegUtils::GetCompatiblePixelFormat(
static_cast<AVPixelFormat>(avstream->codecpar->format));
static_cast<AVPixelFormat>(
avstream->codecpar->format));
bool image_is_still = false;
{
@@ -643,15 +646,17 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename,
goto cleanup;
AVCodecContext *avctx = instance.codec_ctx();
interlacing = FFmpegFieldOrderToOlive(avctx->field_order);
interlacing =
FFmpegFieldOrderToOlive(avctx->field_order);
if (instance.GetFrame(pkt, frame) >= 0) {
pixel_aspect_ratio =
av_guess_sample_aspect_ratio(instance.fmt_ctx(),
instance.avstream(), frame);
frame_rate =
av_guess_frame_rate(instance.fmt_ctx(),
instance.avstream(), frame);
av_guess_sample_aspect_ratio(
instance.fmt_ctx(), instance.avstream(),
frame);
frame_rate = av_guess_frame_rate(
instance.fmt_ctx(), instance.avstream(),
frame);
}
int ret = instance.GetFrame(pkt, frame);
@@ -660,7 +665,8 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename,
} else if (avstream->duration == AV_NOPTS_VALUE ||
duration_guessed_from_bitrate) {
int64_t last_ts = frame->best_effort_timestamp;
while (instance.GetFrame(pkt, frame) >= 0 &&
while (
instance.GetFrame(pkt, frame) >= 0 &&
(!cancelled || !cancelled->IsCancelled()))
last_ts = frame->best_effort_timestamp;
avstream->duration = last_ts;
@@ -669,7 +675,7 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename,
instance.Close();
}
cleanup:
cleanup:
av_frame_free(&frame);
av_packet_free(&pkt);
@@ -677,26 +683,30 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename,
stream.set_stream_index(i);
stream.set_width(avstream->codecpar->width);
stream.set_height(avstream->codecpar->height);
stream.set_video_type(image_is_still ? VideoParams::kVideoTypeStill
: VideoParams::kVideoTypeVideo);
stream.set_format(GetNativePixelFormat(compatible_pix_fmt));
stream.set_channel_count(GetNativeChannelCount(compatible_pix_fmt));
stream.set_video_type(image_is_still ?
VideoParams::kVideoTypeStill :
VideoParams::kVideoTypeVideo);
stream.set_format(
GetNativePixelFormat(compatible_pix_fmt));
stream.set_channel_count(
GetNativeChannelCount(compatible_pix_fmt));
stream.set_interlacing(interlacing); // <-- 已正确填充
stream.set_pixel_aspect_ratio(pixel_aspect_ratio);
stream.set_frame_rate(frame_rate);
stream.set_start_time(avstream->start_time);
stream.set_time_base(avstream->time_base);
stream.set_duration(avstream->duration);
stream.set_color_range(avstream->codecpar->color_range == AVCOL_RANGE_JPEG
? VideoParams::kColorRangeFull
: VideoParams::kColorRangeLimited);
stream.set_color_range(
avstream->codecpar->color_range ==
AVCOL_RANGE_JPEG ?
VideoParams::kColorRangeFull :
VideoParams::kColorRangeLimited);
stream.set_premultiplied_alpha(false);
desc.AddVideoStream(stream);
image_is_still ? still_streams++ : video_streams++;
}
} else if (avstream->codecpar->codec_type ==
AVMEDIA_TYPE_AUDIO) {
// Create an audio stream object
@@ -837,7 +847,7 @@ bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames,
}
// Create resampling context
AVChannelLayout layout = params.channel_layout();
SwrContext *resampler=NULL;
SwrContext *resampler = NULL;
swr_alloc_set_opts2(
&resampler, &layout,
FFmpegUtils::GetFFmpegSampleFormat(params.format()),
@@ -1270,14 +1280,14 @@ AVFramePtr FFmpegDecoder::RetrieveFrame(const rational &time,
AVFramePtr FFmpegDecoder::TransferHardwareFrame(AVFramePtr f)
{
if (!instance_.hwaccel_enabled() ||
f->format != instance_.hw_pix_fmt()) {
if (!instance_.hwaccel_enabled() || f->format != instance_.hw_pix_fmt()) {
return f;
}
AVFrame *sw_frame = av_frame_alloc();
if (!sw_frame) {
qCritical() << "Failed to allocate software frame for hardware transfer";
qCritical()
<< "Failed to allocate software frame for hardware transfer";
return nullptr;
}
@@ -1291,7 +1301,8 @@ AVFramePtr FFmpegDecoder::TransferHardwareFrame(AVFramePtr f)
ret = av_frame_copy_props(sw_frame, f.get());
if (ret < 0) {
qWarning() << "Failed to copy frame properties during hardware transfer:"
qWarning()
<< "Failed to copy frame properties during hardware transfer:"
<< FFmpegError(ret);
}
@@ -1372,7 +1383,8 @@ bool FFmpegDecoder::Instance::Open(const char *filename, int stream_index)
// Open file in a format context
AVDictionary *format_opts = nullptr;
ApplyFormatOpenOptions(&format_opts);
int error_code = avformat_open_input(&fmt_ctx_, filename, nullptr, &format_opts);
int error_code =
avformat_open_input(&fmt_ctx_, filename, nullptr, &format_opts);
av_dict_free(&format_opts);
TuneFormatContext(fmt_ctx_);
DiscardSubtitleStreams(fmt_ctx_);
@@ -1437,13 +1449,14 @@ bool FFmpegDecoder::Instance::Open(const char *filename, int stream_index)
error_code = avcodec_open2(codec_ctx_, codec, &opts_);
if (error_code == 0) {
hwaccel_enabled_ = true;
qDebug() << "Hardware decoding enabled for" << filename
<< "using" << av_hwdevice_get_type_name(hw_device_type_)
qDebug() << "Hardware decoding enabled for" << filename << "using"
<< av_hwdevice_get_type_name(hw_device_type_)
<< "pixel format" << av_get_pix_fmt_name(hw_pix_fmt_);
return true;
}
qWarning() << "Failed to open hardware codec, falling back to software decoding:";
qWarning()
<< "Failed to open hardware codec, falling back to software decoding:";
char buf[512];
av_strerror(error_code, buf, 512);
qWarning() << FFmpegError(error_code) << buf;
@@ -1454,11 +1467,13 @@ bool FFmpegDecoder::Instance::Open(const char *filename, int stream_index)
codec_ctx_ = avcodec_alloc_context3(codec);
if (codec_ctx_ == nullptr) {
qCritical() << "Failed to allocate codec context for software fallback";
qCritical()
<< "Failed to allocate codec context for software fallback";
return false;
}
error_code = avcodec_parameters_to_context(codec_ctx_, avstream_->codecpar);
error_code =
avcodec_parameters_to_context(codec_ctx_, avstream_->codecpar);
if (error_code < 0) {
qCritical()
<< "Failed to copy parameters from AVStream to AVCodecContext";
@@ -1494,7 +1509,8 @@ AVHWDeviceType FFmpegDecoder::Instance::ChooseHardwareDevice()
}
}
#elif defined(Q_OS_WIN)
for (AVHWDeviceType type : { AV_HWDEVICE_TYPE_D3D11VA, AV_HWDEVICE_TYPE_DXVA2,
for (AVHWDeviceType type :
{ AV_HWDEVICE_TYPE_D3D11VA, AV_HWDEVICE_TYPE_DXVA2,
AV_HWDEVICE_TYPE_CUDA }) {
if (av_hwdevice_find_type_by_name(av_hwdevice_get_type_name(type)) !=
AV_HWDEVICE_TYPE_NONE) {
@@ -1502,17 +1518,17 @@ AVHWDeviceType FFmpegDecoder::Instance::ChooseHardwareDevice()
}
}
#elif defined(Q_OS_MACOS)
if (av_hwdevice_find_type_by_name(
av_hwdevice_get_type_name(AV_HWDEVICE_TYPE_VIDEOTOOLBOX)) !=
AV_HWDEVICE_TYPE_NONE) {
if (av_hwdevice_find_type_by_name(av_hwdevice_get_type_name(
AV_HWDEVICE_TYPE_VIDEOTOOLBOX)) != AV_HWDEVICE_TYPE_NONE) {
return AV_HWDEVICE_TYPE_VIDEOTOOLBOX;
}
#endif
return AV_HWDEVICE_TYPE_NONE;
}
AVPixelFormat FFmpegDecoder::Instance::GetHardwareFormat(
AVCodecContext *ctx, const AVPixelFormat *pix_fmts)
AVPixelFormat
FFmpegDecoder::Instance::GetHardwareFormat(AVCodecContext *ctx,
const AVPixelFormat *pix_fmts)
{
const Instance *inst = static_cast<const Instance *>(ctx->opaque);
for (const AVPixelFormat *p = pix_fmts; *p != AV_PIX_FMT_NONE; p++) {
@@ -1521,7 +1537,8 @@ AVPixelFormat FFmpegDecoder::Instance::GetHardwareFormat(
}
}
qWarning() << "Hardware pixel format not supported by decoder, using first software format";
qWarning()
<< "Hardware pixel format not supported by decoder, using first software format";
return pix_fmts[0];
}
@@ -1547,8 +1564,8 @@ bool FFmpegDecoder::Instance::InitHardwareAcceleration(const AVCodec *codec)
}
if (hw_pix_fmt_ == AV_PIX_FMT_NONE) {
qDebug() << "Codec" << codec->id
<< "does not support hardware device type"
qDebug()
<< "Codec" << codec->id << "does not support hardware device type"
<< av_hwdevice_get_type_name(device_type);
return false;
}
+2 -1
View File
@@ -74,7 +74,8 @@ protected:
virtual bool OpenInternal() override;
virtual TexturePtr
RetrieveVideoInternal(const RetrieveVideoParams &p) override;
virtual FramePtr RetrieveVideoFrameInternal(const RetrieveVideoParams &p) override;
virtual FramePtr
RetrieveVideoFrameInternal(const RetrieveVideoParams &p) override;
virtual bool ConformAudioInternal(const QVector<QString> &filenames,
const AudioParams &params,
CancelAtom *cancelled) override;
+2 -1
View File
@@ -185,7 +185,8 @@ FramePtr OIIODecoder::RetrieveVideoFrameInternal(const RetrieveVideoParams &p)
if (!frame->allocate()) {
return nullptr;
}
memcpy(frame->data(), buffer_.const_data(), size_t(buffer_.allocated_size()));
memcpy(frame->data(), buffer_.const_data(),
size_t(buffer_.allocated_size()));
return frame;
}
+2 -1
View File
@@ -51,7 +51,8 @@ protected:
virtual bool OpenInternal() override;
virtual TexturePtr
RetrieveVideoInternal(const RetrieveVideoParams &p) override;
virtual FramePtr RetrieveVideoFrameInternal(const RetrieveVideoParams &p) override;
virtual FramePtr
RetrieveVideoFrameInternal(const RetrieveVideoParams &p) override;
virtual void CloseInternal() override;
private:
+12 -14
View File
@@ -52,14 +52,12 @@ QString ProxyManager::GetProxyFilename(const QString &cache_path,
const QString proxy_dir = GetProxyDirectory(cache_path);
const QString extension =
params.extension.isEmpty() ? QStringLiteral("mp4") : params.extension;
const QString filename = QStringLiteral("%1-%2.%3x%4.v%5.%6")
.arg(FileFunctions::GetUniqueFileIdentifier(
source_filename),
QString::number(stream_index),
QString::number(params.width),
const QString filename =
QStringLiteral("%1-%2.%3x%4.v%5.%6")
.arg(FileFunctions::GetUniqueFileIdentifier(source_filename),
QString::number(stream_index), QString::number(params.width),
QString::number(params.height),
QString::number(params.version),
extension);
QString::number(params.version), extension);
return QDir(proxy_dir).filePath(filename);
}
@@ -119,9 +117,10 @@ ProxyManager::ProxyStateFromString(const QString &state)
return kProxyMissing;
}
ProxyManager::Proxy ProxyManager::GetOrStartProxy(
const QString &cache_path, const QString &source_filename,
int stream_index, const ProxyParams &params)
ProxyManager::Proxy
ProxyManager::GetOrStartProxy(const QString &cache_path,
const QString &source_filename, int stream_index,
const ProxyParams &params)
{
QMutexLocker locker(&mutex_);
@@ -145,10 +144,9 @@ ProxyManager::Proxy ProxyManager::GetOrStartProxy(
}
const QString working_filename = GetWorkingProxyFilename(filename);
ProxyTask *task = new ProxyTask(source_filename, stream_index, params,
working_filename);
connect(task, &Task::Finished, this,
&ProxyManager::ProxyTaskFinished);
ProxyTask *task =
new ProxyTask(source_filename, stream_index, params, working_filename);
connect(task, &Task::Finished, this, &ProxyManager::ProxyTaskFinished);
task->moveToThread(TaskManager::instance()->thread());
QMetaObject::invokeMethod(TaskManager::instance(), "AddTask",
Qt::QueuedConnection, Q_ARG(Task *, task));
+1 -2
View File
@@ -90,8 +90,7 @@ public:
static ProxyState ProxyStateFromString(const QString &state);
Proxy GetOrStartProxy(const QString &cache_path,
const QString &source_filename,
int stream_index,
const QString &source_filename, int stream_index,
const ProxyParams &params);
signals:
+9 -8
View File
@@ -28,8 +28,9 @@
namespace olive
{
TimecodeMetadata::SourceTime TimecodeMetadata::FromTimecodeString(
const QString &timecode, const core::rational &timebase)
TimecodeMetadata::SourceTime
TimecodeMetadata::FromTimecodeString(const QString &timecode,
const core::rational &timebase)
{
SourceTime result;
const QString trimmed = timecode.trimmed();
@@ -39,8 +40,8 @@ TimecodeMetadata::SourceTime TimecodeMetadata::FromTimecodeString(
bool ok = false;
const core::Timecode::Display display =
trimmed.contains(';') ? core::Timecode::kTimecodeDropFrame
: core::Timecode::kTimecodeNonDropFrame;
trimmed.contains(';') ? core::Timecode::kTimecodeDropFrame :
core::Timecode::kTimecodeNonDropFrame;
result.time = core::Timecode::timecode_to_time(trimmed.toStdString(),
timebase, display, &ok);
result.valid = ok;
@@ -50,8 +51,9 @@ TimecodeMetadata::SourceTime TimecodeMetadata::FromTimecodeString(
return result;
}
TimecodeMetadata::SourceTime TimecodeMetadata::FromBwfTimeReference(
const QString &time_reference, int sample_rate)
TimecodeMetadata::SourceTime
TimecodeMetadata::FromBwfTimeReference(const QString &time_reference,
int sample_rate)
{
SourceTime result;
if (sample_rate <= 0) {
@@ -73,8 +75,7 @@ TimecodeMetadata::SourceTime TimecodeMetadata::FromBwfTimeReference(
const qulonglong rational_limit =
static_cast<qulonglong>(std::numeric_limits<int>::max());
if (numerator <= rational_limit && denominator <= rational_limit) {
result.time =
core::rational(static_cast<int>(numerator),
result.time = core::rational(static_cast<int>(numerator),
static_cast<int>(denominator));
} else {
result.time = core::rational::fromDouble(
+10 -10
View File
@@ -25,31 +25,31 @@
class Current {
public:
static Current& getInstance()
static Current &getInstance()
{
return current;
}
olive::VideoParams& currentVideoParams()
olive::VideoParams &currentVideoParams()
{
return currentVideoParams_;
}
olive::AudioParams& currentAudioParams()
olive::AudioParams &currentAudioParams()
{
return currentAudioParams_;
}
void setCurrentVideoParams(olive::VideoParams& params)
void setCurrentVideoParams(olive::VideoParams &params)
{
currentVideoParams_ = params;
}
void setCurrentAudioParams(olive::AudioParams& params)
void setCurrentAudioParams(olive::AudioParams &params)
{
currentAudioParams_ = params;
}
void setCurrentVideoParams(olive::VideoParams&& params)
void setCurrentVideoParams(olive::VideoParams &&params)
{
currentVideoParams_ = params;
}
void setCurrentAudioParams(olive::AudioParams&& params)
void setCurrentAudioParams(olive::AudioParams &&params)
{
currentAudioParams_ = params;
}
@@ -73,10 +73,12 @@ public:
return plugin_cache_;
}
void setPluginCache(std::shared_ptr<OFX::Host::ImageEffect::PluginCache> cache)
void
setPluginCache(std::shared_ptr<OFX::Host::ImageEffect::PluginCache> cache)
{
plugin_cache_ = cache;
}
private:
static Current current;
olive::VideoParams currentVideoParams_;
@@ -85,6 +87,4 @@ private:
std::shared_ptr<OFX::Host::ImageEffect::PluginCache> plugin_cache_;
};
#endif //CURRENT_H
+2 -2
View File
@@ -341,8 +341,8 @@ void Config::Load()
// Config::Load() is called before Core (and therefore the main window)
// is constructed, so we cannot use Core::instance()->main_window() as
// the message box parent. Passing nullptr creates a top-level dialog.
QWidget *parent = Core::instance() ? Core::instance()->main_window()
: nullptr;
QWidget *parent = Core::instance() ? Core::instance()->main_window() :
nullptr;
QMessageBox::critical(
parent,
QCoreApplication::translate("Config", "Error loading settings"),
+18 -18
View File
@@ -80,7 +80,8 @@
#include "widget/menu/menushared.h"
#include "window/mainwindow/mainwindow.h"
namespace {
namespace
{
QStringList FootageVideoExtensions()
{
@@ -95,22 +96,21 @@ QStringList FootageVideoExtensions()
QStringList FootageAudioExtensions()
{
return QStringList{
QStringLiteral("wav"), QStringLiteral("mp3"), QStringLiteral("flac"),
QStringLiteral("aac"), QStringLiteral("ogg"), QStringLiteral("opus"),
QStringLiteral("m4a"), QStringLiteral("alac"), QStringLiteral("aif"),
QStringLiteral("aiff"), QStringLiteral("aifc"), QStringLiteral("wma")
};
return QStringList{ QStringLiteral("wav"), QStringLiteral("mp3"),
QStringLiteral("flac"), QStringLiteral("aac"),
QStringLiteral("ogg"), QStringLiteral("opus"),
QStringLiteral("m4a"), QStringLiteral("alac"),
QStringLiteral("aif"), QStringLiteral("aiff"),
QStringLiteral("aifc"), QStringLiteral("wma") };
}
QStringList FootageImageExtensions()
{
return QStringList{
QStringLiteral("png"), QStringLiteral("jpg"), QStringLiteral("jpeg"),
QStringLiteral("tif"), QStringLiteral("tiff"), QStringLiteral("bmp"),
QStringLiteral("gif"), QStringLiteral("exr"), QStringLiteral("dpx"),
QStringLiteral("webp")
};
return QStringList{ QStringLiteral("png"), QStringLiteral("jpg"),
QStringLiteral("jpeg"), QStringLiteral("tif"),
QStringLiteral("tiff"), QStringLiteral("bmp"),
QStringLiteral("gif"), QStringLiteral("exr"),
QStringLiteral("dpx"), QStringLiteral("webp") };
}
QString BuildFootageFilterGroup(const QString &label,
@@ -122,8 +122,8 @@ QString BuildFootageFilterGroup(const QString &label,
patterns.append(QStringLiteral("*.%1").arg(ext));
}
return QStringLiteral("%1 (%2)")
.arg(label, patterns.join(QLatin1Char(' ')));
return QStringLiteral("%1 (%2)").arg(label,
patterns.join(QLatin1Char(' ')));
}
QString BuildFootageFileDialogFilter()
@@ -457,9 +457,9 @@ void Core::DialogAboutShow()
void Core::DialogImportShow()
{
// Open dialog for user to select files
QStringList files = QFileDialog::getOpenFileNames(
main_window_, tr("Import footage..."), QString(),
FootageFileDialogFilter());
QStringList files =
QFileDialog::getOpenFileNames(main_window_, tr("Import footage..."),
QString(), FootageFileDialogFilter());
// Check if the user actually selected files to import
if (!files.isEmpty()) {
+7 -4
View File
@@ -22,6 +22,9 @@ add_executable(
$<TARGET_OBJECTS:olive-version-obj>
)
# Rename the generated binary to oak-crashhandler so it matches the Oak branding.
set_target_properties(olive-crashhandler PROPERTIES OUTPUT_NAME "oak-crashhandler")
# Disable console appearing on crash handler dialog
set_target_properties(olive-crashhandler PROPERTIES
WIN32_EXECUTABLE TRUE
@@ -49,17 +52,17 @@ target_link_libraries(
set(CRASHPAD_HANDLER "crashpad_handler${CMAKE_EXECUTABLE_SUFFIX}")
set(MINIDUMP_STACKWALK "minidump_stackwalk${CMAKE_EXECUTABLE_SUFFIX}")
if(APPLE)
if (APPLE)
# Move crash handler executables inside Mac app bundle
add_custom_command(TARGET olive-crashhandler POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different olive-crashhandler $<TARGET_FILE_DIR:olive-editor>
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:olive-crashhandler> $<TARGET_FILE_DIR:olive-editor>
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CRASHPAD_LIBRARY_DIRS}/${CRASHPAD_HANDLER} $<TARGET_FILE_DIR:olive-editor>
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${BREAKPAD_BIN_DIR}/${MINIDUMP_STACKWALK} $<TARGET_FILE_DIR:olive-editor>
)
elseif(UNIX)
elseif (UNIX)
install(TARGETS olive-crashhandler RUNTIME DESTINATION bin)
install(PROGRAMS ${CRASHPAD_LIBRARY_DIRS}/${CRASHPAD_HANDLER} DESTINATION bin)
install(PROGRAMS ${BREAKPAD_BIN_DIR}/${MINIDUMP_STACKWALK} DESTINATION bin)
endif()
endif ()
target_compile_definitions(olive-crashhandler PRIVATE ${OLIVE_DEFINITIONS})
+8 -8
View File
@@ -54,8 +54,8 @@ CrashHandlerDialog::CrashHandlerDialog(const QString &report_path)
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(new QLabel(
tr("We're sorry, Oak Video Editor has crashed. Please help us fix it by "
layout->addWidget(new QLabel(tr(
"We're sorry, Oak Video Editor has crashed. Please help us fix it by "
"sending an error report.")));
QSplitter *splitter = new QSplitter(Qt::Vertical);
@@ -124,7 +124,7 @@ QString CrashHandlerDialog::GetSymbolPath()
#elif BUILDFLAG(IS_LINUX)
app_path.cdUp();
symbols_path =
app_path.filePath(QStringLiteral("share/olive-editor/symbols"));
app_path.filePath(QStringLiteral("share/oak-editor/symbols"));
#elif BUILDFLAG(IS_APPLE)
app_path.cdUp();
symbols_path = app_path.filePath(QStringLiteral("Resources/symbols"));
@@ -291,11 +291,11 @@ void CrashHandlerDialog::SendErrorReport()
QString symbol_bin_name;
#if BUILDFLAG(IS_WIN)
symbol_bin_name = QStringLiteral("olive-editor.pdb");
symbol_bin_name = QStringLiteral("oak-editor.pdb");
#elif BUILDFLAG(IS_APPLE)
symbol_bin_name = QStringLiteral("Olive");
symbol_bin_name = QStringLiteral("Oak");
#else
symbol_bin_name = QStringLiteral("olive-editor");
symbol_bin_name = QStringLiteral("oak-editor");
#endif
symbol_dir = QDir(symbol_dir.filePath(symbol_bin_name));
@@ -320,9 +320,9 @@ void CrashHandlerDialog::SendErrorReport()
// Create sym section
QString symbol_filename;
#if BUILDFLAG(IS_APPLE)
symbol_filename = QStringLiteral("Olive.sym");
symbol_filename = QStringLiteral("Oak.sym");
#else
symbol_filename = QStringLiteral("olive-editor.sym");
symbol_filename = QStringLiteral("oak-editor.sym");
#endif
QString symbol_full_path = symbol_dir.filePath(symbol_filename);
QHttpPart sym_part;
+2 -2
View File
@@ -25,9 +25,9 @@ add_subdirectory(footageproperties)
add_subdirectory(footagerelink)
add_subdirectory(keyframeproperties)
add_subdirectory(markerproperties)
if(OpenTimelineIO_FOUND)
if (OpenTimelineIO_FOUND)
add_subdirectory(otioproperties)
endif()
endif ()
add_subdirectory(preferences)
add_subdirectory(progress)
add_subdirectory(projectproperties)
+2 -1
View File
@@ -66,7 +66,8 @@ AboutDialog::AboutDialog(bool welcome_dialog, QWidget *parent)
"<p>%3</p>" // Description
"<p>%4</p>" // Fork notice
"</body></html>")
.arg(QApplication::applicationName(),
.arg(
QApplication::applicationName(),
QApplication::applicationVersion(),
tr("Oak Video Editor is a free open source non-linear video editor. "
"This software is licensed under the GNU GPL Version 3."),
+3 -2
View File
@@ -57,7 +57,7 @@ url = 'https://www.patreon.com/api/oauth2/v2/campaigns/1478705/members?include=c
name_list = ''
while True:
member_data = requests.get(url, headers = {"authorization": "Bearer " + os.environ.get('PATREON_KEY')})
member_data = requests.get(url, headers={"authorization": "Bearer " + os.environ.get('PATREON_KEY')})
member_data_decoded = json.loads(member_data.text)
for member in member_data_decoded["data"]:
@@ -79,5 +79,6 @@ while True:
break
text_file = open("patreon.h", "w", encoding="utf-8")
text_file.write("#ifndef PATREON_H\n#define PATREON_H\n\n#include <QStringList>\n\nQStringList patrons = {\n%s\n};\n\n#endif // PATREON_H\n" % name_list)
text_file.write(
"#ifndef PATREON_H\n#define PATREON_H\n\n#include <QStringList>\n\nQStringList patrons = {\n%s\n};\n\n#endif // PATREON_H\n" % name_list)
text_file.close()
-1
View File
@@ -26,7 +26,6 @@
#include <QLabel>
#include <QMessageBox>
namespace olive
{
-1
View File
@@ -24,7 +24,6 @@
#include <QGridLayout>
#include <QLabel>
namespace olive
{
+6 -3
View File
@@ -46,15 +46,18 @@ PreferencesDialog::PreferencesDialog(MainWindow *main_window, int start_tab)
AddTab(new PreferencesGeneralTab(), tr("General"));
AddTab(new PreferencesAppearanceTab(), tr("Appearance"));
AddTab(new PreferencesAudioTab(), tr("Audio"));
AddTab(new PreferencesBehaviorTab(PreferencesBehaviorTab::kCategoryTimeline),
AddTab(
new PreferencesBehaviorTab(PreferencesBehaviorTab::kCategoryTimeline),
tr("Timeline"));
AddTab(new PreferencesBehaviorTab(PreferencesBehaviorTab::kCategoryPlayback),
AddTab(
new PreferencesBehaviorTab(PreferencesBehaviorTab::kCategoryPlayback),
tr("Playback"));
AddTab(new PreferencesBehaviorTab(PreferencesBehaviorTab::kCategoryProject),
tr("Project"));
AddTab(new PreferencesBehaviorTab(PreferencesBehaviorTab::kCategoryNodes),
tr("Nodes"));
AddTab(new PreferencesBehaviorTab(PreferencesBehaviorTab::kCategoryRendering),
AddTab(
new PreferencesBehaviorTab(PreferencesBehaviorTab::kCategoryRendering),
tr("Rendering"));
AddTab(new PreferencesDiskTab(), tr("Disk"));
AddTab(new PreferencesKeyboardTab(main_window), tr("Keyboard"));
@@ -40,8 +40,7 @@ PreferencesBehaviorTab::PreferencesBehaviorTab(Category category)
AddItems({
{ tr("Auto-Seek to Imported Clips"),
QStringLiteral("EnableSeekToImport") },
{ tr("Edit Tool Also Seeks"),
QStringLiteral("EditToolAlsoSeeks") },
{ tr("Edit Tool Also Seeks"), QStringLiteral("EditToolAlsoSeeks") },
{ tr("Edit Tool Selects Links"),
QStringLiteral("EditToolSelectsLinks") },
{ tr("Enable Drag Files to Timeline"),
@@ -83,8 +82,7 @@ PreferencesBehaviorTab::PreferencesBehaviorTab(Category category)
});
break;
case kCategoryRendering:
{
case kCategoryRendering: {
QLabel *backend_label = new QLabel(tr("Graphics Backend"));
backend_label->setToolTip(
tr("Selects the graphics API Oak should request on next launch. "
@@ -100,11 +98,10 @@ PreferencesBehaviorTab::PreferencesBehaviorTab(Category category)
const QString current_backend =
OLIVE_CONFIG("GraphicsBackend").toString().toLower();
const int backend_index = graphics_backend_combobox_->findData(
current_backend.isEmpty() ? QStringLiteral("opengl")
: current_backend);
graphics_backend_combobox_->setCurrentIndex(backend_index >= 0
? backend_index
: 0);
current_backend.isEmpty() ? QStringLiteral("opengl") :
current_backend);
graphics_backend_combobox_->setCurrentIndex(
backend_index >= 0 ? backend_index : 0);
QHBoxLayout *backend_layout = new QHBoxLayout();
backend_layout->addWidget(backend_label);
@@ -49,7 +49,8 @@ public:
static QString BehaviorPrefTr(const char *text)
{
return QCoreApplication::translate("olive::PreferencesBehaviorTab", text);
return QCoreApplication::translate("olive::PreferencesBehaviorTab",
text);
}
private:
@@ -105,16 +105,14 @@ PreferencesDiskTab::PreferencesDiskTab()
proxy_width_slider_ = new IntegerSlider();
proxy_width_slider_->SetMinimum(160);
proxy_width_slider_->SetMaximum(4096);
proxy_width_slider_->SetValue(
OLIVE_CONFIG("ProxyWidth").value<int>());
proxy_width_slider_->SetValue(OLIVE_CONFIG("ProxyWidth").value<int>());
proxy_layout->addWidget(proxy_width_slider_, proxy_row, 1);
proxy_layout->addWidget(new QLabel(tr("Proxy Height:")), proxy_row, 2);
proxy_height_slider_ = new IntegerSlider();
proxy_height_slider_->SetMinimum(120);
proxy_height_slider_->SetMaximum(2160);
proxy_height_slider_->SetValue(
OLIVE_CONFIG("ProxyHeight").value<int>());
proxy_height_slider_->SetValue(OLIVE_CONFIG("ProxyHeight").value<int>());
proxy_layout->addWidget(proxy_height_slider_, proxy_row, 3);
proxy_row++;
@@ -123,28 +121,22 @@ PreferencesDiskTab::PreferencesDiskTab()
proxy_crf_slider_ = new IntegerSlider();
proxy_crf_slider_->SetMinimum(0);
proxy_crf_slider_->SetMaximum(51);
proxy_crf_slider_->SetValue(
OLIVE_CONFIG("ProxyCRF").value<int>());
proxy_crf_slider_->SetValue(OLIVE_CONFIG("ProxyCRF").value<int>());
proxy_layout->addWidget(proxy_crf_slider_, proxy_row, 1);
proxy_layout->addWidget(new QLabel(tr("Proxy Preset:")), proxy_row, 2);
proxy_preset_combo_ = new QComboBox();
const QStringList presets = {
QStringLiteral("ultrafast"),
QStringLiteral("superfast"),
QStringLiteral("veryfast"),
QStringLiteral("faster"),
QStringLiteral("fast"),
QStringLiteral("medium"),
QStringLiteral("slow"),
QStringLiteral("slower"),
QStringLiteral("ultrafast"), QStringLiteral("superfast"),
QStringLiteral("veryfast"), QStringLiteral("faster"),
QStringLiteral("fast"), QStringLiteral("medium"),
QStringLiteral("slow"), QStringLiteral("slower"),
QStringLiteral("veryslow"),
};
for (const QString &preset : presets) {
proxy_preset_combo_->addItem(preset);
}
proxy_preset_combo_->setCurrentText(
OLIVE_CONFIG("ProxyPreset").toString());
proxy_preset_combo_->setCurrentText(OLIVE_CONFIG("ProxyPreset").toString());
proxy_layout->addWidget(proxy_preset_combo_, proxy_row, 3);
outer_layout->addStretch();
@@ -185,8 +177,10 @@ void PreferencesDiskTab::Accept(MultiUndoCommand *command)
OLIVE_CONFIG("DiskCacheAhead") = QVariant::fromValue(
rational::fromDouble(cache_ahead_slider_->GetValue()));
OLIVE_CONFIG("ProxyWidth") = static_cast<int>(proxy_width_slider_->GetValue());
OLIVE_CONFIG("ProxyHeight") = static_cast<int>(proxy_height_slider_->GetValue());
OLIVE_CONFIG("ProxyWidth") =
static_cast<int>(proxy_width_slider_->GetValue());
OLIVE_CONFIG("ProxyHeight") =
static_cast<int>(proxy_height_slider_->GetValue());
OLIVE_CONFIG("ProxyCRF") = static_cast<int>(proxy_crf_slider_->GetValue());
OLIVE_CONFIG("ProxyPreset") = proxy_preset_combo_->currentText();
}
@@ -169,21 +169,25 @@ PreferencesGeneralTab::PreferencesGeneralTab()
}
{
QGroupBox *behavior_groupbox = new QGroupBox(PreferencesBehaviorTab::BehaviorPrefTr("Behavior"));
QGroupBox *behavior_groupbox =
new QGroupBox(PreferencesBehaviorTab::BehaviorPrefTr("Behavior"));
QVBoxLayout *behavior_layout = new QVBoxLayout(behavior_groupbox);
layout->addWidget(behavior_groupbox);
hover_focus_ = new QCheckBox(PreferencesBehaviorTab::BehaviorPrefTr("Enable hover focus"));
hover_focus_ = new QCheckBox(
PreferencesBehaviorTab::BehaviorPrefTr("Enable hover focus"));
hover_focus_->setToolTip(PreferencesBehaviorTab::BehaviorPrefTr(
"Panels will be considered focused when the mouse cursor is over them without having to click them."));
hover_focus_->setChecked(OLIVE_CONFIG("HoverFocus").toBool());
behavior_layout->addWidget(hover_focus_);
slider_ladder_ = new QCheckBox(PreferencesBehaviorTab::BehaviorPrefTr("Enable slider ladder"));
slider_ladder_ = new QCheckBox(
PreferencesBehaviorTab::BehaviorPrefTr("Enable slider ladder"));
slider_ladder_->setChecked(OLIVE_CONFIG("UseSliderLadders").toBool());
behavior_layout->addWidget(slider_ladder_);
scroll_zooms_ = new QCheckBox(PreferencesBehaviorTab::BehaviorPrefTr("Scrolling zooms by default"));
scroll_zooms_ = new QCheckBox(PreferencesBehaviorTab::BehaviorPrefTr(
"Scrolling zooms by default"));
scroll_zooms_->setToolTip(PreferencesBehaviorTab::BehaviorPrefTr(
"By default, scrolling will move the view around, and holding Ctrl/Cmd will make it zoom instead. "
"Enabling this will switch those, scrolling will zoom by default, and holding Ctrl/Cmd will move the view instead."));
@@ -23,7 +23,6 @@
#include <QPushButton>
#include <QVBoxLayout>
namespace olive
{
-1
View File
@@ -27,7 +27,6 @@
#include <QPushButton>
#include <QVBoxLayout>
namespace olive
{
+8 -8
View File
@@ -148,14 +148,15 @@ int decompress_project(const QString &project)
int main(int argc, char *argv[])
{
// Set up debug handler
qInstallMessageHandler(olive::DebugHandler);
// Ignore SIGPIPE so that writing to a render-worker process that has
// already crashed/closed does not terminate the main application. QProcess
// will report the failure through its normal error path instead.
#if !defined(_WIN32)
signal(SIGPIPE, SIG_IGN);
#endif
// Set application metadata
QCoreApplication::setOrganizationName("oakvideoeditor.org");
@@ -227,8 +228,7 @@ int main(int argc, char *argv[])
auto no_plugin = parser.AddOption(
{ QStringLiteral("-no-plugin") },
QCoreApplication::translate("main", "Don't load plugins")
);
QCoreApplication::translate("main", "Don't load plugins"));
// Qt options re-implemented (add to this as necessary)
//
@@ -350,13 +350,13 @@ int main(int argc, char *argv[])
olive::Config::Current()[QStringLiteral("GraphicsBackend")]
.toString()
.toLower();
qputenv("QSG_RHI_BACKEND",
graphics_backend == QStringLiteral("vulkan")
? QByteArrayLiteral("vulkan")
: QByteArrayLiteral("opengl"));
qputenv("QSG_RHI_BACKEND", graphics_backend == QStringLiteral("vulkan") ?
QByteArrayLiteral("vulkan") :
QByteArrayLiteral("opengl"));
if (auto *gui_app = qobject_cast<QGuiApplication *>(a.get())) {
gui_app->setWindowIcon(QIcon(QStringLiteral(":/graphics/oak-logo.png")));
gui_app->setWindowIcon(
QIcon(QStringLiteral(":/graphics/oak-logo.png")));
}
if (load_plugins) {
+10 -8
View File
@@ -37,7 +37,8 @@ const QString OCIOLutNode::kDirectionInput = QStringLiteral("lut_dir_in");
#define super OCIOBaseNode
namespace {
namespace
{
bool IsSupportedLutExtension(const QString &suffix)
{
@@ -47,7 +48,8 @@ bool IsSupportedLutExtension(const QString &suffix)
bool IsMainProcess()
{
return qobject_cast<QApplication *>(QCoreApplication::instance()) != nullptr;
return qobject_cast<QApplication *>(QCoreApplication::instance()) !=
nullptr;
}
int ReadDirectionInput(const Node *node)
@@ -245,9 +247,8 @@ bool OCIOLutNode::CreateProcessorFromInputs() const
ColorProcessorPtr processor;
try {
const bool forward =
static_cast<ColorProcessor::Direction>(direction) ==
ColorProcessor::kNormal;
const bool forward = static_cast<ColorProcessor::Direction>(
direction) == ColorProcessor::kNormal;
qDebug() << "OCIOLutNode: creating processor for" << path
<< "direction=" << direction
<< "ocio_dir=" << (forward ? "FORWARD" : "INVERSE")
@@ -256,10 +257,11 @@ bool OCIOLutNode::CreateProcessorFromInputs() const
OCIO::FileTransformRcPtr transform = OCIO::FileTransform::Create();
transform->setSrc(path.toUtf8().constData());
transform->setInterpolation(OCIO::INTERP_LINEAR);
transform->setDirection(
forward ? OCIO::TRANSFORM_DIR_FORWARD : OCIO::TRANSFORM_DIR_INVERSE);
transform->setDirection(forward ? OCIO::TRANSFORM_DIR_FORWARD :
OCIO::TRANSFORM_DIR_INVERSE);
processor = ColorProcessor::Create(manager()->GetConfig()->getProcessor(transform));
processor = ColorProcessor::Create(
manager()->GetConfig()->getProcessor(transform));
} catch (const std::exception &e) {
qWarning() << "OCIO LUT processor error:" << e.what();
processor = nullptr;
@@ -87,8 +87,7 @@ void ThreeWayColorNode::Retranslate()
SetInputName(kHighlightsAmountInput, tr("Highlights Amount"));
}
ShaderCode
ThreeWayColorNode::GetShaderCode(const ShaderRequest &request) const
ShaderCode ThreeWayColorNode::GetShaderCode(const ShaderRequest &request) const
{
Q_UNUSED(request)
return ShaderCode(
+4 -7
View File
@@ -94,7 +94,6 @@ void NodeFactory::Initialize()
}
RegisterPluginNodes();
}
void NodeFactory::Destroy()
@@ -151,8 +150,7 @@ Menu *NodeFactory::CreateMenu(QWidget *parent, bool create_none_item,
// Determine final destination (support secondary grouping)
Menu *destination = top_menu;
QString sub = n->SubCategory();
if (!sub.isEmpty() &&
n->Category().contains(Node::kCategoryOpenFX)) {
if (!sub.isEmpty() && n->Category().contains(Node::kCategoryOpenFX)) {
QList<QAction *> sub_actions = top_menu->actions();
foreach (QAction *action, sub_actions) {
if (action->menu() && action->menu()->title() == sub) {
@@ -249,16 +247,15 @@ void NodeFactory::RegisterPluginNodes()
continue;
}
const QString plugin_id = QString::fromStdString(
image_effect->getIdentifier());
const QString plugin_id =
QString::fromStdString(image_effect->getIdentifier());
if (existing_ids.contains(plugin_id)) {
continue;
}
const auto &contexts = image_effect->getContexts();
if (contexts.empty()) {
qWarning() << "Skipping OFX plugin with no contexts:"
<< plugin_id;
qWarning() << "Skipping OFX plugin with no contexts:" << plugin_id;
continue;
}
std::string context = kOfxImageEffectContextFilter;
+9 -6
View File
@@ -182,7 +182,10 @@ public:
* @brief Return a sub-category string for secondary grouping
* within the primary category (e.g. "Filter" under "OpenFX").
*/
virtual QString SubCategory() const { return QString(); }
virtual QString SubCategory() const
{
return QString();
}
/**
* @brief Return a description of this node's purpose (optional for subclassing, but recommended)
@@ -1136,20 +1139,20 @@ public:
static const QString kEnabledInput;
OFX::Host::ImageEffect::Instance* getPluginInstance() const
OFX::Host::ImageEffect::Instance *getPluginInstance() const
{
return plugin_instance_;
}
OFX::Host::ImageEffect::ImageEffectPlugin* getPlugin() const
OFX::Host::ImageEffect::ImageEffectPlugin *getPlugin() const
{
return plugin_instance_ ? plugin_instance_->getPlugin() : nullptr;
}
protected:
// If set, this node owns a plugin instance.
OFX::Host::ImageEffect::Instance* plugin_instance_ = nullptr;
OFX::Host::ImageEffect::Instance *plugin_instance_ = nullptr;
void setPluginInstance(OFX::Host::ImageEffect::Instance* instance)
void setPluginInstance(OFX::Host::ImageEffect::Instance *instance)
{
plugin_instance_ = instance;
}
+4 -4
View File
@@ -581,12 +581,12 @@ void ViewerOutput::set_parameters_from_footage(
found_video_params = true;
}
SetVideoParams(VideoParams(
s.width(), s.height(), using_timebase,
SetVideoParams(
VideoParams(s.width(), s.height(), using_timebase,
static_cast<PixelFormat::Format>(
OLIVE_CONFIG("OfflinePixelFormat").toInt()),
VideoParams::kInternalChannelCount, s.pixel_aspect_ratio(),
s.interlacing(), 1));
VideoParams::kInternalChannelCount,
s.pixel_aspect_ratio(), s.interlacing(), 1));
if (found_video_params) {
break;
+62 -88
View File
@@ -32,7 +32,8 @@
#include <QVector2D>
#include <QVector3D>
namespace {
namespace
{
QHash<QString, QHash<QString, QVariant>> g_plugin_param_defaults;
static bool IsNormalisedCoordSystem(const OFX::Host::Param::Base *param)
@@ -61,8 +62,7 @@ QVariant DefaultValueForParam(const OFX::Host::Param::Base *param)
const std::string &ofxType = param->getType();
const auto &props = param->getProperties();
if (ofxType == kOfxParamTypeInteger ||
ofxType == kOfxParamTypeChoice) {
if (ofxType == kOfxParamTypeInteger || ofxType == kOfxParamTypeChoice) {
return props.getIntProperty(kOfxParamPropDefault);
}
if (ofxType == kOfxParamTypeBoolean) {
@@ -77,34 +77,30 @@ QVariant DefaultValueForParam(const OFX::Host::Param::Base *param)
}
return val;
}
if (ofxType == kOfxParamTypeString ||
ofxType == kOfxParamTypeStrChoice ||
if (ofxType == kOfxParamTypeString || ofxType == kOfxParamTypeStrChoice ||
ofxType == kOfxParamTypeCustom) {
return QString::fromStdString(
props.getStringProperty(kOfxParamPropDefault));
}
if (ofxType == kOfxParamTypeRGB ||
ofxType == kOfxParamTypeRGBA) {
if (ofxType == kOfxParamTypeRGB || ofxType == kOfxParamTypeRGBA) {
const int count = (ofxType == kOfxParamTypeRGBA) ? 4 : 3;
double values[4] = {0.0, 0.0, 0.0, 1.0};
double values[4] = { 0.0, 0.0, 0.0, 1.0 };
props.getDoublePropertyN(kOfxParamPropDefault, values, count);
const double alpha = (count == 4) ? values[3] : 1.0;
return QVariant::fromValue(
olive::core::Color(values[0], values[1], values[2], alpha));
}
if (ofxType == kOfxParamTypeDouble2D ||
ofxType == kOfxParamTypeDouble3D ||
if (ofxType == kOfxParamTypeDouble2D || ofxType == kOfxParamTypeDouble3D ||
ofxType == kOfxParamTypeInteger2D ||
ofxType == kOfxParamTypeInteger3D) {
const bool is_double =
(ofxType == kOfxParamTypeDouble2D ||
const bool is_double = (ofxType == kOfxParamTypeDouble2D ||
ofxType == kOfxParamTypeDouble3D);
const int count = (ofxType == kOfxParamTypeDouble2D ||
ofxType == kOfxParamTypeInteger2D)
? 2
: 3;
ofxType == kOfxParamTypeInteger2D) ?
2 :
3;
if (is_double) {
double values[3] = {0.0, 0.0, 0.0};
double values[3] = { 0.0, 0.0, 0.0 };
props.getDoublePropertyN(kOfxParamPropDefault, values, count);
if (IsNormalisedCoordSystem(param)) {
double xSize, ySize;
@@ -120,7 +116,7 @@ QVariant DefaultValueForParam(const OFX::Host::Param::Base *param)
}
return QVector3D(values[0], values[1], values[2]);
}
int values[3] = {0, 0, 0};
int values[3] = { 0, 0, 0 };
props.getIntPropertyN(kOfxParamPropDefault, values, count);
if (count == 2) {
return QVector2D(values[0], values[1]);
@@ -156,8 +152,7 @@ QString DeduceColorSemantic(const OFX::Host::Param::Base *param,
// Rule 1: explicit color keywords → color
static const QStringList kColorKeywords = {
QStringLiteral("color"), QStringLiteral("colour"),
QStringLiteral("fill"), QStringLiteral("tint"),
QStringLiteral("key")
QStringLiteral("fill"), QStringLiteral("tint"), QStringLiteral("key")
};
for (const QString &kw : kColorKeywords) {
if (label.contains(kw) || hint.contains(kw) || name.contains(kw)) {
@@ -183,8 +178,8 @@ QString DeduceColorSemantic(const OFX::Host::Param::Base *param,
// Rule 3: display range significantly outside/asymmetric to [0,1] → scalar
const auto &props = param->getProperties();
const int dim = (ofxType == kOfxParamTypeRGBA) ? 4 : 3;
double dmin[4] = {0, 0, 0, 0};
double dmax[4] = {1, 1, 1, 1};
double dmin[4] = { 0, 0, 0, 0 };
double dmax[4] = { 1, 1, 1, 1 };
props.getDoublePropertyN(kOfxParamPropDisplayMin, dmin, dim);
props.getDoublePropertyN(kOfxParamPropDisplayMax, dmax, dim);
bool range_looks_scalar = false;
@@ -199,7 +194,7 @@ QString DeduceColorSemantic(const OFX::Host::Param::Base *param,
}
// Rule 4: default values all equal → scalar (lean)
double defs[4] = {0, 0, 0, 1};
double defs[4] = { 0, 0, 0, 1 };
props.getDoublePropertyN(kOfxParamPropDefault, defs, dim);
bool all_equal = true;
for (int i = 1; i < dim; ++i) {
@@ -227,14 +222,13 @@ QString DeduceColorSemantic(const OFX::Host::Param::Base *param,
return QStringLiteral("color");
}
QHash<QString, QVariant>
BuildDefaultValues(const std::map<std::string, OFX::Host::Param::Instance *> &params)
QHash<QString, QVariant> BuildDefaultValues(
const std::map<std::string, OFX::Host::Param::Instance *> &params)
{
QHash<QString, QVariant> defaults;
for (const auto &param : params) {
const std::string &ofxType = param.second->getType();
if (ofxType == kOfxParamTypeGroup ||
ofxType == kOfxParamTypePage ||
if (ofxType == kOfxParamTypeGroup || ofxType == kOfxParamTypePage ||
ofxType == kOfxParamTypePushButton) {
continue;
}
@@ -254,7 +248,8 @@ BuildDefaultValues(const std::map<std::string, OFX::Host::Param::Instance *> &pa
return defaults;
}
}
static QString ClipLabelForName(const std::string &name,
static QString
ClipLabelForName(const std::string &name,
const OFX::Host::ImageEffect::ClipDescriptor *desc)
{
if (name == kOfxImageEffectSimpleSourceClipName) {
@@ -278,10 +273,9 @@ static QString ClipLabelForName(const std::string &name,
return QString::fromStdString(name);
}
olive::plugin::PluginNode::PluginNode(
OFX::Host::ImageEffect::Instance *plugin)
olive::plugin::PluginNode::PluginNode(OFX::Host::ImageEffect::Instance *plugin)
{
plugin_instance_=plugin;
plugin_instance_ = plugin;
const std::string &ctx = plugin_instance_->getContext();
if (ctx == kOfxImageEffectContextFilter) {
@@ -299,16 +293,16 @@ olive::plugin::PluginNode::PluginNode(
QHash<QString, QString> page_labels;
QHash<QString, QString> page_for_param;
auto params=plugin_instance_->getParams();
const QString plugin_id = QString::fromStdString(
plugin_instance_->getPlugin()->getIdentifier());
auto params = plugin_instance_->getParams();
const QString plugin_id =
QString::fromStdString(plugin_instance_->getPlugin()->getIdentifier());
auto defaults_iter = g_plugin_param_defaults.find(plugin_id);
if (defaults_iter == g_plugin_param_defaults.end()) {
g_plugin_param_defaults.insert(plugin_id, BuildDefaultValues(params));
defaults_iter = g_plugin_param_defaults.find(plugin_id);
}
const QHash<QString, QVariant> &defaults = defaults_iter.value();
for (auto param: params) {
for (auto param : params) {
const std::string &ofxType = param.second->getType();
if (ofxType == kOfxParamTypeGroup) {
const QString name = QString::fromStdString(param.first);
@@ -336,8 +330,7 @@ olive::plugin::PluginNode::PluginNode(
}
}
for (auto param: params) {
for (auto param : params) {
NodeValue::Type type = NodeValue::kNone;
const std::string &ofxType = param.second->getType();
@@ -355,26 +348,27 @@ olive::plugin::PluginNode::PluginNode(
} else if (ofxType == kOfxParamTypeChoice) {
type = NodeValue::kCombo;
} else if (ofxType == kOfxParamTypeDouble2D ||
ofxType == kOfxParamTypeInteger2D){
type = NodeValue::kVec2;}
else if (ofxType == kOfxParamTypeDouble3D ||
ofxType == kOfxParamTypeInteger3D){
ofxType == kOfxParamTypeInteger2D) {
type = NodeValue::kVec2;
} else if (ofxType == kOfxParamTypeDouble3D ||
ofxType == kOfxParamTypeInteger3D) {
type = NodeValue::kVec3;
} else if (ofxType == kOfxParamTypeStrChoice){
} else if (ofxType == kOfxParamTypeStrChoice) {
type = NodeValue::kStrCombo;
}else if (ofxType == kOfxParamTypeBytes
|| ofxType == kOfxParamTypeCustom) {
} else if (ofxType == kOfxParamTypeBytes ||
ofxType == kOfxParamTypeCustom) {
type = NodeValue::kBinary;
} else if (ofxType == kOfxParamTypePushButton) {
type = NodeValue::kPushButton;
} else if (ofxType == kOfxParamTypeGroup ||
ofxType == kOfxParamTypePage) {
continue;
}else {
} else {
type = NodeValue::kNone;
}
const QString input_id = QString::fromStdString(param.second->getName());
const QString input_id =
QString::fromStdString(param.second->getName());
if (input_id.isEmpty()) {
continue;
}
@@ -395,8 +389,7 @@ olive::plugin::PluginNode::PluginNode(
if (is_secret) {
SetInputFlag(input_id, kInputFlagHidden);
}
const QString label =
QString::fromStdString(param.second->getLabel());
const QString label = QString::fromStdString(param.second->getLabel());
if (!label.isEmpty()) {
SetInputName(input_id, label);
} else {
@@ -413,33 +406,22 @@ olive::plugin::PluginNode::PluginNode(
page_for_param.value(input_id));
}
if (type == NodeValue::kColor) {
QString semantic =
DeduceColorSemantic(param.second, group_labels);
SetInputProperty(input_id,
QStringLiteral("color_semantic"),
QString semantic = DeduceColorSemantic(param.second, group_labels);
SetInputProperty(input_id, QStringLiteral("color_semantic"),
semantic);
const int dim =
(ofxType == kOfxParamTypeRGBA) ? 4 : 3;
double dmin[4] = {0, 0, 0, 0};
double dmax[4] = {1, 1, 1, 1};
props.getDoublePropertyN(kOfxParamPropDisplayMin,
dmin, dim);
props.getDoublePropertyN(kOfxParamPropDisplayMax,
dmax, dim);
SetInputProperty(input_id,
QStringLiteral("min"),
dmin[0]);
SetInputProperty(input_id,
QStringLiteral("max"),
dmax[0]);
const int dim = (ofxType == kOfxParamTypeRGBA) ? 4 : 3;
double dmin[4] = { 0, 0, 0, 0 };
double dmax[4] = { 1, 1, 1, 1 };
props.getDoublePropertyN(kOfxParamPropDisplayMin, dmin, dim);
props.getDoublePropertyN(kOfxParamPropDisplayMax, dmax, dim);
SetInputProperty(input_id, QStringLiteral("min"), dmin[0]);
SetInputProperty(input_id, QStringLiteral("max"), dmax[0]);
const QString hint = QString::fromStdString(
param.second->getHint());
const QString hint =
QString::fromStdString(param.second->getHint());
if (!hint.isEmpty()) {
SetInputProperty(input_id,
QStringLiteral("tooltip"),
hint);
SetInputProperty(input_id, QStringLiteral("tooltip"), hint);
}
}
if (type == NodeValue::kCombo || type == NodeValue::kStrCombo) {
@@ -447,8 +429,7 @@ olive::plugin::PluginNode::PluginNode(
QStringList option_values;
const int label_count =
props.getDimension(kOfxParamPropChoiceOption);
const int value_count =
props.getDimension(kOfxParamPropChoiceEnum);
const int value_count = props.getDimension(kOfxParamPropChoiceEnum);
for (int i = 0; i < label_count; ++i) {
const std::string &label =
@@ -478,13 +459,11 @@ olive::plugin::PluginNode::PluginNode(
indices[i] = i;
}
std::stable_sort(indices.begin(), indices.end(),
[&](int a, int b) {
return props.getIntProperty(
kOfxParamPropChoiceOrder,
std::stable_sort(
indices.begin(), indices.end(), [&](int a, int b) {
return props.getIntProperty(kOfxParamPropChoiceOrder,
a) <
props.getIntProperty(
kOfxParamPropChoiceOrder,
props.getIntProperty(kOfxParamPropChoiceOrder,
b);
});
@@ -520,15 +499,13 @@ olive::plugin::PluginNode::PluginNode(
has_texture_input = true;
}
const QString source_id =
QString::fromUtf8(kOfxImageEffectSimpleSourceClipName);
if (HasInputWithID(source_id)) {
SetEffectInput(source_id);
} else if (HasInputWithID(kTextureInput)) {
SetEffectInput(kTextureInput);
}
else {
} else {
if (has_texture_input) {
AddInput(kTextureInput, NodeValue::kTexture);
SetInputName(kTextureInput, tr("Texture"));
@@ -545,7 +522,6 @@ QString olive::plugin::PluginNode::Name() const
.getProps()
.getStringProperty(kOfxPropLabel)
.data();
}
QVector<olive::Node::CategoryID> olive::plugin::PluginNode::Category() const
@@ -565,7 +541,6 @@ QString olive::plugin::PluginNode::Description() const
.getProps()
.getStringProperty(kOfxPropPluginDescription)
.data();
}
void olive::plugin::PluginNode::ProcessSamples(const NodeValueRow &values,
const SampleBuffer &input,
@@ -669,8 +644,8 @@ QString olive::plugin::PluginNode::id() const
return plugin->getIdentifier().data();
}
olive::Node *olive::plugin::PluginNode::copy() const
{
olive::Node *olive::plugin::PluginNode::copy() const
{
if (!plugin_instance_) {
return nullptr;
}
@@ -689,10 +664,9 @@ QString olive::plugin::PluginNode::id() const
}
auto *node = new PluginNode(instance);
if (auto *olive_instance =
dynamic_cast<OlivePluginInstance *>(instance)) {
if (auto *olive_instance = dynamic_cast<OlivePluginInstance *>(instance)) {
olive_instance->setNode(
std::shared_ptr<PluginNode>(node, [](PluginNode *) {}));
}
return node;
}
}
+4 -7
View File
@@ -29,12 +29,11 @@ namespace plugin
{
const QString kTextureInput = QStringLiteral("tex_in");
class PluginNode : public olive::Node{
class PluginNode : public olive::Node {
public:
PluginNode(OFX::Host::ImageEffect::Instance* plugin) ;
PluginNode(OFX::Host::ImageEffect::Instance *plugin);
~PluginNode() override;
QString Name() const override;
QString id() const override;
QVector<CategoryID> Category() const override;
@@ -58,8 +57,8 @@ public:
* corresponding output if it's connected to one. If your node doesn't directly deal with time, the default behavior
* of the NodeParam objects will handle everything related to it automatically.
*/
void Value(const NodeValueRow &value,
const NodeGlobals &globals, NodeValueTable *table) const override;
void Value(const NodeValueRow &value, const NodeGlobals &globals,
NodeValueTable *table) const override;
/**
* @brief If Value() pushes a ShaderJob, this is the function that will process them.
@@ -82,11 +81,9 @@ private:
public slots:
void pushButtonClicked(QString name);
};
}
}
#endif //PLUGIN_H
+5 -7
View File
@@ -121,9 +121,8 @@ SerializedData Project::Load(QXmlStreamReader *reader)
}
}
const QString path = bundle_path.isEmpty()
? file_path
: bundle_path;
const QString path = bundle_path.isEmpty() ? file_path :
bundle_path;
if (!path.isEmpty()) {
plugin_paths.insert(path);
}
@@ -250,8 +249,7 @@ void Project::Save(QXmlStreamWriter *writer) const
seen.insert(key);
QMap<QString, QString> attrs;
attrs.insert(QStringLiteral("id"),
QString::fromStdString(id));
attrs.insert(QStringLiteral("id"), QString::fromStdString(id));
attrs.insert(QStringLiteral("major"), QString::number(major));
attrs.insert(QStringLiteral("minor"), QString::number(minor));
if (!bundle_path.isEmpty()) {
@@ -269,8 +267,8 @@ void Project::Save(QXmlStreamWriter *writer) const
writer->writeStartElement(QStringLiteral("plugins"));
for (const auto &entry : plugins_to_save) {
writer->writeStartElement(QStringLiteral("plugin"));
for (auto it = entry.second.cbegin();
it != entry.second.cend(); ++it) {
for (auto it = entry.second.cbegin(); it != entry.second.cend();
++it) {
writer->writeAttribute(it.key(), it.value());
}
writer->writeEndElement();
+5 -9
View File
@@ -242,11 +242,8 @@ void Footage::set_proxy_enabled(bool enabled)
}
}
void Footage::SetProxy(const QString &path,
ProxyManager::ProxyState state,
int video_stream_index,
int preset_version,
bool enabled)
void Footage::SetProxy(const QString &path, ProxyManager::ProxyState state,
int video_stream_index, int preset_version, bool enabled)
{
qDebug() << "Footage::SetProxy:" << filename() << "enabled=" << enabled
<< "state=" << ProxyManager::ProxyStateToString(state)
@@ -598,11 +595,10 @@ void Footage::SaveCustom(QXmlStreamWriter *writer) const
if (!proxy_path_.isEmpty() || proxy_enabled_) {
writer->writeStartElement(QStringLiteral("proxy"));
writer->writeAttribute(QStringLiteral("enabled"),
proxy_enabled_ ? QStringLiteral("1")
: QStringLiteral("0"));
proxy_enabled_ ? QStringLiteral("1") :
QStringLiteral("0"));
writer->writeAttribute(QStringLiteral("state"),
ProxyManager::ProxyStateToString(
proxy_state_));
ProxyManager::ProxyStateToString(proxy_state_));
writer->writeAttribute(QStringLiteral("stream"),
QString::number(proxy_video_stream_index_));
writer->writeAttribute(QStringLiteral("preset"),
+2 -5
View File
@@ -200,11 +200,8 @@ public:
return proxy_state_;
}
void SetProxy(const QString &path,
ProxyManager::ProxyState state,
int video_stream_index,
int preset_version,
bool enabled);
void SetProxy(const QString &path, ProxyManager::ProxyState state,
int video_stream_index, int preset_version, bool enabled);
void ClearProxy();
@@ -78,8 +78,7 @@ bool FootageDescription::Load(const QString &filename)
const QStringList split =
reader.readElementText().split('/');
if (split.size() == 2) {
SetSourceStartTime(
rational(split.at(0).toInt(),
SetSourceStartTime(rational(split.at(0).toInt(),
split.at(1).toInt()),
source);
}
+4 -1
View File
@@ -96,7 +96,10 @@ ProjectSerializer::Result ProjectSerializer::Load(Project *project,
return inner_result;
}
} else {
return kFileError;
Result r(kFileError);
r.SetDetails(QStringLiteral("Unable to open '%1': %2")
.arg(filename, project_file.errorString()));
return r;
}
}
+5 -4
View File
@@ -502,19 +502,20 @@ void NodeTraverser::ResolveJobs(NodeValue &val)
}
val.set_value(tex);
}
else if (plugin::PluginJob* plugin_job=dynamic_cast<plugin::PluginJob*>(base_job)) {
} else if (plugin::PluginJob *plugin_job =
dynamic_cast<plugin::PluginJob *>(
base_job)) {
VideoParams tex_params = job_tex->params();
// Force internal working format (F32) for plugin processing,
// matching FootageJob/GenerateJob behavior.
tex_params.set_format(GetCacheVideoParams().format());
tex_params.set_channel_count(VideoParams::kRGBAChannelCount);
tex_params.set_channel_count(
VideoParams::kRGBAChannelCount);
TexturePtr tex = CreateTexture(tex_params);
ProcessPluginJob(job_tex, tex, val.source());
val.set_value(tex);
}
// Cache resolved value
+3 -1
View File
@@ -147,7 +147,9 @@ protected:
return SampleBuffer();
}
virtual TexturePtr ProcessPluginJob(TexturePtr texture, TexturePtr destination, const Node *node);
virtual TexturePtr ProcessPluginJob(TexturePtr texture,
TexturePtr destination,
const Node *node);
SampleBuffer CreateSampleBuffer(const AudioParams &params,
const rational &length)
{
+2 -2
View File
@@ -14,6 +14,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
if(UNIX AND NOT APPLE)
if (UNIX AND NOT APPLE)
add_subdirectory(linux)
endif()
endif ()
+27
View File
@@ -0,0 +1,27 @@
# Maintainer: Oak Video Editor Team
pkgname=oak-video-editor
pkgver=@VERSION@
pkgrel=1
pkgdesc="Oak - Non-linear video editor"
arch=('x86_64')
url="https://oakvideoeditor.org"
license=('GPL3')
depends=('qt6-base' 'qt6-tools' 'ffmpeg' 'openimageio' 'opencolorio' 'openexr' 'expat' 'portaudio' 'mesa' 'vulkan-icd-loader' 'libxkbcommon' 'fmt')
makedepends=('cmake' 'ninja' 'git' 'pkgconf')
source=("${pkgname}-${pkgver}.tar.gz")
md5sums=('SKIP')
build() {
cd "${srcdir}/${pkgname}-${pkgver}"
cmake -S . -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr \
-DBUILD_QT6=ON \
-DCMAKE_DISABLE_FIND_PACKAGE_Vulkan=ON
cmake --build build
}
package() {
cd "${srcdir}/${pkgname}-${pkgver}"
DESTDIR="${pkgdir}" cmake --install build
}
+4 -4
View File
@@ -20,16 +20,16 @@
APPDIR=$(readlink -f $(dirname "$0"))
# Custom AppRun that ensures the AppImage doesn't dismount before olive-crashhandler exits
# Custom AppRun that ensures the AppImage doesn't dismount before oak-crashhandler exits
# Run main program
"$APPDIR/usr/bin/olive-editor" "$@"
"$APPDIR/usr/bin/oak-editor" "$@"
# Wait arbitrary amount of time
sleep 5
# While olive-crashhandler exists, keep sleeping
while [[ $(ps -aux | grep olive-crashhandler | grep -v grep) ]]
# While oak-crashhandler exists, keep sleeping
while [[ $(ps -aux | grep oak-crashhandler | grep -v grep) ]]
do
sleep 5
done
+5 -5
View File
@@ -15,17 +15,17 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
if(GIT_FOUND)
if (GIT_FOUND)
set(APPDATA_RELEASE_VERSION ${PROJECT_VERSION}-${GIT_HASH})
execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --pretty=%cd --date=short
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE APPDATA_RELEASE_DATE
OUTPUT_STRIP_TRAILING_WHITESPACE
)
else()
else ()
set(APPDATA_RELEASE_VERSION ${PROJECT_VERSION})
file(TIMESTAMP "${CMAKE_SOURCE_DIR}/CMakeLists.txt" APPDATA_RELEASE_DATE "%Y-%m-%d")
endif()
endif ()
configure_file(
org.oakvideoeditor.Oak.appdata.xml.in
@@ -36,7 +36,7 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.oakvideoeditor.Oak.appdata.xml DES
install(FILES org.oakvideoeditor.Oak.desktop DESTINATION share/applications)
install(FILES org.oakvideoeditor.Oak.xml DESTINATION share/mime/packages)
foreach(size 16 32 48 64 128 256 512)
foreach (size 16 32 48 64 128 256 512)
install(
FILES icons/${size}x${size}/org.oakvideoeditor.Oak.png
DESTINATION share/icons/hicolor/${size}x${size}/apps
@@ -45,4 +45,4 @@ foreach(size 16 32 48 64 128 256 512)
FILES icons/${size}x${size}/application-vnd.olive-project.png
DESTINATION share/icons/hicolor/${size}x${size}/mimetypes
)
endforeach()
endforeach ()
@@ -28,8 +28,8 @@
<p xml:lang="id">Oak Video Editor adalah aplikasi edit video bersifat non-linier yang bebas dan gratis, bertujuan untuk memberikan alternatif yang lengkap untuk aplikasi edit video profesional. Ini adalah fork komunitas dari Olive Video Editor.</p>
<p xml:lang="fr">Oak Video Editor est un éditeur vidéo non linéaire libre visant à fournir une alternative complète aux logiciels de montage vidéo professionnels haut de gamme. Il s'agit d'un fork communautaire d'Olive Video Editor.</p>
</description>
<url type="homepage">https://github.com/olive-editor/olive</url>
<url type="bugtracker">https://github.com/olive-editor/olive/issues</url>
<url type="homepage">https://github.com/OakVideoEditorCommunity/oak</url>
<url type="bugtracker">https://github.com/OakVideoEditorCommunity/oak/issues</url>
<screenshots>
<screenshot type="default"><image>https://olivevideoeditor.org/img/screenshot.1600.jpg</image></screenshot>
</screenshots>
@@ -4,7 +4,7 @@ Comment=Professional open-source non-linear video editor
Comment[fr]=Éditeur vidéo non-linéaire open-source professionnel
Comment[it]=Programma di montaggio video professionale open-source
Comment[id]=Aplikasi edit video yang non-linier, profesional serta sumbernya terbuka.
Exec=olive-editor %f
Exec=oak-editor %f
Icon=org.oakvideoeditor.Oak
Terminal=false
Type=Application
@@ -4,9 +4,9 @@
!define MUI_UNICON "uninstall icon.ico"
!define APP_NAME "Oak Video Editor"
!define APP_TARGET "olive-editor"
!define APP_TARGET "oak-editor"
!define MUI_FINISHPAGE_RUN "$INSTDIR\olive-editor.exe"
!define MUI_FINISHPAGE_RUN "$INSTDIR\oak-editor.exe"
SetCompressor lzma
@@ -38,7 +38,15 @@ InstallDir "$PROGRAMFILES32\${APP_NAME}"
Section "Oak Video Editor"
SectionIn RO
SetOutPath $INSTDIR
File /r olive-editor\*
File /r oak-editor\*
# Render worker process must live next to the editor binary
File "oak-editor\oak-render-worker.exe"
# Render backends must also live next to the editor binary
File "oak-editor\oakgl.dll"
File /nonfatal "oak-editor\oakvulkan.dll"
WriteUninstaller "$INSTDIR\uninstall.exe"
# Install Visual C++ 2010 Redistributable
@@ -61,8 +69,8 @@ Section "Associate *.ove files with Oak Video Editor"
WriteRegStr HKCR ".ove" "" "OakEditor.OVEFile"
WriteRegStr HKCR ".ove" "Content Type" "application/vnd.olive-project"
WriteRegStr HKCR "OakEditor.OVEFile" "" "Oak project file"
WriteRegStr HKCR "OakEditor.OVEFile\DefaultIcon" "" "$INSTDIR\olive-editor.exe,1"
WriteRegStr HKCR "OakEditor.OVEFile\shell\open\command" "" "$\"$INSTDIR\olive-editor.exe$\" $\"%1$\""
WriteRegStr HKCR "OakEditor.OVEFile\DefaultIcon" "" "$INSTDIR\oak-editor.exe,1"
WriteRegStr HKCR "OakEditor.OVEFile\shell\open\command" "" "$\"$INSTDIR\oak-editor.exe$\" $\"%1$\""
System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)'
SectionEnd
+1 -2
View File
@@ -131,8 +131,7 @@ void PanelWidget::changeEvent(QEvent *e)
if (e->type() == QEvent::WindowStateChange) {
if (isVisible() && !isMinimized()) {
emit shown(Qt::OtherFocusReason);
}
else {
} else {
emit hidden();
}
}
+1
View File
@@ -287,6 +287,7 @@ signals:
void CloseRequested();
void shown(Qt::FocusReason reason);
void hidden();
protected:
/**
* @brief paintEvent
+69 -77
View File
@@ -41,7 +41,8 @@ extern "C" {
#include <libswscale/swscale.h>
#include <libavutil/pixdesc.h>
}
namespace {
namespace
{
const std::string kBitDepthNoneStr(kOfxBitDepthNone);
const std::string kBitDepthByteStr(kOfxBitDepthByte);
const std::string kBitDepthShortStr(kOfxBitDepthShort);
@@ -117,16 +118,16 @@ static bool PackedDstInfo(AVPixelFormat fmt, int *channels,
}
}
static olive::AVFramePtr ReadbackTextureToFrame(olive::TexturePtr texture,
static olive::AVFramePtr
ReadbackTextureToFrame(olive::TexturePtr texture,
const olive::VideoParams &params)
{
if (!texture || texture->IsDummy() || !texture->renderer()) {
return nullptr;
}
AVPixelFormat pix_fmt =
olive::FFmpegUtils::GetFFmpegPixelFormat(params.format(),
params.channel_count());
AVPixelFormat pix_fmt = olive::FFmpegUtils::GetFFmpegPixelFormat(
params.format(), params.channel_count());
if (pix_fmt == AV_PIX_FMT_NONE) {
return nullptr;
}
@@ -145,15 +146,15 @@ static olive::AVFramePtr ReadbackTextureToFrame(olive::TexturePtr texture,
return nullptr;
}
const int linesize_pixels = BytesToPixels(frame->linesize[0], params);
texture->renderer()->DownloadFromTexture(texture->id(), params,
frame->data[0],
linesize_pixels);
texture->renderer()->DownloadFromTexture(
texture->id(), params, frame->data[0], linesize_pixels);
return frame;
}
olive::VideoParams rgba_params(
params.width(), params.height(), olive::core::PixelFormat::U8, 4,
params.pixel_aspect_ratio(), params.interlacing(), params.divider());
olive::VideoParams rgba_params(params.width(), params.height(),
olive::core::PixelFormat::U8, 4,
params.pixel_aspect_ratio(),
params.interlacing(), params.divider());
olive::AVFramePtr rgba_frame = olive::CreateAVFramePtr();
rgba_frame->format = AV_PIX_FMT_RGBA;
@@ -165,9 +166,8 @@ static olive::AVFramePtr ReadbackTextureToFrame(olive::TexturePtr texture,
const int linesize_pixels =
BytesToPixels(rgba_frame->linesize[0], rgba_params);
texture->renderer()->DownloadFromTexture(texture->id(), rgba_params,
rgba_frame->data[0],
linesize_pixels);
texture->renderer()->DownloadFromTexture(
texture->id(), rgba_params, rgba_frame->data[0], linesize_pixels);
olive::AVFramePtr dst = olive::CreateAVFramePtr();
dst->format = pix_fmt;
@@ -179,9 +179,8 @@ static olive::AVFramePtr ReadbackTextureToFrame(olive::TexturePtr texture,
SwsContext *sws_ctx = sws_getContext(
rgba_frame->width, rgba_frame->height,
static_cast<AVPixelFormat>(rgba_frame->format),
dst->width, dst->height, pix_fmt, SWS_POINT,
nullptr, nullptr, nullptr);
static_cast<AVPixelFormat>(rgba_frame->format), dst->width, dst->height,
pix_fmt, SWS_POINT, nullptr, nullptr, nullptr);
if (!sws_ctx) {
return rgba_frame;
}
@@ -218,9 +217,7 @@ static olive::AVFramePtr ConvertPackedFloatFrame(olive::AVFramePtr src,
return nullptr;
}
auto clamp01 = [](float v) -> float {
return std::clamp(v, 0.0f, 1.0f);
};
auto clamp01 = [](float v) -> float { return std::clamp(v, 0.0f, 1.0f); };
for (int y = 0; y < src->height; ++y) {
const float *src_row = reinterpret_cast<const float *>(
@@ -242,17 +239,13 @@ static olive::AVFramePtr ConvertPackedFloatFrame(olive::AVFramePtr src,
continue;
}
dst_row_u16[x * dst_channels + 0] =
static_cast<uint16_t>(
std::lround(clamp01(r) * 65535.0f));
static_cast<uint16_t>(std::lround(clamp01(r) * 65535.0f));
dst_row_u16[x * dst_channels + 1] =
static_cast<uint16_t>(
std::lround(clamp01(g) * 65535.0f));
static_cast<uint16_t>(std::lround(clamp01(g) * 65535.0f));
dst_row_u16[x * dst_channels + 2] =
static_cast<uint16_t>(
std::lround(clamp01(b) * 65535.0f));
static_cast<uint16_t>(std::lround(clamp01(b) * 65535.0f));
if (dst_channels == 4) {
dst_row_u16[x * dst_channels + 3] =
static_cast<uint16_t>(
dst_row_u16[x * dst_channels + 3] = static_cast<uint16_t>(
std::lround(clamp01(a) * 65535.0f));
}
}
@@ -270,18 +263,14 @@ static olive::AVFramePtr ConvertPackedFloatFrame(olive::AVFramePtr src,
continue;
}
dst_row[x * dst_channels + 0] =
static_cast<uint8_t>(
std::lround(clamp01(r) * 255.0f));
static_cast<uint8_t>(std::lround(clamp01(r) * 255.0f));
dst_row[x * dst_channels + 1] =
static_cast<uint8_t>(
std::lround(clamp01(g) * 255.0f));
static_cast<uint8_t>(std::lround(clamp01(g) * 255.0f));
dst_row[x * dst_channels + 2] =
static_cast<uint8_t>(
std::lround(clamp01(b) * 255.0f));
static_cast<uint8_t>(std::lround(clamp01(b) * 255.0f));
if (dst_channels == 4) {
dst_row[x * dst_channels + 3] =
static_cast<uint8_t>(
std::lround(clamp01(a) * 255.0f));
static_cast<uint8_t>(std::lround(clamp01(a) * 255.0f));
}
}
}
@@ -385,7 +374,7 @@ bool olive::plugin::OliveClipInstance::getConnected() const
return true;
}
#endif
if(images_.empty())
if (images_.empty())
return false;
return true;
}
@@ -394,7 +383,7 @@ bool olive::plugin::OliveClipInstance::getConnected() const
return true;
}
#endif
if(images_.empty())
if (images_.empty())
return false;
return true;
}
@@ -427,7 +416,8 @@ olive::plugin::OliveClipInstance::getImage(OfxTime time,
if (name_ == "Output") {
if (!images_.contains(time)) {
// make a new ref counted image
images_.insert(time, new Image(*const_cast<OliveClipInstance *>(this),
images_.insert(time,
new Image(*const_cast<OliveClipInstance *>(this),
params_, bounds, rod, true));
}
@@ -443,7 +433,7 @@ olive::plugin::OliveClipInstance::getImage(OfxTime time,
return images_[time];
} else {
if (images_.contains(time)) {
Image* image = images_.value(time);
Image *image = images_.value(time);
image->EnsureAllocatedFromParams(params_, bounds, rod, false);
image->addReference();
return image;
@@ -474,7 +464,7 @@ olive::plugin::OliveClipInstance::getImage(OfxTime time,
}
}
OFX::Host::ImageEffect::Image*
OFX::Host::ImageEffect::Image *
olive::plugin::OliveClipInstance::getOutputImage(OfxTime time)
{
if (images_.contains(time)) {
@@ -504,7 +494,8 @@ olive::plugin::OliveClipInstance::getOutputImage(OfxTime time)
return image;
}
olive::VideoParams olive::plugin::OliveClipInstance::getPluginPreferredParams() const
olive::VideoParams
olive::plugin::OliveClipInstance::getPluginPreferredParams() const
{
VideoParams result = params_;
@@ -584,7 +575,10 @@ void olive::plugin::OliveClipInstance::setParams(const VideoParams &params)
setComponents(getUnmappedComponents());
}
void olive::plugin::OliveClipInstance::setInputTexture(TexturePtr texture, OfxTime time, bool readback_cpu){
void olive::plugin::OliveClipInstance::setInputTexture(TexturePtr texture,
OfxTime time,
bool readback_cpu)
{
if (!texture) {
return;
}
@@ -622,9 +616,8 @@ void olive::plugin::OliveClipInstance::setInputTexture(TexturePtr texture, OfxTi
if (!frame || !frame->data[0]) {
frame = ReadbackTextureToFrame(texture, params_);
}
AVPixelFormat expected_fmt =
FFmpegUtils::GetFFmpegPixelFormat(params_.format(),
params_.channel_count());
AVPixelFormat expected_fmt = FFmpegUtils::GetFFmpegPixelFormat(
params_.format(), params_.channel_count());
if (expected_fmt == AV_PIX_FMT_NONE) {
return;
}
@@ -635,21 +628,20 @@ void olive::plugin::OliveClipInstance::setInputTexture(TexturePtr texture, OfxTi
static_cast<int>(std::ceil(rod_d.x2)),
static_cast<int>(std::ceil(rod_d.y2)) };
Image* image;
Image *image;
if (images_.contains(time)) {
image = images_.value(time);
image->EnsureAllocatedFromParams(params_, bounds, regionOfDefinition,
false);
} else {
pruneImagesCache();
image = new Image(*this, params_, bounds,
regionOfDefinition, false);
image = new Image(*this, params_, bounds, regionOfDefinition, false);
image->EnsureAllocatedFromParams(params_, bounds, regionOfDefinition,
false);
images_.insert(time, image);
}
uint8_t *dst = (uint8_t*)image->data();
uint8_t *dst = (uint8_t *)image->data();
if (!dst) {
return;
}
@@ -665,14 +657,16 @@ void olive::plugin::OliveClipInstance::setInputTexture(TexturePtr texture, OfxTi
// and SIGSEGV on Apple Silicon (where (int)NaN often evaluates to 0 or
// INT_MIN, causing huge offsets into bgrid._data).
if (params_.format() == core::PixelFormat::F32) {
const float *fptr = reinterpret_cast<const float*>(frame->data[0]);
const float *fptr = reinterpret_cast<const float *>(frame->data[0]);
int row_floats = frame->linesize[0] / static_cast<int>(sizeof(float));
bool has_nan = false;
for (int y = 0; y < params_.height() && !has_nan; ++y) {
for (int x = 0; x < params_.width() * params_.channel_count(); ++x) {
for (int x = 0; x < params_.width() * params_.channel_count();
++x) {
float v = fptr[y * row_floats + x];
if (std::isnan(v) || std::isinf(v)) {
qWarning() << "[PLUGIN] NaN/Inf detected in input frame at pixel ("
qWarning()
<< "[PLUGIN] NaN/Inf detected in input frame at pixel ("
<< x / params_.channel_count() << "," << y
<< ") channel=" << (x % params_.channel_count())
<< " value=" << v;
@@ -682,19 +676,19 @@ void olive::plugin::OliveClipInstance::setInputTexture(TexturePtr texture, OfxTi
}
}
if (has_nan) {
qWarning() << "[PLUGIN] Filling corrupted input frame with black to avoid CImg crash";
qWarning()
<< "[PLUGIN] Filling corrupted input frame with black to avoid CImg crash";
std::memset(dst, 0, image->row_bytes() * image->height());
return;
}
}
AVFramePtr src_frame = frame;
if (frame->format != expected_fmt ||
frame->width != params_.width() ||
if (frame->format != expected_fmt || frame->width != params_.width() ||
frame->height != params_.height()) {
if (PackedFloatChannels(static_cast<AVPixelFormat>(frame->format)) > 0) {
AVFramePtr converted =
ConvertPackedFloatFrame(frame, expected_fmt);
if (PackedFloatChannels(static_cast<AVPixelFormat>(frame->format)) >
0) {
AVFramePtr converted = ConvertPackedFloatFrame(frame, expected_fmt);
if (converted) {
src_frame = converted;
goto copy_pixels;
@@ -710,9 +704,8 @@ void olive::plugin::OliveClipInstance::setInputTexture(TexturePtr texture, OfxTi
SwsContext *sws_ctx = sws_getContext(
frame->width, frame->height,
static_cast<AVPixelFormat>(frame->format),
converted->width, converted->height,
static_cast<AVPixelFormat>(converted->format),
static_cast<AVPixelFormat>(frame->format), converted->width,
converted->height, static_cast<AVPixelFormat>(converted->format),
SWS_POINT, nullptr, nullptr, nullptr);
if (!sws_ctx) {
return;
@@ -727,18 +720,18 @@ void olive::plugin::OliveClipInstance::setInputTexture(TexturePtr texture, OfxTi
copy_pixels:
int bytes_per_component = params_.format().byte_count();
int bytes_per_row = params_.width() * params_.channel_count() *
bytes_per_component;
int bytes_per_row =
params_.width() * params_.channel_count() * bytes_per_component;
int src_row_bytes = src_frame->linesize[0];
int dst_row_bytes = image->row_bytes();
int copy_bytes = std::min(bytes_per_row,
std::min(src_row_bytes, dst_row_bytes));
int copy_bytes =
std::min(bytes_per_row, std::min(src_row_bytes, dst_row_bytes));
int copy_height = std::min(image->height(), src_frame->height);
const uint8_t *src = src_frame->data[0];
if (params_.format() == core::PixelFormat::F32) {
const float *src_f = reinterpret_cast<const float*>(src);
float *dst_f = reinterpret_cast<float*>(dst);
const float *src_f = reinterpret_cast<const float *>(src);
float *dst_f = reinterpret_cast<float *>(dst);
int src_stride = src_row_bytes / static_cast<int>(sizeof(float));
int dst_stride = dst_row_bytes / static_cast<int>(sizeof(float));
int floats_per_row = copy_bytes / static_cast<int>(sizeof(float));
@@ -754,7 +747,8 @@ copy_pixels:
}
}
if (has_nan) {
qWarning() << "[PLUGIN] NaN/Inf scrubbed from input frame data during copy";
qWarning()
<< "[PLUGIN] NaN/Inf scrubbed from input frame data during copy";
}
} else if (dst_row_bytes == src_row_bytes && src_row_bytes == copy_bytes) {
std::memcpy(dst, src, copy_bytes * copy_height);
@@ -764,8 +758,6 @@ copy_pixels:
copy_bytes);
}
}
}
void olive::plugin::OliveClipInstance::setOutputTexture(TexturePtr texture,
@@ -818,17 +810,17 @@ olive::plugin::OliveClipInstance::loadTexture(OfxTime time, const char *format,
bounds.x2 = std::min(bounds.x2, rod.x2);
bounds.y2 = std::min(bounds.y2, rod.y2);
const int bytes_per_row =
params_.width() * params_.channel_count() * params_.format().byte_count();
const int bytes_per_row = params_.width() * params_.channel_count() *
params_.format().byte_count();
const std::string &field = getFieldOrder();
const std::string unique_id = std::to_string(
reinterpret_cast<uintptr_t>(gl_texture.get())) + "_" +
const std::string unique_id =
std::to_string(reinterpret_cast<uintptr_t>(gl_texture.get())) + "_" +
std::to_string(static_cast<long long>(time));
const int texture_id = gl_texture->id().value<GLuint>();
OFX::Host::ImageEffect::Texture *texture =
new OFX::Host::ImageEffect::Texture(
*this, 1.0, 1.0, texture_id, GL_TEXTURE_2D, bounds, rod,
new OFX::Host::ImageEffect::Texture(*this, 1.0, 1.0, texture_id,
GL_TEXTURE_2D, bounds, rod,
bytes_per_row, field, unique_id);
texture->addReference();
return texture;
+17 -15
View File
@@ -35,17 +35,18 @@ namespace olive
{
namespace plugin
{
class OliveClipInstance: public OFX::Host::ImageEffect::ClipInstance {
class OliveClipInstance : public OFX::Host::ImageEffect::ClipInstance {
public:
OliveClipInstance(OFX::Host::ImageEffect::Instance* effectInstance,
OFX::Host::ImageEffect::ClipDescriptor& desc,VideoParams &params)
OliveClipInstance(OFX::Host::ImageEffect::Instance *effectInstance,
OFX::Host::ImageEffect::ClipDescriptor &desc,
VideoParams &params)
: ClipInstance(effectInstance, desc)
, params_(params)
, defaultRegionOfDefinitions_{0, 0, 0, 0}
, defaultRegionOfDefinitions_{ 0, 0, 0, 0 }
, name_(desc.getName())
{
}
OFX::Host::ImageEffect::Image* getOutputImage(OfxTime time);
OFX::Host::ImageEffect::Image *getOutputImage(OfxTime time);
const std::string &getUnmappedBitDepth() const override;
const std::string &getUnmappedComponents() const override;
@@ -56,21 +57,24 @@ public:
const std::string &getFieldOrder() const override;
bool getConnected() const override;
double getUnmappedFrameRate() const override;
void getUnmappedFrameRange(double &startFrame, double &endFrame) const override;
void getUnmappedFrameRange(double &startFrame,
double &endFrame) const override;
bool getContinuousSamples() const override;
OFX::Host::ImageEffect::Image* getImage(OfxTime time, const OfxRectD *optionalBounds) override;
OFX::Host::ImageEffect::Image *
getImage(OfxTime time, const OfxRectD *optionalBounds) override;
OfxRectD getRegionOfDefinition(OfxTime time) const override;
void setRegionOfDefinition(OfxRectD regionOfDefinition, OfxTime time);
void setDefaultRegionOfDefinition(OfxRectD regionOfDefinition);
void setParams(const VideoParams &params);
# ifdef OFX_SUPPORTS_OPENGLRENDER
OFX::Host::ImageEffect::Texture* loadTexture(OfxTime time,
const char *format,
#ifdef OFX_SUPPORTS_OPENGLRENDER
OFX::Host::ImageEffect::Texture *
loadTexture(OfxTime time, const char *format,
const OfxRectD *optionalBounds) override;
# endif
#endif
void setInputTexture(TexturePtr texture, OfxTime time, bool readback_cpu = true);
void setInputTexture(TexturePtr texture, OfxTime time,
bool readback_cpu = true);
void setOutputTexture(TexturePtr texture, OfxTime time);
// Get the plugin-preferred VideoParams based on base class _pixelDepth/_components
@@ -90,7 +94,7 @@ private:
OfxRectD defaultRegionOfDefinitions_;
std::string name_;
QMap<OfxTime, Image*> images_;
QMap<OfxTime, Image *> images_;
#ifdef OFX_SUPPORTS_OPENGLRENDER
QMap<OfxTime, TexturePtr> input_textures_;
QMap<OfxTime, TexturePtr> output_textures_;
@@ -99,6 +103,4 @@ private:
}
}
#endif //OLIVECLIP_H
+32 -24
View File
@@ -37,14 +37,18 @@
using namespace OFX::Host;
using namespace olive::plugin;
namespace olive {
namespace plugin {
namespace olive
{
namespace plugin
{
class PluginNode;
}
}
namespace {
void AddPluginPath(OFX::Host::PluginCache *cache, const QString &path, bool recurse = true)
namespace
{
void AddPluginPath(OFX::Host::PluginCache *cache, const QString &path,
bool recurse = true)
{
if (!cache || path.isEmpty()) {
return;
@@ -80,7 +84,8 @@ void olive::plugin::loadPlugins(QString path)
host = std::make_shared<OliveHost>();
Current::getInstance().setPluginHost(host);
imageEffectPluginCache = std::make_shared<ImageEffect::PluginCache>(*host);
imageEffectPluginCache =
std::make_shared<ImageEffect::PluginCache>(*host);
Current::getInstance().setPluginCache(imageEffectPluginCache);
imageEffectPluginCache->registerInCache(
@@ -92,7 +97,8 @@ void olive::plugin::loadPlugins(QString path)
const QString home_path = QDir::homePath();
AddPluginPath(cache, QDir(home_path).filePath(".OFX/Plugins"));
AddPluginPath(cache, QDir(home_path).filePath(".local/share/OFX/Plugins"));
AddPluginPath(cache, QDir(home_path).filePath(".local/share/olive/ofx/Plugins"));
AddPluginPath(cache,
QDir(home_path).filePath(".local/share/olive/ofx/Plugins"));
const QString app_dir = QCoreApplication::applicationDirPath();
AddPluginPath(cache, QDir(app_dir).filePath("../OFX/Plugins"));
@@ -109,10 +115,9 @@ void olive::plugin::loadPlugins(QString path)
}
OliveHost::~OliveHost()
{
}
void OliveHost::destroyInstance(OFX::Host::ImageEffect::Instance* instance)
void OliveHost::destroyInstance(OFX::Host::ImageEffect::Instance *instance)
{
if (!instance) {
return;
@@ -151,11 +156,12 @@ OliveHost::makeDescriptor(const std::string &bundlePath,
return desc;
}
ImageEffect::Instance* OliveHost::newInstance(void *clientData,
ImageEffect::ImageEffectPlugin* plugin,
ImageEffect::Descriptor& desc,
const std::string& context){
auto* instance = new OlivePluginInstance(
ImageEffect::Instance *
OliveHost::newInstance(void *clientData, ImageEffect::ImageEffectPlugin *plugin,
ImageEffect::Descriptor &desc,
const std::string &context)
{
auto *instance = new OlivePluginInstance(
plugin, desc, context, Current::getInstance().interactive());
if (clientData) {
auto *node = static_cast<PluginNode *>(clientData);
@@ -165,8 +171,9 @@ ImageEffect::Instance* OliveHost::newInstance(void *clientData,
instances_.append(std::shared_ptr<OlivePluginInstance>(instance));
return instance;
};
OfxStatus olive::plugin::OliveHost::vmessage(const char *type, const char *id, const char *format,
va_list args){
OfxStatus olive::plugin::OliveHost::vmessage(const char *type, const char *id,
const char *format, va_list args)
{
if (!type || !format) {
return kOfxStatFailed;
}
@@ -178,8 +185,7 @@ OfxStatus olive::plugin::OliveHost::vmessage(const char *type, const char *id, c
auto *app = qobject_cast<QApplication *>(QCoreApplication::instance());
if (!app) {
qWarning().noquote()
<< "OFX message:" << type << message;
qWarning().noquote() << "OFX message:" << type << message;
if (strcmp(type, kOfxMessageQuestion) == 0) {
return kOfxStatReplyNo;
}
@@ -187,8 +193,8 @@ OfxStatus olive::plugin::OliveHost::vmessage(const char *type, const char *id, c
}
if (strcmp(type, kOfxMessageQuestion) == 0) {
auto ret = QMessageBox::question(nullptr, "", message,
QMessageBox::Ok, QMessageBox::Cancel);
auto ret = QMessageBox::question(nullptr, "", message, QMessageBox::Ok,
QMessageBox::Cancel);
return (ret == QMessageBox::Ok) ? kOfxStatReplyYes : kOfxStatReplyNo;
}
@@ -203,8 +209,10 @@ OfxStatus olive::plugin::OliveHost::vmessage(const char *type, const char *id, c
return kOfxStatOK;
}
// TODO: Persistent messages shouldn't use pop-up window.
OfxStatus olive::plugin::OliveHost::setPersistentMessage(
const char *type, const char *id, const char *format, va_list args)
OfxStatus olive::plugin::OliveHost::setPersistentMessage(const char *type,
const char *id,
const char *format,
va_list args)
{
if (!type || !format) {
return kOfxStatFailed;
@@ -216,13 +224,13 @@ OfxStatus olive::plugin::OliveHost::setPersistentMessage(
QString message(buffer);
if (strcmp(type, kOfxMessageError) == 0) {
persistent_messages_.append({HostMessageType::Error, message});
persistent_messages_.append({ HostMessageType::Error, message });
QMessageBox::critical(nullptr, "", message);
} else if (strcmp(type, kOfxMessageWarning) == 0) {
persistent_messages_.append({HostMessageType::Warning, message});
persistent_messages_.append({ HostMessageType::Warning, message });
QMessageBox::warning(nullptr, "", message);
} else if (strcmp(type, kOfxMessageMessage) == 0) {
persistent_messages_.append({HostMessageType::Message, message});
persistent_messages_.append({ HostMessageType::Message, message });
QMessageBox::information(nullptr, "", message);
} else {
return kOfxStatFailed;
+32 -26
View File
@@ -43,7 +43,8 @@ namespace olive
{
namespace plugin
{
namespace {
namespace
{
const std::string kImageFieldNoneStr(kOfxImageFieldNone);
const std::string kImageFieldUpperStr(kOfxImageFieldUpper);
const std::string kImageFieldLowerStr(kOfxImageFieldLower);
@@ -62,7 +63,8 @@ QString FormatOfxMessage(const char *format, va_list args)
return QString::fromUtf8(buffer);
}
QByteArray dynamic_buffer(needed + 1, 0);
const int written = vsnprintf(dynamic_buffer.data(), dynamic_buffer.size(), format, args);
const int written =
vsnprintf(dynamic_buffer.data(), dynamic_buffer.size(), format, args);
if (written < 0) {
return QString();
}
@@ -136,7 +138,8 @@ ViewerOutput *GetActiveViewerOutput()
}
}
QList<TimelinePanel *> timelines = manager->GetPanelsOfType<TimelinePanel>();
QList<TimelinePanel *> timelines =
manager->GetPanelsOfType<TimelinePanel>();
for (TimelinePanel *panel : timelines) {
if (panel && panel->GetConnectedViewer()) {
return panel->GetConnectedViewer();
@@ -180,7 +183,8 @@ OfxStatus OlivePluginInstance::vmessage(const char *type, const char *id,
if (is_question) {
const auto ret = QMessageBox::question(
nullptr, "", message, QMessageBox::Ok, QMessageBox::Cancel);
result = (ret == QMessageBox::Ok) ? kOfxStatReplyYes : kOfxStatReplyNo;
result = (ret == QMessageBox::Ok) ? kOfxStatReplyYes :
kOfxStatReplyNo;
} else {
QMessageBox::information(nullptr, "", message);
result = kOfxStatOK;
@@ -191,7 +195,8 @@ OfxStatus OlivePluginInstance::vmessage(const char *type, const char *id,
show_message();
} else if (auto *app = QCoreApplication::instance()) {
if (is_question) {
QMetaObject::invokeMethod(app, show_message, Qt::BlockingQueuedConnection);
QMetaObject::invokeMethod(app, show_message,
Qt::BlockingQueuedConnection);
} else {
QMetaObject::invokeMethod(app, show_message, Qt::QueuedConnection);
}
@@ -201,8 +206,10 @@ OfxStatus OlivePluginInstance::vmessage(const char *type, const char *id,
return result;
}
OfxStatus OlivePluginInstance::setPersistentMessage(const char *type, const char *id,
const char *format, va_list args)
OfxStatus OlivePluginInstance::setPersistentMessage(const char *type,
const char *id,
const char *format,
va_list args)
{
const QString message = FormatOfxMessage(format, args);
if (message.isEmpty()) {
@@ -215,11 +222,13 @@ OfxStatus OlivePluginInstance::setPersistentMessage(const char *type, const char
error_type = ErrorType::Error;
}
// A warning
else if (strncmp(type, kOfxMessageWarning, strlen(kOfxMessageWarning)) == 0) {
else if (strncmp(type, kOfxMessageWarning, strlen(kOfxMessageWarning)) ==
0) {
error_type = ErrorType::Warning;
}
// A simple information
else if (strncmp(type, kOfxMessageMessage, strlen(kOfxMessageMessage)) == 0) {
else if (strncmp(type, kOfxMessageMessage, strlen(kOfxMessageMessage)) ==
0) {
error_type = ErrorType::Message;
} else {
return kOfxStatFailed;
@@ -272,7 +281,8 @@ void OlivePluginInstance::getProjectSize(double &xSize, double &ySize) const
xSize = params_.width() * par;
ySize = params_.height();
}
void OlivePluginInstance::getProjectOffset(double &xOffset, double &yOffset) const
void OlivePluginInstance::getProjectOffset(double &xOffset,
double &yOffset) const
{
double par = params_.pixel_aspect_ratio().toDouble();
xOffset = params_.x() * par;
@@ -343,8 +353,7 @@ OlivePluginInstance::newParam(const std::string &name,
return new Double3DInstance(node_, name, desc, this);
} else if (type == kOfxParamTypeInteger3D) {
return new Integer3DInstance(node_, name, desc, this);
} else if (type == kOfxParamTypeCustom ||
type == kOfxParamTypeBytes) {
} else if (type == kOfxParamTypeCustom || type == kOfxParamTypeBytes) {
return new CustomInstance(node_, name, desc, this);
} else if (type == kOfxParamTypeGroup) {
return new GroupInstance(desc, this);
@@ -366,8 +375,7 @@ OfxStatus OlivePluginInstance::editBegin(const std::string &name)
edit_param_count_ = 0;
if (!name.empty()) {
edit_first_label_ =
QCoreApplication::translate(
"OlivePluginInstance", "Change %1")
QCoreApplication::translate("OlivePluginInstance", "Change %1")
.arg(QString::fromStdString(name));
}
}
@@ -383,15 +391,14 @@ OfxStatus OlivePluginInstance::editEnd()
if (label.isEmpty()) {
if (edit_param_count_ <= 1 && !edit_first_label_.isEmpty()) {
label = edit_first_label_;
} else if (edit_param_count_ > 1 &&
!edit_first_label_.isEmpty()) {
label = QCoreApplication::translate(
"OlivePluginInstance", "%1 (+%2)")
} else if (edit_param_count_ > 1 && !edit_first_label_.isEmpty()) {
label = QCoreApplication::translate("OlivePluginInstance",
"%1 (+%2)")
.arg(edit_first_label_)
.arg(edit_param_count_ - 1);
} else {
label = QCoreApplication::translate(
"OlivePluginInstance", "Edit Parameters");
label = QCoreApplication::translate("OlivePluginInstance",
"Edit Parameters");
}
}
Core::instance()->undo_stack()->push(edit_command_, label);
@@ -450,9 +457,8 @@ void OlivePluginInstance::progressStart(const std::string &message,
progress_dialog_->deleteLater();
}
QString dialog_message = message.empty()
? QStringLiteral("Processing...")
: QString::fromStdString(message);
QString dialog_message = message.empty() ? QStringLiteral("Processing...") :
QString::fromStdString(message);
progress_dialog_ = new ::olive::ProgressDialog(
dialog_message, QStringLiteral("OpenFX"), nullptr);
@@ -547,11 +553,11 @@ void OlivePluginInstance::setCustomInArgs(const std::string &action,
OFX::Host::ImageEffect::ClipInstance *OlivePluginInstance::newClipInstance(
OFX::Host::ImageEffect::Instance *plugin,
OFX::Host::ImageEffect::ClipDescriptor *descriptor,
int index)
OFX::Host::ImageEffect::ClipDescriptor *descriptor, int index)
{
// Create a new clip instance
OliveClipInstance* clipInstance = new OliveClipInstance(plugin, *descriptor, params_);
OliveClipInstance *clipInstance =
new OliveClipInstance(plugin, *descriptor, params_);
// Initialize base class clip properties from VideoParams so that
// setupClipPreferencesArgs and plugin constructors (which may fetch
+38 -39
View File
@@ -33,7 +33,8 @@
#include <qcontainerfwd.h>
#include <qlist.h>
namespace olive {
namespace olive
{
inline bool IsGuiThread()
{
@@ -43,48 +44,44 @@ inline bool IsGuiThread()
return true;
}
class ProgressDialog;
namespace plugin {
namespace plugin
{
class PluginNode;
enum class ErrorType{
Error,
Warning,
Message
};
struct PersistentErrors{
enum class ErrorType { Error, Warning, Message };
struct PersistentErrors {
ErrorType type;
QString message;
};
class OlivePluginInstance : public OFX::Host::ImageEffect::Instance {
public:
OlivePluginInstance(
OFX::Host::ImageEffect::ImageEffectPlugin* plugin,
OFX::Host::ImageEffect::Descriptor& desc,
const std::string& context,
bool interactive)
OlivePluginInstance(OFX::Host::ImageEffect::ImageEffectPlugin *plugin,
OFX::Host::ImageEffect::Descriptor &desc,
const std::string &context, bool interactive)
: OFX::Host::ImageEffect::Instance(plugin, desc, context, interactive)
{
}
OlivePluginInstance(OlivePluginInstance& instance)
OlivePluginInstance(OlivePluginInstance &instance)
: Instance(instance._plugin, *instance._descriptor, instance._context,
instance._interactive)
{
// Do NOT shallow-copy _clips: Instance::~Instance() deletes them,
// which would cause a double-free. Clips are re-created in populate().
_created=instance._created;
_clipPrefsDirty=instance._clipPrefsDirty;
_continuousSamples=instance._continuousSamples;
_frameVarying=instance._frameVarying;
_outputPreMultiplication=instance._outputPreMultiplication;
_outputFielding=instance._outputFielding;
_outputFrameRate=instance._outputFrameRate;
_created = instance._created;
_clipPrefsDirty = instance._clipPrefsDirty;
_continuousSamples = instance._continuousSamples;
_frameVarying = instance._frameVarying;
_outputPreMultiplication = instance._outputPreMultiplication;
_outputFielding = instance._outputFielding;
_outputFrameRate = instance._outputFrameRate;
}
explicit OlivePluginInstance(Instance & instance):Instance(instance){};
explicit OlivePluginInstance(Instance &instance)
: Instance(instance) {};
~OlivePluginInstance() override;
const std::string &getDefaultOutputFielding() const override;
void setVideoParam(VideoParams params)
{
this->params_=params;
this->params_ = params;
}
void setNode(std::shared_ptr<PluginNode> node);
std::shared_ptr<PluginNode> node() const
@@ -99,20 +96,16 @@ public:
{
return _created;
}
OFX::Host::ImageEffect::ClipInstance *newClipInstance(
OFX::Host::ImageEffect::Instance *plugin,
OFX::Host::ImageEffect::ClipInstance *
newClipInstance(OFX::Host::ImageEffect::Instance *plugin,
OFX::Host::ImageEffect::ClipDescriptor *descriptor,
int index) override;
OfxStatus vmessage(const char* type,
const char* id,
const char* format,
OfxStatus vmessage(const char *type, const char *id, const char *format,
va_list args) override;
OfxStatus setPersistentMessage(const char* type,
const char* id,
const char* format,
va_list args) override;
OfxStatus setPersistentMessage(const char *type, const char *id,
const char *format, va_list args) override;
OfxStatus clearPersistentMessage() override;
int persistentMessageCount() const
@@ -124,9 +117,9 @@ public:
return persistentErrors_;
}
void getProjectSize(double& xSize, double& ySize) const override;
void getProjectOffset(double& xOffset, double& yOffset) const override;
void getProjectExtent(double& xSize, double& ySize) const override;
void getProjectSize(double &xSize, double &ySize) const override;
void getProjectOffset(double &xOffset, double &yOffset) const override;
void getProjectExtent(double &xSize, double &ySize) const override;
// The pixel aspect ratio of the current project
double getProjectPixelAspectRatio() const override;
@@ -153,14 +146,16 @@ public:
/// make a parameter instance
///
/// Client host code needs to implement this
OFX::Host::Param::Instance* newParam(const std::string& name, OFX::Host::Param::Descriptor& Descriptor) override;
OFX::Host::Param::Instance *
newParam(const std::string &name,
OFX::Host::Param::Descriptor &Descriptor) override;
void SubmitUndoCommand(UndoCommand *command, const QString &label);
/// Triggered when the plug-in calls OfxParameterSuiteV1::paramEditBegin
///
/// Client host code needs to implement this
virtual OfxStatus editBegin(const std::string& name) override;
virtual OfxStatus editBegin(const std::string &name) override;
/// Triggered when the plug-in calls OfxParameterSuiteV1::paramEditEnd
///
@@ -210,7 +205,6 @@ public:
void setCustomInArgs(const std::string &action,
OFX::Host::Property::Set &inArgs) override;
private:
QList<PersistentErrors> persistentErrors_;
VideoParams params_;
@@ -224,8 +218,13 @@ private:
bool progress_cancelled_ = false;
bool progress_active_ = false;
bool open_gl_enabled_ = false;
public:
std::mutex& mutex() { return mutex_; }
std::mutex &mutex()
{
return mutex_;
}
private:
std::mutex mutex_;
};
+22 -35
View File
@@ -23,8 +23,10 @@
#include <algorithm>
namespace olive {
namespace plugin {
namespace olive
{
namespace plugin
{
static const char *PixelDepthToOfx(core::PixelFormat format)
{
@@ -69,16 +71,14 @@ Image::Image(OFX::Host::ImageEffect::ClipInstance &clip_instance)
, premultiplied_alpha_(false)
, channel_count_(0)
, row_bytes_(0)
, bounds_{0, 0, 0, 0}
, rod_{0, 0, 0, 0}
, bounds_{ 0, 0, 0, 0 }
, rod_{ 0, 0, 0, 0 }
{
}
Image::Image(OFX::Host::ImageEffect::ClipInstance &clip_instance,
const VideoParams &params,
const OfxRectI &bounds,
const OfxRectI &rod,
bool clear)
const VideoParams &params, const OfxRectI &bounds,
const OfxRectI &rod, bool clear)
: OFX::Host::ImageEffect::Image(clip_instance)
, width_(0)
, height_(0)
@@ -86,8 +86,8 @@ Image::Image(OFX::Host::ImageEffect::ClipInstance &clip_instance,
, premultiplied_alpha_(false)
, channel_count_(0)
, row_bytes_(0)
, bounds_{0, 0, 0, 0}
, rod_{0, 0, 0, 0}
, bounds_{ 0, 0, 0, 0 }
, rod_{ 0, 0, 0, 0 }
{
AllocateFromParams(params, bounds, rod, clear);
}
@@ -97,24 +97,17 @@ Image::~Image()
}
void Image::AllocateFromParams(const VideoParams &params,
const OfxRectI &bounds,
const OfxRectI &rod,
const OfxRectI &bounds, const OfxRectI &rod,
bool clear)
{
Allocate(bounds.x2 - bounds.x1,
bounds.y2 - bounds.y1,
params.format(),
params.channel_count(),
params.premultiplied_alpha(),
bounds,
rod,
Allocate(bounds.x2 - bounds.x1, bounds.y2 - bounds.y1, params.format(),
params.channel_count(), params.premultiplied_alpha(), bounds, rod,
clear);
}
void Image::EnsureAllocatedFromParams(const VideoParams &params,
const OfxRectI &bounds,
const OfxRectI &rod,
bool clear)
const OfxRectI &rod, bool clear)
{
bool same = (width_ == bounds.x2 - bounds.x1) &&
(height_ == bounds.y2 - bounds.y1) &&
@@ -133,14 +126,9 @@ void Image::EnsureAllocatedFromParams(const VideoParams &params,
}
}
void Image::Allocate(int width,
int height,
core::PixelFormat format,
int channel_count,
bool premultiplied_alpha,
const OfxRectI &bounds,
const OfxRectI &rod,
bool clear)
void Image::Allocate(int width, int height, core::PixelFormat format,
int channel_count, bool premultiplied_alpha,
const OfxRectI &bounds, const OfxRectI &rod, bool clear)
{
width_ = width;
height_ = height;
@@ -174,11 +162,10 @@ void Image::Allocate(int width,
setIntProperty(kOfxImagePropRegionOfDefinition, rod.y2, 3);
setStringProperty(kOfxImageEffectPropComponents,
ComponentsToOfx(channel_count_));
setStringProperty(kOfxImageEffectPropPixelDepth,
PixelDepthToOfx(format_));
setStringProperty(kOfxImageEffectPropPixelDepth, PixelDepthToOfx(format_));
setStringProperty(kOfxImageEffectPropPreMultiplication,
premultiplied_alpha_ ? kOfxImagePreMultiplied
: kOfxImageUnPreMultiplied);
premultiplied_alpha_ ? kOfxImagePreMultiplied :
kOfxImageUnPreMultiplied);
}
core::PixelFormat Image::pixel_format()
@@ -212,7 +199,7 @@ bool Image::premultiplied_alpha()
int Image::width()
{
int bounds[4] = {0};
int bounds[4] = { 0 };
getIntPropertyN(kOfxImagePropBounds, bounds, 4);
width_ = bounds[2] - bounds[0];
return width_;
@@ -220,7 +207,7 @@ int Image::width()
int Image::height()
{
int bounds[4] = {0};
int bounds[4] = { 0 };
getIntPropertyN(kOfxImagePropBounds, bounds, 4);
height_ = bounds[3] - bounds[1];
return height_;
+11 -18
View File
@@ -37,12 +37,11 @@ class Image : public OFX::Host::ImageEffect::Image {
public:
Image(OFX::Host::ImageEffect::ClipInstance &clip_instance);
Image(OFX::Host::ImageEffect::ClipInstance &clip_instance,
const VideoParams &params,
const OfxRectI &bounds,
const OfxRectI &rod,
bool clear = true);
const VideoParams &params, const OfxRectI &bounds,
const OfxRectI &rod, bool clear = true);
~Image();
uint8_t *data() {
uint8_t *data()
{
return (uint8_t *)getPointerProperty(kOfxImagePropData);
}
int width();
@@ -51,26 +50,20 @@ public:
bool premultiplied_alpha();
int channel_count();
void AllocateFromParams(const VideoParams &params,
const OfxRectI &bounds,
const OfxRectI &rod,
bool clear = true);
void AllocateFromParams(const VideoParams &params, const OfxRectI &bounds,
const OfxRectI &rod, bool clear = true);
void EnsureAllocatedFromParams(const VideoParams &params,
const OfxRectI &bounds,
const OfxRectI &rod,
const OfxRectI &bounds, const OfxRectI &rod,
bool clear = false);
void Allocate(int width,
int height,
core::PixelFormat format,
int channel_count,
bool premultiplied_alpha,
const OfxRectI &bounds,
const OfxRectI &rod,
void Allocate(int width, int height, core::PixelFormat format,
int channel_count, bool premultiplied_alpha,
const OfxRectI &bounds, const OfxRectI &rod,
bool clear = true);
int row_bytes() const
{
return row_bytes_;
}
protected:
std::vector<uint8_t> image_;
int width_;
+1 -3
View File
@@ -17,7 +17,6 @@
*
*/
#include "paraminstance.h"
#include "OlivePluginInstance.h"
@@ -35,8 +34,7 @@ void SubmitUndoCommand(const std::shared_ptr<PluginNode> &node,
if (node) {
auto *instance = node->getPluginInstance();
auto *olive_instance =
dynamic_cast<OlivePluginInstance *>(instance);
auto *olive_instance = dynamic_cast<OlivePluginInstance *>(instance);
if (olive_instance) {
olive_instance->SubmitUndoCommand(command, label);
return;
+175 -146
View File
@@ -34,14 +34,15 @@
#include "undo/undocommand.h"
#include "common/Current.h"
#include <iostream>
#include <mutex>
#include <qlogging.h>
namespace olive
{
namespace plugin
{
inline bool IsNormalisedCoordinateSystem(
const OFX::Host::Param::Descriptor &descriptor)
inline bool
IsNormalisedCoordinateSystem(const OFX::Host::Param::Descriptor &descriptor)
{
return descriptor.getDefaultCoordinateSystem() ==
kOfxParamCoordinatesNormalised;
@@ -83,8 +84,10 @@ class PushbuttonInstance : public OFX::Host::Param::PushbuttonInstance,
protected:
std::shared_ptr<PluginNode> node;
OFX::Host::Param::Descriptor *_descriptor;
public:
PushbuttonInstance(std::shared_ptr<PluginNode> effect, const std::string &name,
PushbuttonInstance(std::shared_ptr<PluginNode> effect,
const std::string &name,
OFX::Host::Param::Descriptor &descriptor,
OFX::Host::Param::SetInstance *paramSet = nullptr)
: OFX::Host::Param::PushbuttonInstance(descriptor, paramSet)
@@ -102,12 +105,15 @@ class IntegerInstance : public OFX::Host::Param::IntegerInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> _node;
OFX::Host::Param::Descriptor& _descriptor;
OFX::Host::Param::Descriptor &_descriptor;
QString id;
mutable std::mutex no_node_mutex_;
bool has_value_ = false;
int value_ = 0;
public:
IntegerInstance(std::shared_ptr<PluginNode>node, OFX::Host::Param::Descriptor &descriptor,
IntegerInstance(std::shared_ptr<PluginNode> node,
OFX::Host::Param::Descriptor &descriptor,
OFX::Host::Param::SetInstance *paramSet = nullptr)
: OFX::Host::Param::IntegerInstance(descriptor, paramSet)
, _node(node)
@@ -115,7 +121,8 @@ public:
, id(_descriptor.getName().c_str())
{
try {
value_ = _descriptor.getProperties().getIntProperty(kOfxParamPropDefault);
value_ = _descriptor.getProperties().getIntProperty(
kOfxParamPropDefault);
has_value_ = true;
} catch (...) {
value_ = 0;
@@ -129,41 +136,45 @@ public:
OfxStatus get(int &a)
{
if (!_node) {
std::lock_guard<std::mutex> lock(no_node_mutex_);
a = has_value_ ? value_ : 0;
return kOfxStatOK;
}
if (id.isEmpty()) {
return kOfxStatErrBadHandle;
}
QVariant variant=_node->GetStandardValue(id);
QVariant variant = _node->GetStandardValue(id);
if (variant.canConvert<int>()) {
a=variant.toInt();
a = variant.toInt();
return kOfxStatOK;
}
a=0;
a = 0;
return kOfxStatErrValue;
}
OfxStatus get(OfxTime time, int &data)
{
if (!_node) {
std::lock_guard<std::mutex> lock(no_node_mutex_);
data = has_value_ ? value_ : 0;
return kOfxStatOK;
}
if (id.isEmpty()) {
return kOfxStatErrBadHandle;
}
QVariant variant=_node->GetValueAtTime(id, rational::fromDouble(time));
QVariant variant =
_node->GetValueAtTime(id, rational::fromDouble(time));
if (variant.canConvert<int>()) {
data=variant.toInt();
data = variant.toInt();
return kOfxStatOK;
}
data=0;
data = 0;
return kOfxStatErrValue;
}
OfxStatus set(int data)
{
if (!_node) {
std::lock_guard<std::mutex> lock(no_node_mutex_);
value_ = data;
has_value_ = true;
return kOfxStatOK;
@@ -179,6 +190,7 @@ public:
OfxStatus set(OfxTime time, int data)
{
if (!_node) {
std::lock_guard<std::mutex> lock(no_node_mutex_);
value_ = data;
has_value_ = true;
return kOfxStatOK;
@@ -196,11 +208,13 @@ class DoubleInstance : public OFX::Host::Param::DoubleInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> node;
OFX::Host::Param::Descriptor& _descriptor;
OFX::Host::Param::Descriptor &_descriptor;
bool has_value_ = false;
double value_ = 0.0;
public:
DoubleInstance(std::shared_ptr<PluginNode> effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor,
DoubleInstance(std::shared_ptr<PluginNode> effect, const std::string &name,
OFX::Host::Param::Descriptor &descriptor,
OFX::Host::Param::SetInstance *paramSet = nullptr)
: OFX::Host::Param::DoubleInstance(descriptor, paramSet)
, node(effect)
@@ -208,7 +222,8 @@ public:
{
(void)name;
try {
value_ = _descriptor.getProperties().getDoubleProperty(kOfxParamPropDefault);
value_ = _descriptor.getProperties().getDoubleProperty(
kOfxParamPropDefault);
has_value_ = true;
} catch (...) {
value_ = 0.0;
@@ -219,13 +234,14 @@ public:
{
node = new_node;
}
OfxStatus get(double& data)
OfxStatus get(double &data)
{
if (!node) {
data = has_value_ ? value_ : 0.0;
return kOfxStatOK;
}
QVariant variant = node->GetStandardValue(_descriptor.getName().c_str());
QVariant variant =
node->GetStandardValue(_descriptor.getName().c_str());
if (variant.canConvert<double>()) {
data = variant.toDouble();
if (IsNormalisedCoordinateSystem(_descriptor)) {
@@ -238,14 +254,13 @@ public:
data = 0.0;
return kOfxStatErrValue;
}
OfxStatus get(OfxTime time, double& data)
OfxStatus get(OfxTime time, double &data)
{
if (!node) {
data = has_value_ ? value_ : 0.0;
return kOfxStatOK;
}
QVariant variant =
node->GetValueAtTime(_descriptor.getName().c_str(),
QVariant variant = node->GetValueAtTime(_descriptor.getName().c_str(),
rational::fromDouble(time));
if (variant.canConvert<double>()) {
data = variant.toDouble();
@@ -293,17 +308,17 @@ public:
val = ToCanonical(val, xSize);
}
auto command = new MultiUndoCommand();
Node::SetValueAtTime(
NodeInput(node.get(), _descriptor.getName().c_str()),
Node::SetValueAtTime(NodeInput(node.get(),
_descriptor.getName().c_str()),
rational::fromDouble(time), val, 0, command, true);
SubmitUndoCommand(node, command, ParamChangeLabel(_descriptor));
return kOfxStatOK;
}
OfxStatus derive(OfxTime, double&)
OfxStatus derive(OfxTime, double &)
{
return kOfxStatErrUnsupported;
}
OfxStatus integrate(OfxTime, OfxTime, double&)
OfxStatus integrate(OfxTime, OfxTime, double &)
{
return kOfxStatErrUnsupported;
}
@@ -313,16 +328,18 @@ class BooleanInstance : public OFX::Host::Param::BooleanInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> node;
OFX::Host::Param::Descriptor& _descriptor;
OFX::Host::Param::Descriptor &_descriptor;
bool has_value_ = false;
bool value_ = false;
bool DefaultValue() const
{
return _descriptor.getProperties()
.getIntProperty(kOfxParamPropDefault) != 0;
return _descriptor.getProperties().getIntProperty(
kOfxParamPropDefault) != 0;
}
public:
BooleanInstance(std::shared_ptr<PluginNode> effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor,
BooleanInstance(std::shared_ptr<PluginNode> effect, const std::string &name,
OFX::Host::Param::Descriptor &descriptor,
OFX::Host::Param::SetInstance *paramSet = nullptr)
: OFX::Host::Param::BooleanInstance(descriptor, paramSet)
, node(effect)
@@ -336,13 +353,14 @@ public:
{
node = new_node;
}
OfxStatus get(bool& data)
OfxStatus get(bool &data)
{
if (!node) {
data = has_value_ ? value_ : false;
return kOfxStatOK;
}
QVariant variant = node->GetStandardValue(_descriptor.getName().c_str());
QVariant variant =
node->GetStandardValue(_descriptor.getName().c_str());
if (variant.canConvert<bool>()) {
data = variant.toBool();
return kOfxStatOK;
@@ -350,17 +368,18 @@ public:
data = DefaultValue();
return kOfxStatOK;
}
OfxStatus get(OfxTime time, bool& data)
OfxStatus get(OfxTime time, bool &data)
{
if (!node) {
data = has_value_ ? value_ : false;
return kOfxStatOK;
}
QVariant variant =
node->GetValueAtTime(_descriptor.getName().c_str(),
QVariant variant = node->GetValueAtTime(_descriptor.getName().c_str(),
rational::fromDouble(time));
if (variant.isNull()){
qWarning().noquote()<<"Boolean get failed: Varient is null" << time << rational::fromDouble(time).toDouble();
if (variant.isNull()) {
qWarning().noquote()
<< "Boolean get failed: Varient is null" << time
<< rational::fromDouble(time).toDouble();
}
if (!variant.isValid()) {
qWarning().noquote()
@@ -408,11 +427,13 @@ class ChoiceInstance : public OFX::Host::Param::ChoiceInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> node;
OFX::Host::Param::Descriptor& _descriptor;
OFX::Host::Param::Descriptor &_descriptor;
bool has_value_ = false;
int value_ = 0;
public:
ChoiceInstance(std::shared_ptr<PluginNode> effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor,
ChoiceInstance(std::shared_ptr<PluginNode> effect, const std::string &name,
OFX::Host::Param::Descriptor &descriptor,
OFX::Host::Param::SetInstance *paramSet = nullptr)
: OFX::Host::Param::ChoiceInstance(descriptor, paramSet)
, node(effect)
@@ -420,7 +441,8 @@ public:
{
(void)name;
try {
value_ = _descriptor.getProperties().getIntProperty(kOfxParamPropDefault);
value_ = _descriptor.getProperties().getIntProperty(
kOfxParamPropDefault);
has_value_ = true;
} catch (...) {
value_ = 0;
@@ -431,13 +453,14 @@ public:
{
node = new_node;
}
OfxStatus get(int& data)
OfxStatus get(int &data)
{
if (!node) {
data = has_value_ ? value_ : 0;
return kOfxStatOK;
}
QVariant variant = node->GetStandardValue(_descriptor.getName().c_str());
QVariant variant =
node->GetStandardValue(_descriptor.getName().c_str());
if (variant.canConvert<int>()) {
data = variant.toInt();
return kOfxStatOK;
@@ -445,14 +468,13 @@ public:
data = 0;
return kOfxStatErrValue;
}
OfxStatus get(OfxTime time, int& data)
OfxStatus get(OfxTime time, int &data)
{
if (!node) {
data = has_value_ ? value_ : 0;
return kOfxStatOK;
}
QVariant variant =
node->GetValueAtTime(_descriptor.getName().c_str(),
QVariant variant = node->GetValueAtTime(_descriptor.getName().c_str(),
rational::fromDouble(time));
if (variant.canConvert<int>()) {
data = variant.toInt();
@@ -495,11 +517,13 @@ class RGBAInstance : public OFX::Host::Param::RGBAInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> node;
OFX::Host::Param::Descriptor& _descriptor;
OFX::Host::Param::Descriptor &_descriptor;
bool has_value_ = false;
double value_[4] = {0.0, 0.0, 0.0, 0.0};
double value_[4] = { 0.0, 0.0, 0.0, 0.0 };
public:
RGBAInstance(std::shared_ptr<PluginNode> effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor,
RGBAInstance(std::shared_ptr<PluginNode> effect, const std::string &name,
OFX::Host::Param::Descriptor &descriptor,
OFX::Host::Param::SetInstance *paramSet = nullptr)
: OFX::Host::Param::RGBAInstance(descriptor, paramSet)
, node(effect)
@@ -511,7 +535,7 @@ public:
{
node = new_node;
}
OfxStatus get(double& r,double& g,double& b,double& a)
OfxStatus get(double &r, double &g, double &b, double &a)
{
if (!node) {
if (has_value_) {
@@ -534,7 +558,7 @@ public:
a = static_cast<double>(c.alpha());
return kOfxStatOK;
}
OfxStatus get(OfxTime time, double& r,double& g,double& b,double& a)
OfxStatus get(OfxTime time, double &r, double &g, double &b, double &a)
{
if (!node) {
if (has_value_) {
@@ -558,7 +582,7 @@ public:
a = static_cast<double>(c.alpha());
return kOfxStatOK;
}
OfxStatus set(double r,double g,double b,double a)
OfxStatus set(double r, double g, double b, double a)
{
if (!node) {
value_[0] = r;
@@ -576,7 +600,7 @@ public:
SubmitUndoCommand(node, command, ParamChangeLabel(_descriptor));
return kOfxStatOK;
}
OfxStatus set(OfxTime time, double r,double g,double b,double a)
OfxStatus set(OfxTime time, double r, double g, double b, double a)
{
if (!node) {
value_[0] = r;
@@ -588,29 +612,30 @@ public:
}
auto command = new MultiUndoCommand();
const QString name = _descriptor.getName().c_str();
Node::SetValueAtTime(NodeInput(node.get(), name), rational::fromDouble(time),
r, 0, command, true);
Node::SetValueAtTime(NodeInput(node.get(), name), rational::fromDouble(time),
g, 1, command, true);
Node::SetValueAtTime(NodeInput(node.get(), name), rational::fromDouble(time),
b, 2, command, true);
Node::SetValueAtTime(NodeInput(node.get(), name), rational::fromDouble(time),
a, 3, command, true);
Node::SetValueAtTime(NodeInput(node.get(), name),
rational::fromDouble(time), r, 0, command, true);
Node::SetValueAtTime(NodeInput(node.get(), name),
rational::fromDouble(time), g, 1, command, true);
Node::SetValueAtTime(NodeInput(node.get(), name),
rational::fromDouble(time), b, 2, command, true);
Node::SetValueAtTime(NodeInput(node.get(), name),
rational::fromDouble(time), a, 3, command, true);
SubmitUndoCommand(node, command, ParamChangeLabel(_descriptor));
return kOfxStatOK;
}
};
class RGBInstance : public OFX::Host::Param::RGBInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> node;
OFX::Host::Param::Descriptor& _descriptor;
OFX::Host::Param::Descriptor &_descriptor;
bool has_value_ = false;
double value_[3] = {0.0, 0.0, 0.0};
double value_[3] = { 0.0, 0.0, 0.0 };
public:
RGBInstance(std::shared_ptr<PluginNode> effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor,
RGBInstance(std::shared_ptr<PluginNode> effect, const std::string &name,
OFX::Host::Param::Descriptor &descriptor,
OFX::Host::Param::SetInstance *paramSet = nullptr)
: OFX::Host::Param::RGBInstance(descriptor, paramSet)
, node(effect)
@@ -622,7 +647,7 @@ public:
{
node = new_node;
}
OfxStatus get(double& r,double& g,double& b)
OfxStatus get(double &r, double &g, double &b)
{
if (!node) {
if (has_value_) {
@@ -643,7 +668,7 @@ public:
b = static_cast<double>(c.blue());
return kOfxStatOK;
}
OfxStatus get(OfxTime time, double& r,double& g,double& b)
OfxStatus get(OfxTime time, double &r, double &g, double &b)
{
if (!node) {
if (has_value_) {
@@ -665,7 +690,7 @@ public:
b = static_cast<double>(c.blue());
return kOfxStatOK;
}
OfxStatus set(double r,double g,double b)
OfxStatus set(double r, double g, double b)
{
if (!node) {
value_[0] = r;
@@ -682,7 +707,7 @@ public:
SubmitUndoCommand(node, command, ParamChangeLabel(_descriptor));
return kOfxStatOK;
}
OfxStatus set(OfxTime time, double r,double g,double b)
OfxStatus set(OfxTime time, double r, double g, double b)
{
if (!node) {
value_[0] = r;
@@ -693,12 +718,12 @@ public:
}
auto command = new MultiUndoCommand();
const QString name = _descriptor.getName().c_str();
Node::SetValueAtTime(NodeInput(node.get(), name), rational::fromDouble(time),
r, 0, command, true);
Node::SetValueAtTime(NodeInput(node.get(), name), rational::fromDouble(time),
g, 1, command, true);
Node::SetValueAtTime(NodeInput(node.get(), name), rational::fromDouble(time),
b, 2, command, true);
Node::SetValueAtTime(NodeInput(node.get(), name),
rational::fromDouble(time), r, 0, command, true);
Node::SetValueAtTime(NodeInput(node.get(), name),
rational::fromDouble(time), g, 1, command, true);
Node::SetValueAtTime(NodeInput(node.get(), name),
rational::fromDouble(time), b, 2, command, true);
SubmitUndoCommand(node, command, ParamChangeLabel(_descriptor));
return kOfxStatOK;
}
@@ -708,11 +733,14 @@ class Double2DInstance : public OFX::Host::Param::Double2DInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> node;
OFX::Host::Param::Descriptor& _descriptor;
OFX::Host::Param::Descriptor &_descriptor;
bool has_value_ = false;
double value_[2] = {0.0, 0.0};
double value_[2] = { 0.0, 0.0 };
public:
Double2DInstance(std::shared_ptr<PluginNode> effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor,
Double2DInstance(std::shared_ptr<PluginNode> effect,
const std::string &name,
OFX::Host::Param::Descriptor &descriptor,
OFX::Host::Param::SetInstance *paramSet = nullptr)
: OFX::Host::Param::Double2DInstance(descriptor, paramSet)
, node(effect)
@@ -724,7 +752,7 @@ public:
{
node = new_node;
}
OfxStatus get(double& x,double& y)
OfxStatus get(double &x, double &y)
{
if (!node) {
if (has_value_) {
@@ -735,8 +763,7 @@ public:
}
return kOfxStatOK;
}
QVector2D vec =
node->GetStandardValue(_descriptor.getName().c_str())
QVector2D vec = node->GetStandardValue(_descriptor.getName().c_str())
.value<QVector2D>();
x = static_cast<double>(vec.x());
y = static_cast<double>(vec.y());
@@ -748,7 +775,7 @@ public:
}
return kOfxStatOK;
}
OfxStatus get(OfxTime time,double& x,double& y)
OfxStatus get(OfxTime time, double &x, double &y)
{
if (!node) {
if (has_value_) {
@@ -759,8 +786,7 @@ public:
}
return kOfxStatOK;
}
QVector2D vec =
node->GetValueAtTime(_descriptor.getName().c_str(),
QVector2D vec = node->GetValueAtTime(_descriptor.getName().c_str(),
rational::fromDouble(time))
.value<QVector2D>();
x = static_cast<double>(vec.x());
@@ -773,7 +799,7 @@ public:
}
return kOfxStatOK;
}
OfxStatus set(double x,double y)
OfxStatus set(double x, double y)
{
if (!node) {
value_[0] = x;
@@ -795,7 +821,7 @@ public:
SubmitUndoCommand(node, command, ParamChangeLabel(_descriptor));
return kOfxStatOK;
}
OfxStatus set(OfxTime time,double x,double y)
OfxStatus set(OfxTime time, double x, double y)
{
if (!node) {
value_[0] = x;
@@ -812,10 +838,10 @@ public:
}
auto command = new MultiUndoCommand();
const QString name = _descriptor.getName().c_str();
Node::SetValueAtTime(NodeInput(node.get(), name), rational::fromDouble(time),
xv, 0, command, true);
Node::SetValueAtTime(NodeInput(node.get(), name), rational::fromDouble(time),
yv, 1, command, true);
Node::SetValueAtTime(NodeInput(node.get(), name),
rational::fromDouble(time), xv, 0, command, true);
Node::SetValueAtTime(NodeInput(node.get(), name),
rational::fromDouble(time), yv, 1, command, true);
SubmitUndoCommand(node, command, ParamChangeLabel(_descriptor));
return kOfxStatOK;
}
@@ -825,11 +851,14 @@ class Integer2DInstance : public OFX::Host::Param::Integer2DInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> node;
OFX::Host::Param::Descriptor& _descriptor;
OFX::Host::Param::Descriptor &_descriptor;
bool has_value_ = false;
int value_[2] = {0, 0};
int value_[2] = { 0, 0 };
public:
Integer2DInstance(std::shared_ptr<PluginNode> effect, const std::string& name, OFX::Host::Param::Descriptor& descriptor,
Integer2DInstance(std::shared_ptr<PluginNode> effect,
const std::string &name,
OFX::Host::Param::Descriptor &descriptor,
OFX::Host::Param::SetInstance *paramSet = nullptr)
: OFX::Host::Param::Integer2DInstance(descriptor, paramSet)
, node(effect)
@@ -841,7 +870,7 @@ public:
{
node = new_node;
}
OfxStatus get(int& x,int& y)
OfxStatus get(int &x, int &y)
{
if (!node) {
if (has_value_) {
@@ -852,14 +881,13 @@ public:
}
return kOfxStatOK;
}
QVector2D vec =
node->GetStandardValue(_descriptor.getName().c_str())
QVector2D vec = node->GetStandardValue(_descriptor.getName().c_str())
.value<QVector2D>();
x = static_cast<int>(vec.x());
y = static_cast<int>(vec.y());
return kOfxStatOK;
}
OfxStatus get(OfxTime time,int& x,int& y)
OfxStatus get(OfxTime time, int &x, int &y)
{
if (!node) {
if (has_value_) {
@@ -870,15 +898,14 @@ public:
}
return kOfxStatOK;
}
QVector2D vec =
node->GetValueAtTime(_descriptor.getName().c_str(),
QVector2D vec = node->GetValueAtTime(_descriptor.getName().c_str(),
rational::fromDouble(time))
.value<QVector2D>();
x = static_cast<int>(vec.x());
y = static_cast<int>(vec.y());
return kOfxStatOK;
}
OfxStatus set(int x,int y)
OfxStatus set(int x, int y)
{
if (!node) {
value_[0] = x;
@@ -893,7 +920,7 @@ public:
SubmitUndoCommand(node, command, ParamChangeLabel(_descriptor));
return kOfxStatOK;
}
OfxStatus set(OfxTime time,int x,int y)
OfxStatus set(OfxTime time, int x, int y)
{
if (!node) {
value_[0] = x;
@@ -903,10 +930,10 @@ public:
}
auto command = new MultiUndoCommand();
const QString name = _descriptor.getName().c_str();
Node::SetValueAtTime(NodeInput(node.get(), name), rational::fromDouble(time),
x, 0, command, true);
Node::SetValueAtTime(NodeInput(node.get(), name), rational::fromDouble(time),
y, 1, command, true);
Node::SetValueAtTime(NodeInput(node.get(), name),
rational::fromDouble(time), x, 0, command, true);
Node::SetValueAtTime(NodeInput(node.get(), name),
rational::fromDouble(time), y, 1, command, true);
SubmitUndoCommand(node, command, ParamChangeLabel(_descriptor));
return kOfxStatOK;
}
@@ -916,12 +943,14 @@ class Double3DInstance : public OFX::Host::Param::Double3DInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> node;
OFX::Host::Param::Descriptor& _descriptor;
OFX::Host::Param::Descriptor &_descriptor;
bool has_value_ = false;
double value_[3] = {0.0, 0.0, 0.0};
double value_[3] = { 0.0, 0.0, 0.0 };
public:
Double3DInstance(std::shared_ptr<PluginNode> effect, const std::string& name,
OFX::Host::Param::Descriptor& descriptor,
Double3DInstance(std::shared_ptr<PluginNode> effect,
const std::string &name,
OFX::Host::Param::Descriptor &descriptor,
OFX::Host::Param::SetInstance *paramSet = nullptr)
: OFX::Host::Param::Double3DInstance(descriptor, paramSet)
, node(effect)
@@ -933,7 +962,7 @@ public:
{
node = new_node;
}
OfxStatus get(double& x,double& y,double& z)
OfxStatus get(double &x, double &y, double &z)
{
if (!node) {
if (has_value_) {
@@ -945,8 +974,7 @@ public:
}
return kOfxStatOK;
}
QVector3D vec =
node->GetStandardValue(_descriptor.getName().c_str())
QVector3D vec = node->GetStandardValue(_descriptor.getName().c_str())
.value<QVector3D>();
x = static_cast<double>(vec.x());
y = static_cast<double>(vec.y());
@@ -960,7 +988,7 @@ public:
}
return kOfxStatOK;
}
OfxStatus get(OfxTime time,double& x,double& y,double& z)
OfxStatus get(OfxTime time, double &x, double &y, double &z)
{
if (!node) {
if (has_value_) {
@@ -972,8 +1000,7 @@ public:
}
return kOfxStatOK;
}
QVector3D vec =
node->GetValueAtTime(_descriptor.getName().c_str(),
QVector3D vec = node->GetValueAtTime(_descriptor.getName().c_str(),
rational::fromDouble(time))
.value<QVector3D>();
x = static_cast<double>(vec.x());
@@ -988,7 +1015,7 @@ public:
}
return kOfxStatOK;
}
OfxStatus set(double x,double y,double z)
OfxStatus set(double x, double y, double z)
{
if (!node) {
value_[0] = x;
@@ -1012,7 +1039,7 @@ public:
SubmitUndoCommand(node, command, ParamChangeLabel(_descriptor));
return kOfxStatOK;
}
OfxStatus set(OfxTime time,double x,double y,double z)
OfxStatus set(OfxTime time, double x, double y, double z)
{
if (!node) {
value_[0] = x;
@@ -1046,12 +1073,14 @@ class Integer3DInstance : public OFX::Host::Param::Integer3DInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> node;
OFX::Host::Param::Descriptor& _descriptor;
OFX::Host::Param::Descriptor &_descriptor;
bool has_value_ = false;
int value_[3] = {0, 0, 0};
int value_[3] = { 0, 0, 0 };
public:
Integer3DInstance(std::shared_ptr<PluginNode> effect, const std::string& name,
OFX::Host::Param::Descriptor& descriptor,
Integer3DInstance(std::shared_ptr<PluginNode> effect,
const std::string &name,
OFX::Host::Param::Descriptor &descriptor,
OFX::Host::Param::SetInstance *paramSet = nullptr)
: OFX::Host::Param::Integer3DInstance(descriptor, paramSet)
, node(effect)
@@ -1063,7 +1092,7 @@ public:
{
node = new_node;
}
OfxStatus get(int& x,int& y,int& z)
OfxStatus get(int &x, int &y, int &z)
{
if (!node) {
if (has_value_) {
@@ -1075,15 +1104,14 @@ public:
}
return kOfxStatOK;
}
QVector3D vec =
node->GetStandardValue(_descriptor.getName().c_str())
QVector3D vec = node->GetStandardValue(_descriptor.getName().c_str())
.value<QVector3D>();
x = static_cast<int>(vec.x());
y = static_cast<int>(vec.y());
z = static_cast<int>(vec.z());
return kOfxStatOK;
}
OfxStatus get(OfxTime time,int& x,int& y,int& z)
OfxStatus get(OfxTime time, int &x, int &y, int &z)
{
if (!node) {
if (has_value_) {
@@ -1095,8 +1123,7 @@ public:
}
return kOfxStatOK;
}
QVector3D vec =
node->GetValueAtTime(_descriptor.getName().c_str(),
QVector3D vec = node->GetValueAtTime(_descriptor.getName().c_str(),
rational::fromDouble(time))
.value<QVector3D>();
x = static_cast<int>(vec.x());
@@ -1104,7 +1131,7 @@ public:
z = static_cast<int>(vec.z());
return kOfxStatOK;
}
OfxStatus set(int x,int y,int z)
OfxStatus set(int x, int y, int z)
{
if (!node) {
value_[0] = x;
@@ -1120,7 +1147,7 @@ public:
SubmitUndoCommand(node, command, ParamChangeLabel(_descriptor));
return kOfxStatOK;
}
OfxStatus set(OfxTime time,int x,int y,int z)
OfxStatus set(OfxTime time, int x, int y, int z)
{
if (!node) {
value_[0] = x;
@@ -1146,12 +1173,13 @@ class StringInstance : public OFX::Host::Param::StringInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> node;
OFX::Host::Param::Descriptor& _descriptor;
OFX::Host::Param::Descriptor &_descriptor;
bool has_value_ = false;
std::string value_;
public:
StringInstance(std::shared_ptr<PluginNode> effect, const std::string& name,
OFX::Host::Param::Descriptor& descriptor,
StringInstance(std::shared_ptr<PluginNode> effect, const std::string &name,
OFX::Host::Param::Descriptor &descriptor,
OFX::Host::Param::SetInstance *paramSet = nullptr)
: OFX::Host::Param::StringInstance(descriptor, paramSet)
, node(effect)
@@ -1159,7 +1187,8 @@ public:
{
(void)name;
try {
value_ = _descriptor.getProperties().getStringProperty(kOfxParamPropDefault);
value_ = _descriptor.getProperties().getStringProperty(
kOfxParamPropDefault);
has_value_ = true;
} catch (...) {
value_.clear();
@@ -1176,7 +1205,8 @@ public:
data = has_value_ ? value_ : std::string();
return kOfxStatOK;
}
QVariant variant = node->GetStandardValue(_descriptor.getName().c_str());
QVariant variant =
node->GetStandardValue(_descriptor.getName().c_str());
if (variant.canConvert<QString>()) {
data = variant.toString().toStdString();
return kOfxStatOK;
@@ -1190,8 +1220,7 @@ public:
data = has_value_ ? value_ : std::string();
return kOfxStatOK;
}
QVariant variant =
node->GetValueAtTime(_descriptor.getName().c_str(),
QVariant variant = node->GetValueAtTime(_descriptor.getName().c_str(),
rational::fromDouble(time));
if (variant.canConvert<QString>()) {
data = variant.toString().toStdString();
@@ -1223,9 +1252,10 @@ public:
return kOfxStatOK;
}
auto command = new MultiUndoCommand();
Node::SetValueAtTime(
NodeInput(node.get(), _descriptor.getName().c_str()),
rational::fromDouble(time), QString::fromUtf8(data), 0, command, true);
Node::SetValueAtTime(NodeInput(node.get(),
_descriptor.getName().c_str()),
rational::fromDouble(time),
QString::fromUtf8(data), 0, command, true);
SubmitUndoCommand(node, command, ParamChangeLabel(_descriptor));
return kOfxStatOK;
}
@@ -1235,12 +1265,13 @@ class CustomInstance : public OFX::Host::Param::CustomInstance,
public NodeBoundParam {
protected:
std::shared_ptr<PluginNode> node;
OFX::Host::Param::Descriptor& _descriptor;
OFX::Host::Param::Descriptor &_descriptor;
bool has_value_ = false;
std::string value_;
public:
CustomInstance(std::shared_ptr<PluginNode> effect, const std::string& name,
OFX::Host::Param::Descriptor& descriptor,
CustomInstance(std::shared_ptr<PluginNode> effect, const std::string &name,
OFX::Host::Param::Descriptor &descriptor,
OFX::Host::Param::SetInstance *paramSet = nullptr)
: OFX::Host::Param::CustomInstance(descriptor, paramSet)
, node(effect)
@@ -1258,7 +1289,8 @@ public:
data = has_value_ ? value_ : std::string();
return kOfxStatOK;
}
QVariant variant = node->GetStandardValue(_descriptor.getName().c_str());
QVariant variant =
node->GetStandardValue(_descriptor.getName().c_str());
if (variant.canConvert<QByteArray>()) {
data = variant.toByteArray().toStdString();
return kOfxStatOK;
@@ -1276,8 +1308,7 @@ public:
data = has_value_ ? value_ : std::string();
return kOfxStatOK;
}
QVariant variant =
node->GetValueAtTime(_descriptor.getName().c_str(),
QVariant variant = node->GetValueAtTime(_descriptor.getName().c_str(),
rational::fromDouble(time));
if (variant.canConvert<QByteArray>()) {
data = variant.toByteArray().toStdString();
@@ -1323,7 +1354,7 @@ public:
class GroupInstance : public OFX::Host::Param::GroupInstance {
public:
GroupInstance(OFX::Host::Param::Descriptor& descriptor,
GroupInstance(OFX::Host::Param::Descriptor &descriptor,
OFX::Host::Param::SetInstance *paramSet = nullptr)
: OFX::Host::Param::GroupInstance(descriptor, paramSet)
{
@@ -1332,7 +1363,7 @@ public:
class PageInstance : public OFX::Host::Param::PageInstance {
public:
PageInstance(OFX::Host::Param::Descriptor& descriptor,
PageInstance(OFX::Host::Param::Descriptor &descriptor,
OFX::Host::Param::SetInstance *paramSet = nullptr)
: OFX::Host::Param::PageInstance(descriptor, paramSet)
{
@@ -1341,6 +1372,4 @@ public:
}
}
#endif // HOST_DEMO_PARAM_INSTANCE_H
+21 -16
View File
@@ -37,21 +37,24 @@ DynamicRenderer::~DynamicRenderer()
// system libGL/libvulkan loader is never mistaken for an Oak render backend.
QString DynamicRenderer::LibraryFilename() const
{
const QString base = backend_ == QStringLiteral("vulkan")
? QStringLiteral("oakvulkan")
: QStringLiteral("oakgl");
const QString base = backend_ == QStringLiteral("vulkan") ?
QStringLiteral("oakvulkan") :
QStringLiteral("oakgl");
#if defined(Q_OS_WIN)
const QString filename = base + QStringLiteral(".dll");
#elif defined(Q_OS_MAC)
const QString filename = QStringLiteral("lib") + base + QStringLiteral(".dylib");
const QString filename =
QStringLiteral("lib") + base + QStringLiteral(".dylib");
#else
const QString filename = QStringLiteral("lib") + base + QStringLiteral(".so");
const QString filename =
QStringLiteral("lib") + base + QStringLiteral(".so");
#endif
const QDir app_dir(QCoreApplication::applicationDirPath());
const QStringList candidates = {
app_dir.filePath(filename),
app_dir.filePath(QDir(QStringLiteral("render_backends")).filePath(filename)),
app_dir.filePath(
QDir(QStringLiteral("render_backends")).filePath(filename)),
app_dir.filePath(QDir(QStringLiteral("../lib")).filePath(filename)),
app_dir.filePath(QDir(QStringLiteral("../../lib")).filePath(filename)),
app_dir.filePath(QDir(QStringLiteral("../app")).filePath(filename)),
@@ -77,9 +80,9 @@ bool DynamicRenderer::Load()
library_.setFileName(LibraryFilename());
if (!library_.load()) {
if (backend_ == QStringLiteral("vulkan")) {
qWarning() << "Failed to load Vulkan render backend"
<< library_.fileName() << library_.errorString()
<< "falling back to OpenGL backend";
qWarning()
<< "Failed to load Vulkan render backend" << library_.fileName()
<< library_.errorString() << "falling back to OpenGL backend";
backend_ = QStringLiteral("opengl");
library_.setFileName(LibraryFilename());
}
@@ -128,7 +131,8 @@ bool DynamicRenderer::ResolveFunctions()
ResetFunctions();
#define RESOLVE(member, type, symbol) \
member = reinterpret_cast<type>(library_.resolve(symbol)); \
if (!member) return false
if (!member) \
return false
RESOLVE(create_, OakBackendCreateFn, "oak_renderer_create");
RESOLVE(destroy_, OakBackendDestroyFn, "oak_renderer_destroy");
@@ -281,8 +285,8 @@ void DynamicRenderer::DestroyNativeShader(QVariant shader)
// Uploads CPU pixel data into a backend texture through the dynamic ABI.
void DynamicRenderer::UploadToTexture(const QVariant &handle,
const VideoParams &params, const void *data,
int linesize)
const VideoParams &params,
const void *data, int linesize)
{
upload_to_texture_(handle_, &handle, &params, data, linesize);
}
@@ -313,9 +317,9 @@ Color DynamicRenderer::GetPixelFromTexture(Texture *texture, const QPointF &pt)
// null so callers can avoid GL-only paths.
QOpenGLContext *DynamicRenderer::OpenGLContext() const
{
return opengl_context_ && handle_
? static_cast<QOpenGLContext *>(opengl_context_(handle_))
: nullptr;
return opengl_context_ && handle_ ?
static_cast<QOpenGLContext *>(opengl_context_(handle_)) :
nullptr;
}
// Reports the effective backend after any load-time fallback has completed.
@@ -340,7 +344,8 @@ void DynamicRenderer::Blit(QVariant shader, AcceleratedJob &job,
// Allocates a backend-native texture and wraps its opaque handle in QVariant.
QVariant DynamicRenderer::CreateNativeTexture(int width, int height, int depth,
PixelFormat format, int channel_count,
PixelFormat format,
int channel_count,
const void *data, int linesize)
{
QVariant out;
+3 -3
View File
@@ -42,9 +42,9 @@ public:
// Runs backend post-init setup.
virtual void PostInit() override;
// Clears either a native texture destination or the backend output target.
virtual void ClearDestination(Texture *texture = nullptr,
double r = 0.0, double g = 0.0,
double b = 0.0, double a = 0.0) override;
virtual void ClearDestination(Texture *texture = nullptr, double r = 0.0,
double g = 0.0, double b = 0.0,
double a = 0.0) override;
// Creates a native shader through the dynamic backend.
virtual QVariant CreateNativeShader(ShaderCode code) override;
// Destroys a native shader through the dynamic backend.
+7 -7
View File
@@ -7,7 +7,8 @@
#ifdef _WIN32
#define OAK_RENDER_BACKEND_EXPORT extern "C" __declspec(dllexport)
#else
#define OAK_RENDER_BACKEND_EXPORT extern "C" __attribute__((visibility("default")))
#define OAK_RENDER_BACKEND_EXPORT \
extern "C" __attribute__((visibility("default")))
#endif
#ifdef __cplusplus
@@ -69,11 +70,9 @@ typedef void (*OakBackendClearDestinationFn)(OakRenderBackendHandle handle,
void *texture, double r, double g,
double b, double a);
/* Creates a native texture and writes a QVariant-compatible handle. */
typedef void (*OakBackendCreateNativeTextureFn)(OakRenderBackendHandle handle,
int width, int height, int depth,
int format, int channel_count,
const void *data, int linesize,
void *out_variant);
typedef void (*OakBackendCreateNativeTextureFn)(
OakRenderBackendHandle handle, int width, int height, int depth, int format,
int channel_count, const void *data, int linesize, void *out_variant);
/* Destroys a native texture represented by a QVariant-compatible handle. */
typedef void (*OakBackendDestroyNativeTextureFn)(OakRenderBackendHandle handle,
const void *variant);
@@ -98,7 +97,8 @@ typedef void (*OakBackendDownloadFromTextureFn)(OakRenderBackendHandle handle,
typedef void (*OakBackendFlushFn)(OakRenderBackendHandle handle);
/* Reads one pixel from a texture. */
typedef void (*OakBackendGetPixelFromTextureFn)(OakRenderBackendHandle handle,
void *texture, const void *point,
void *texture,
const void *point,
void *out_color);
/* Executes a shader blit job. */
typedef void (*OakBackendBlitFn)(OakRenderBackendHandle handle,
+11 -8
View File
@@ -125,7 +125,8 @@ bool Renderer::GetColorContext(const ColorTransformJob &color_job,
OCIO::GpuShaderDesc::TextureType channel =
OCIO::GpuShaderDesc::TEXTURE_RGB_CHANNEL;
OCIO::Interpolation interpolation = OCIO::INTERP_LINEAR;
#if OCIO_VERSION_MAJOR > 2 || (OCIO_VERSION_MAJOR == 2 && OCIO_VERSION_MINOR >= 3)
#if OCIO_VERSION_MAJOR > 2 || \
(OCIO_VERSION_MAJOR == 2 && OCIO_VERSION_MINOR >= 3)
OCIO::GpuShaderDesc::TextureDimensions dimensions =
OCIO::GpuShaderDesc::TEXTURE_2D;
shader_desc->getTexture(i, tex_name, sampler_name, width, height,
@@ -149,12 +150,14 @@ bool Renderer::GetColorContext(const ColorTransformJob &color_job,
}
// Allocate 1D LUT
int lut_channels = (channel ==
OCIO::GpuShaderDesc::TEXTURE_RED_CHANNEL) ?
int lut_channels =
(channel == OCIO::GpuShaderDesc::TEXTURE_RED_CHANNEL) ?
1 :
VideoParams::kRGBChannelCount;
VideoParams lut_params(width, height, PixelFormat::F32, lut_channels);
color_ctx.lut1d_textures[i].texture = CreateTexture(lut_params, values);
VideoParams lut_params(width, height, PixelFormat::F32,
lut_channels);
color_ctx.lut1d_textures[i].texture =
CreateTexture(lut_params, values);
color_ctx.lut1d_textures[i].name = sampler_name;
color_ctx.lut1d_textures[i].interpolation =
(interpolation == OCIO::INTERP_NEAREST) ? Texture::kNearest :
@@ -176,9 +179,9 @@ void Renderer::BlitColorManaged(const ColorTransformJob &color_job,
ShaderJob fallback_job;
fallback_job.Insert(QStringLiteral("ove_maintex"),
color_job.GetInputTexture());
fallback_job.Insert(
QStringLiteral("ove_mvpmat"),
NodeValue(NodeValue::kMatrix, color_job.GetTransformMatrix()));
fallback_job.Insert(QStringLiteral("ove_mvpmat"),
NodeValue(NodeValue::kMatrix,
color_job.GetTransformMatrix()));
if (destination) {
BlitToTexture(GetDefaultShader(), fallback_job, destination,
+4 -2
View File
@@ -67,7 +67,8 @@ ColorProcessor::ColorProcessor(ColorManager *config, const QString &input,
} else {
auto group = OCIO::GroupTransform::Create();
const char *out_cs = OCIO::LookTransform::GetLooksResultColorSpace(
const char *out_cs =
OCIO::LookTransform::GetLooksResultColorSpace(
ocio_config, ocio_config->getCurrentContext(),
transform.look().toUtf8());
@@ -105,7 +106,8 @@ ColorProcessor::ColorProcessor(ColorManager *config, const QString &input,
ColorProcessor::ColorProcessor(OCIO::ConstProcessorRcPtr processor)
{
processor_ = processor;
cpu_processor_ = processor_ ? processor_->getDefaultCPUProcessor() : nullptr;
cpu_processor_ = processor_ ? processor_->getDefaultCPUProcessor() :
nullptr;
}
void ColorProcessor::ConvertFrame(Frame *f)
+12 -6
View File
@@ -44,9 +44,12 @@ size_t FrameSlotPool::BytesNeeded(uint32_t slot_count, size_t slot_data_bytes)
{
const uint32_t ring_cap = RingCapacity(slot_count);
size_t total = AlignUp(sizeof(Header), kAlign);
total += AlignUp(SpscRingBuffer::BytesNeeded(ring_cap), kAlign); // free ring
total += AlignUp(SpscRingBuffer::BytesNeeded(ring_cap), kAlign); // ready ring
total += AlignUp(sizeof(FrameSlotMeta) * slot_count, kAlign); // metadata array
total +=
AlignUp(SpscRingBuffer::BytesNeeded(ring_cap), kAlign); // free ring
total +=
AlignUp(SpscRingBuffer::BytesNeeded(ring_cap), kAlign); // ready ring
total +=
AlignUp(sizeof(FrameSlotMeta) * slot_count, kAlign); // metadata array
total += AlignUp(slot_data_bytes, kAlign) * slot_count; // pixel data blocks
return total;
}
@@ -111,9 +114,12 @@ FrameSlotPool FrameSlotPool::Attach(void *mem)
return pool;
}
pool.free_ring_ = SpscRingBuffer::Attach(pool.base_ + pool.header_->free_ring_offset);
pool.ready_ring_ = SpscRingBuffer::Attach(pool.base_ + pool.header_->ready_ring_offset);
pool.meta_ = reinterpret_cast<FrameSlotMeta *>(pool.base_ + pool.header_->meta_offset);
pool.free_ring_ =
SpscRingBuffer::Attach(pool.base_ + pool.header_->free_ring_offset);
pool.ready_ring_ =
SpscRingBuffer::Attach(pool.base_ + pool.header_->ready_ring_offset);
pool.meta_ = reinterpret_cast<FrameSlotMeta *>(pool.base_ +
pool.header_->meta_offset);
pool.data_ = pool.base_ + pool.header_->data_offset;
return pool;
+2 -2
View File
@@ -90,7 +90,8 @@ public:
* Initializes both rings, seeds the free ring with every slot index, and zeroes metadata.
* `mem` must provide at least BytesNeeded(slot_count, slot_data_bytes) bytes.
*/
static FrameSlotPool Create(void *mem, uint32_t slot_count, size_t slot_data_bytes);
static FrameSlotPool Create(void *mem, uint32_t slot_count,
size_t slot_data_bytes);
/**
* @brief Map an existing, already-initialized pool (peer side).
@@ -148,7 +149,6 @@ public:
FrameSlotPool() = default;
private:
struct Header {
uint32_t magic;
uint32_t slot_count;
+10 -5
View File
@@ -93,7 +93,8 @@ bool ReadMessage(QByteArray *buffer, QJsonObject *out, bool *ok = nullptr);
struct HandshakeMsg {
int protocol_version = 0;
QString shm_key; ///< Worker->main output shared-memory segment key.
QString input_shm_key; ///< Main->worker input shared-memory segment key (optional).
QString
input_shm_key; ///< Main->worker input shared-memory segment key (optional).
int input_slots = 0; ///< Number of main->worker input frame slots.
int output_slots = 0; ///< Number of worker->main output frame slots.
qint64 slot_data_bytes = 0; ///< Per-output-slot pixel block size.
@@ -104,8 +105,10 @@ struct HandshakeMsg {
};
struct RenderFrameMsg {
qint64 ticket_id = 0; ///< Correlates this request with the eventual frame_ready.
QString node_uuid; ///< Output/viewer node to render, by stable uuid in the loaded graph.
qint64 ticket_id =
0; ///< Correlates this request with the eventual frame_ready.
QString
node_uuid; ///< Output/viewer node to render, by stable uuid in the loaded graph.
qint64 time_num = 0;
qint64 time_den = 1;
int width = 0; ///< Forced output size (0 = use graph default).
@@ -113,8 +116,10 @@ struct RenderFrameMsg {
int format = -1; ///< Forced PixelFormat::Format (-1 = default/INVALID).
int channel_count = 0; ///< 0 = default.
int mode = 0; ///< RenderMode::Mode.
int input_slot = -1; ///< Optional main->worker decoded input slot for footage nodes.
QVector<int> input_slots; ///< Optional ordered decoded input slots for footage nodes.
int input_slot =
-1; ///< Optional main->worker decoded input slot for footage nodes.
QVector<int>
input_slots; ///< Optional ordered decoded input slots for footage nodes.
// Output color transform to apply before returning the frame. When empty,
// the worker returns the image in the project's reference space.
+13 -7
View File
@@ -75,16 +75,20 @@ bool SharedMemoryRegion::Open(const QString &key, size_t size, Mode mode)
const std::wstring wname = mapping_name.toStdWString();
if (mode == kCreate) {
const DWORD size_high = static_cast<DWORD>((quint64(size) >> 32) & 0xFFFFFFFF);
const DWORD size_high =
static_cast<DWORD>((quint64(size) >> 32) & 0xFFFFFFFF);
const DWORD size_low = static_cast<DWORD>(quint64(size) & 0xFFFFFFFF);
handle_ = CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE,
size_high, size_low, wname.c_str());
handle_ = CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr,
PAGE_READWRITE, size_high, size_low,
wname.c_str());
if (!handle_) {
error_ = QStringLiteral("CreateFileMapping failed: %1").arg(GetLastError());
error_ = QStringLiteral("CreateFileMapping failed: %1")
.arg(GetLastError());
return false;
}
if (GetLastError() == ERROR_ALREADY_EXISTS) {
error_ = QStringLiteral("Shared memory key already exists: %1").arg(key);
error_ =
QStringLiteral("Shared memory key already exists: %1").arg(key);
CloseHandle(handle_);
handle_ = nullptr;
return false;
@@ -92,7 +96,8 @@ bool SharedMemoryRegion::Open(const QString &key, size_t size, Mode mode)
} else {
handle_ = OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, wname.c_str());
if (!handle_) {
error_ = QStringLiteral("OpenFileMapping failed: %1").arg(GetLastError());
error_ =
QStringLiteral("OpenFileMapping failed: %1").arg(GetLastError());
return false;
}
}
@@ -165,7 +170,8 @@ bool SharedMemoryRegion::Open(const QString &key, size_t size, Mode mode)
data_ = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0);
if (data_ == MAP_FAILED) {
error_ = QStringLiteral("mmap failed: %1").arg(QString::fromUtf8(strerror(errno)));
error_ = QStringLiteral("mmap failed: %1")
.arg(QString::fromUtf8(strerror(errno)));
data_ = nullptr;
::close(fd_);
fd_ = -1;
+2 -1
View File
@@ -174,7 +174,8 @@ private:
std::atomic<uint32_t> tail_;
uint32_t capacity_;
static_assert(sizeof(std::atomic<uint32_t>) == sizeof(uint32_t),
static_assert(
sizeof(std::atomic<uint32_t>) == sizeof(uint32_t),
"atomic<uint32_t> must be lock-free POD-sized for shared memory use");
};
+4 -2
View File
@@ -19,7 +19,9 @@
#include "pluginjob.h"
namespace olive {
namespace plugin {
namespace olive
{
namespace plugin
{
} // plugin
} // olive
+19 -14
View File
@@ -26,47 +26,52 @@
#include <any>
#include <chrono>
namespace olive {
namespace plugin {
namespace olive
{
namespace plugin
{
class PluginJob :public AcceleratedJob{
class PluginJob : public AcceleratedJob {
public:
explicit PluginJob(const OFX::Host::ImageEffect::Instance* pluginInstance,
const PluginNode* node, NodeValueRow row,
explicit PluginJob(const OFX::Host::ImageEffect::Instance *pluginInstance,
const PluginNode *node, NodeValueRow row,
const olive::core::rational &time)
: AcceleratedJob()
, time_seconds_(time.toDouble())
{
this->pluginInstance_ = pluginInstance;
this->node_=node;
this->node_ = node;
Insert(row);
}
explicit PluginJob(const OFX::Host::ImageEffect::Instance* pluginInstance,
const PluginNode* node, NodeValueRow row)
explicit PluginJob(const OFX::Host::ImageEffect::Instance *pluginInstance,
const PluginNode *node, NodeValueRow row)
: PluginJob(pluginInstance, node, row, olive::core::rational(0))
{
}
PluginNode *node() const {
PluginNode *node() const
{
return const_cast<PluginNode *>(node_);
}
OFX::Host::ImageEffect::Instance* pluginInstance() {
return const_cast<OFX::Host::ImageEffect::Instance*>(pluginInstance_);
OFX::Host::ImageEffect::Instance *pluginInstance()
{
return const_cast<OFX::Host::ImageEffect::Instance *>(pluginInstance_);
}
double time_seconds() const {
double time_seconds() const
{
return time_seconds_;
}
private:
const OFX::Host::ImageEffect::Instance *pluginInstance_=nullptr;
const OFX::Host::ImageEffect::Instance *pluginInstance_ = nullptr;
QHash<OfxTime, QHash<QString, std::any>> paramsOnTime;
QHash<QString, std::any> params;
const PluginNode *node_=nullptr;
const PluginNode *node_ = nullptr;
double time_seconds_ = 0.0;
};
+2 -2
View File
@@ -16,10 +16,10 @@
file(GLOB_RECURSE OCIOCONF_RESOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.ocio *.spi3d *.spi1d)
set(QRC_BODY "")
foreach(OCIOCONF_FILE ${OCIOCONF_RESOURCES})
foreach (OCIOCONF_FILE ${OCIOCONF_RESOURCES})
string(APPEND QRC_BODY "<file>${OCIOCONF_FILE}</file>\n")
configure_file(${OCIOCONF_FILE} ${OCIOCONF_FILE} COPYONLY)
endforeach()
endforeach ()
configure_file(ocioconf.qrc.in ocioconf.qrc @ONLY)
set(OLIVE_RESOURCES
+69 -50
View File
@@ -10,7 +10,8 @@
#include "render/texture.h"
#include "render/videoparams.h"
namespace {
namespace
{
class BackendOpenGLRenderer : public olive::OpenGLRenderer {
public:
@@ -39,29 +40,32 @@ const QVariant &VariantRef(const void *variant)
} // namespace
// Creates the backend object and returns it as an opaque C handle.
OAK_RENDER_BACKEND_EXPORT OakRenderBackendHandle oak_renderer_create(void *parent)
OAK_RENDER_BACKEND_EXPORT OakRenderBackendHandle
oak_renderer_create(void *parent)
{
return new BackendOpenGLRenderer(static_cast<QObject *>(parent));
}
// Destroys the opaque backend object created by oak_renderer_create().
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy(OakRenderBackendHandle handle)
OAK_RENDER_BACKEND_EXPORT void
oak_renderer_destroy(OakRenderBackendHandle handle)
{
delete Renderer(handle);
}
// Reports static OpenGL backend capabilities to the adapter.
OAK_RENDER_BACKEND_EXPORT bool oak_renderer_get_info(
OakRenderBackendHandle handle, OakRenderBackendInfo *out_info)
OAK_RENDER_BACKEND_EXPORT bool
oak_renderer_get_info(OakRenderBackendHandle handle,
OakRenderBackendInfo *out_info)
{
if (!handle || !out_info) {
return false;
}
out_info->abi_version = 1;
out_info->kind = OAK_RENDER_BACKEND_OPENGL;
out_info->capabilities = OAK_RENDER_BACKEND_CAP_TEXTURES |
OAK_RENDER_BACKEND_CAP_SHADERS | OAK_RENDER_BACKEND_CAP_BLIT |
OAK_RENDER_BACKEND_CAP_READBACK |
out_info->capabilities =
OAK_RENDER_BACKEND_CAP_TEXTURES | OAK_RENDER_BACKEND_CAP_SHADERS |
OAK_RENDER_BACKEND_CAP_BLIT | OAK_RENDER_BACKEND_CAP_READBACK |
OAK_RENDER_BACKEND_CAP_VIEWER_CONTEXT;
out_info->name = "opengl";
out_info->status = "available";
@@ -70,8 +74,8 @@ OAK_RENDER_BACKEND_EXPORT bool oak_renderer_get_info(
// OpenGL availability is context-dependent, so object creation is the minimum
// availability signal for this backend.
OAK_RENDER_BACKEND_EXPORT bool oak_renderer_is_available(
OakRenderBackendHandle handle)
OAK_RENDER_BACKEND_EXPORT bool
oak_renderer_is_available(OakRenderBackendHandle handle)
{
return handle != nullptr;
}
@@ -83,35 +87,37 @@ OAK_RENDER_BACKEND_EXPORT bool oak_renderer_init(OakRenderBackendHandle handle)
}
// Initializes the backend against a caller-owned viewer OpenGL context.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_init_with_context(
OakRenderBackendHandle handle, void *context)
OAK_RENDER_BACKEND_EXPORT void
oak_renderer_init_with_context(OakRenderBackendHandle handle, void *context)
{
Renderer(handle)->Init(static_cast<QOpenGLContext *>(context));
}
// Runs renderer post-initialization once the GL context is available.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_post_init(OakRenderBackendHandle handle)
OAK_RENDER_BACKEND_EXPORT void
oak_renderer_post_init(OakRenderBackendHandle handle)
{
Renderer(handle)->PostInit();
}
// Releases post-init OpenGL surface/context state.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_post_destroy(OakRenderBackendHandle handle)
OAK_RENDER_BACKEND_EXPORT void
oak_renderer_post_destroy(OakRenderBackendHandle handle)
{
Renderer(handle)->PostDestroy();
}
// Releases renderer-owned GL resources before object destruction.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy_internal(
OakRenderBackendHandle handle)
OAK_RENDER_BACKEND_EXPORT void
oak_renderer_destroy_internal(OakRenderBackendHandle handle)
{
Renderer(handle)->DestroyInternal();
}
// Clears either the widget framebuffer or a texture destination.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_clear_destination(
OakRenderBackendHandle handle, void *texture, double r, double g, double b,
double a)
OAK_RENDER_BACKEND_EXPORT void
oak_renderer_clear_destination(OakRenderBackendHandle handle, void *texture,
double r, double g, double b, double a)
{
Renderer(handle)->ClearDestination(static_cast<olive::Texture *>(texture),
r, g, b, a);
@@ -122,51 +128,58 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_create_native_texture(
OakRenderBackendHandle handle, int width, int height, int depth, int format,
int channel_count, const void *data, int linesize, void *out_variant)
{
*static_cast<QVariant *>(out_variant) = Renderer(handle)->CreateNativeTexture(
width, height, depth, static_cast<olive::PixelFormat::Format>(format),
channel_count, data, linesize);
*static_cast<QVariant *>(out_variant) =
Renderer(handle)->CreateNativeTexture(
width, height, depth,
static_cast<olive::PixelFormat::Format>(format), channel_count,
data, linesize);
}
// Destroys an OpenGL texture represented by a QVariant handle.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy_native_texture(
OakRenderBackendHandle handle, const void *variant)
OAK_RENDER_BACKEND_EXPORT void
oak_renderer_destroy_native_texture(OakRenderBackendHandle handle,
const void *variant)
{
Renderer(handle)->DestroyNativeTexture(VariantRef(variant));
}
// Compiles an OpenGL shader program and returns its QVariant handle.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_create_native_shader(
OakRenderBackendHandle handle, const void *shader_code, void *out_variant)
OAK_RENDER_BACKEND_EXPORT void
oak_renderer_create_native_shader(OakRenderBackendHandle handle,
const void *shader_code, void *out_variant)
{
*static_cast<QVariant *>(out_variant) = Renderer(handle)->CreateNativeShader(
*static_cast<QVariant *>(out_variant) =
Renderer(handle)->CreateNativeShader(
*static_cast<const olive::ShaderCode *>(shader_code));
}
// Destroys an OpenGL shader program represented by a QVariant handle.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy_native_shader(
OakRenderBackendHandle handle, const void *variant)
OAK_RENDER_BACKEND_EXPORT void
oak_renderer_destroy_native_shader(OakRenderBackendHandle handle,
const void *variant)
{
Renderer(handle)->DestroyNativeShader(VariantRef(variant));
}
// Uploads CPU pixel data into an OpenGL texture.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_upload_to_texture(
OakRenderBackendHandle handle, const void *variant, const void *video_params,
OAK_RENDER_BACKEND_EXPORT void
oak_renderer_upload_to_texture(OakRenderBackendHandle handle,
const void *variant, const void *video_params,
const void *data, int linesize)
{
Renderer(handle)->UploadToTexture(
VariantRef(variant), *static_cast<const olive::VideoParams *>(video_params),
data, linesize);
VariantRef(variant),
*static_cast<const olive::VideoParams *>(video_params), data, linesize);
}
// Reads an OpenGL texture back to CPU memory.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_download_from_texture(
OakRenderBackendHandle handle, const void *variant, const void *video_params,
void *data, int linesize)
OakRenderBackendHandle handle, const void *variant,
const void *video_params, void *data, int linesize)
{
Renderer(handle)->DownloadFromTexture(
VariantRef(variant), *static_cast<const olive::VideoParams *>(video_params),
data, linesize);
VariantRef(variant),
*static_cast<const olive::VideoParams *>(video_params), data, linesize);
}
// Flushes/waits for pending OpenGL work as required by the renderer.
@@ -176,18 +189,23 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_flush(OakRenderBackendHandle handle)
}
// Reads one pixel from an OpenGL texture.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_get_pixel_from_texture(
OakRenderBackendHandle handle, void *texture, const void *point,
OAK_RENDER_BACKEND_EXPORT void
oak_renderer_get_pixel_from_texture(OakRenderBackendHandle handle,
void *texture, const void *point,
void *out_color)
{
*static_cast<olive::Color *>(out_color) = Renderer(handle)->GetPixelFromTexture(
static_cast<olive::Texture *>(texture), *static_cast<const QPointF *>(point));
*static_cast<olive::Color *>(out_color) =
Renderer(handle)->GetPixelFromTexture(
static_cast<olive::Texture *>(texture),
*static_cast<const QPointF *>(point));
}
// Executes a shader blit through the wrapped C++ OpenGL renderer.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_blit(
OakRenderBackendHandle handle, const void *shader, void *job,
void *destination, const void *destination_params, bool clear_destination)
OAK_RENDER_BACKEND_EXPORT void oak_renderer_blit(OakRenderBackendHandle handle,
const void *shader, void *job,
void *destination,
const void *destination_params,
bool clear_destination)
{
Renderer(handle)->Blit(
VariantRef(shader), *static_cast<olive::AcceleratedJob *>(job),
@@ -197,22 +215,23 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_blit(
}
// Exposes the wrapped OpenGL context for GL-specific integrations.
OAK_RENDER_BACKEND_EXPORT void *oak_renderer_opengl_context(
OakRenderBackendHandle handle)
OAK_RENDER_BACKEND_EXPORT void *
oak_renderer_opengl_context(OakRenderBackendHandle handle)
{
return Renderer(handle)->context();
}
// Binds an output texture for OFX OpenGL rendering.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_attach_output_texture(
OakRenderBackendHandle handle, const void *texture_id)
OAK_RENDER_BACKEND_EXPORT void
oak_renderer_attach_output_texture(OakRenderBackendHandle handle,
const void *texture_id)
{
Renderer(handle)->AttachTextureAsDestination(VariantRef(texture_id));
}
// Detaches any OFX OpenGL output texture binding.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_detach_output_texture(
OakRenderBackendHandle handle)
OAK_RENDER_BACKEND_EXPORT void
oak_renderer_detach_output_texture(OakRenderBackendHandle handle)
{
Renderer(handle)->DetachTextureAsDestination();
}
+50 -37
View File
@@ -265,8 +265,8 @@ void OpenGLRenderer::AttachTextureAsDestination(const QVariant &texture)
void OpenGLRenderer::DetachTextureAsDestination()
{
// QOpenGLWidget renders to a non-zero default FBO.
const GLuint default_fbo =
context_ ? context_->defaultFramebufferObject() : 0;
const GLuint default_fbo = context_ ? context_->defaultFramebufferObject() :
0;
functions_->glBindFramebuffer(GL_FRAMEBUFFER, default_fbo);
}
@@ -504,8 +504,8 @@ struct TextureToBind {
Texture::Interpolation interpolation;
};
void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destination,
VideoParams destination_params,
void OpenGLRenderer::Blit(QVariant s, AcceleratedJob &a_job,
Texture *destination, VideoParams destination_params,
bool clear_destination)
{
GL_PREAMBLE;
@@ -519,7 +519,7 @@ void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destinatio
functions_->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
}
ShaderJob &s_job=dynamic_cast<ShaderJob &>(a_job);
ShaderJob &s_job = dynamic_cast<ShaderJob &>(a_job);
ShaderJob job(s_job);
// If this node is iterative, we'll pick up which input here
QMap<QString, GLuint> texture_index_map;
@@ -585,7 +585,8 @@ void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destinatio
case NodeValue::kColor: {
Color color = value.toColor();
functions_->glUniform4f(variable_location, color.red(),
color.green(), color.blue(), color.alpha());
color.green(), color.blue(),
color.alpha());
break;
}
case NodeValue::kBoolean:
@@ -595,7 +596,8 @@ void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destinatio
TexturePtr texture = value.toTexture();
// Set value to bound texture
functions_->glUniform1i(variable_location, textures_to_bind.size());
functions_->glUniform1i(variable_location,
textures_to_bind.size());
texture_index_map.insert(it.key(), textures_to_bind.size());
@@ -605,8 +607,10 @@ void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destinatio
// Set enable flag if shader wants it
GLuint tex_id = texture ? texture->id().value<GLuint>() : 0;
int enable_param_location = functions_->glGetUniformLocation(
shader,
QStringLiteral("%1_enabled").arg(it.key()).toUtf8().constData());
shader, QStringLiteral("%1_enabled")
.arg(it.key())
.toUtf8()
.constData());
if (enable_param_location > -1) {
functions_->glUniform1i(enable_param_location, tex_id > 0);
}
@@ -637,7 +641,8 @@ void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destinatio
functions_->glActiveTexture(GL_TEXTURE0 + i);
GLenum target = (texture && texture->params().is_3d()) ? GL_TEXTURE_3D :
GLenum target = (texture && texture->params().is_3d()) ?
GL_TEXTURE_3D :
GL_TEXTURE_2D;
functions_->glBindTexture(target, tex_id);
@@ -647,12 +652,12 @@ void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destinatio
if (texture->channel_count() == 1 &&
destination_params.channel_count() != 1) {
// Interpret this texture as a grayscale texture
functions_->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R,
GL_RED);
functions_->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G,
GL_RED);
functions_->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B,
GL_RED);
functions_->glTexParameteri(GL_TEXTURE_2D,
GL_TEXTURE_SWIZZLE_R, GL_RED);
functions_->glTexParameteri(GL_TEXTURE_2D,
GL_TEXTURE_SWIZZLE_G, GL_RED);
functions_->glTexParameteri(GL_TEXTURE_2D,
GL_TEXTURE_SWIZZLE_B, GL_RED);
}
}
}
@@ -683,7 +688,8 @@ void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destinatio
if (!job.GetVertexCoordinates().isEmpty()) {
Q_ASSERT(job.GetVertexCoordinates().size() == 18);
vert_vbo_.allocate(job.GetVertexCoordinates().constData(),
job.GetVertexCoordinates().size() * sizeof(float));
job.GetVertexCoordinates().size() *
sizeof(float));
} else {
vert_vbo_.allocate(blit_vertices.constData(),
blit_vertices.size() * sizeof(GLfloat));
@@ -707,12 +713,13 @@ void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destinatio
vert_vbo_.release();
}
GLint tex_location = functions_->glGetAttribLocation(shader, "a_texcoord");
GLint tex_location =
functions_->glGetAttribLocation(shader, "a_texcoord");
if (tex_location != -1) {
frag_vbo_.bind();
functions_->glEnableVertexAttribArray(tex_location);
functions_->glVertexAttribPointer(tex_location, 2, GL_FLOAT, GL_FALSE,
0, nullptr);
functions_->glVertexAttribPointer(tex_location, 2, GL_FLOAT,
GL_FALSE, 0, nullptr);
frag_vbo_.release();
}
@@ -788,9 +795,9 @@ void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destinatio
// Blit this texture through this shader
{
PRINT_GL_ERRORS;
functions_->glDrawArrays(GL_TRIANGLES, 0, blit_vertices.size() / 3);
functions_->glDrawArrays(GL_TRIANGLES, 0,
blit_vertices.size() / 3);
}
}
if (destination) {
@@ -801,7 +808,8 @@ void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destinatio
// Release any textures we bound before
for (int i = textures_to_bind.size() - 1; i >= 0; i--) {
TexturePtr texture = textures_to_bind.at(i).texture;
GLenum target = (texture && texture->params().is_3d()) ? GL_TEXTURE_3D :
GLenum target = (texture && texture->params().is_3d()) ?
GL_TEXTURE_3D :
GL_TEXTURE_2D;
functions_->glActiveTexture(GL_TEXTURE0 + i);
functions_->glBindTexture(target, 0);
@@ -815,9 +823,8 @@ void OpenGLRenderer::Blit(QVariant s, AcceleratedJob& a_job, Texture *destinatio
vert_vbo_.destroy();
vao_.release();
vao_.destroy();
} catch (std::bad_cast e) {
}
catch (std::bad_cast e){}
}
GLint OpenGLRenderer::GetInternalFormat(PixelFormat format, int channel_layout)
@@ -965,11 +972,11 @@ GLuint OpenGLRenderer::CompileShader(GLenum type, const QString &code)
const int major = context_ ? context_->format().majorVersion() : 0;
const int minor = context_ ? context_->format().minorVersion() : 0;
const bool is_gles2 = is_gles && (major < 3);
const QString gles_preamble = is_gles2
? QStringLiteral("#version 100\n\n"
const QString gles_preamble =
is_gles2 ? QStringLiteral("#version 100\n\n"
"precision highp float;\n\n"
"#define frag_color gl_FragColor\n")
: QStringLiteral("#version 300 es\n\n"
"#define frag_color gl_FragColor\n") :
QStringLiteral("#version 300 es\n\n"
"precision highp float;\n\n");
const QString desktop_preamble =
// Use appropriate GL 3.2 shader header
@@ -991,7 +998,8 @@ GLuint OpenGLRenderer::CompileShader(GLenum type, const QString &code)
QString complete_code;
if (base_code.startsWith(QStringLiteral("#version"))) {
if (is_gles || !desktop_preamble.startsWith(QStringLiteral("#version"))) {
if (is_gles ||
!desktop_preamble.startsWith(QStringLiteral("#version"))) {
int newline = base_code.indexOf('\n');
if (newline >= 0) {
complete_code = shader_preamble + base_code.mid(newline + 1);
@@ -1007,17 +1015,21 @@ GLuint OpenGLRenderer::CompileShader(GLenum type, const QString &code)
if (is_gles2) {
if (type == GL_VERTEX_SHADER) {
complete_code.replace(QRegularExpression(QStringLiteral("\\bin\\b")),
complete_code.replace(
QRegularExpression(QStringLiteral("\\bin\\b")),
QStringLiteral("attribute"));
complete_code.replace(QRegularExpression(QStringLiteral("\\bout\\b")),
complete_code.replace(
QRegularExpression(QStringLiteral("\\bout\\b")),
QStringLiteral("varying"));
} else if (type == GL_FRAGMENT_SHADER) {
complete_code.replace(QRegularExpression(QStringLiteral("\\bin\\b")),
complete_code.replace(
QRegularExpression(QStringLiteral("\\bin\\b")),
QStringLiteral("varying"));
complete_code.replace(QRegularExpression(
QStringLiteral("\\bout\\s+vec4\\s+frag_color\\s*;")),
complete_code.replace(QRegularExpression(QStringLiteral(
"\\bout\\s+vec4\\s+frag_color\\s*;")),
QStringLiteral("// frag_color output"));
complete_code.replace(QRegularExpression(QStringLiteral("\\btexture\\b")),
complete_code.replace(
QRegularExpression(QStringLiteral("\\btexture\\b")),
QStringLiteral("texture2D"));
}
}
@@ -1058,7 +1070,8 @@ bool OpenGLRenderer::EnsureContextCurrent(const char *caller)
// paint code can receive textures produced by a render-thread OpenGL
// renderer, so guard here before makeCurrent() can crash inside Qt/GL.
if (context_->thread() != QThread::currentThread()) {
qWarning() << caller << "called from the wrong thread for this OpenGL context";
qWarning()
<< caller << "called from the wrong thread for this OpenGL context";
return false;
}
+1 -1
View File
@@ -94,7 +94,7 @@ public:
bool EnsureContextCurrent(const char *caller);
protected:
virtual void Blit(QVariant shader, olive::AcceleratedJob& job,
virtual void Blit(QVariant shader, olive::AcceleratedJob &job,
olive::Texture *destination,
olive::VideoParams destination_params,
bool clear_destination) override;
File diff suppressed because it is too large Load Diff
+14 -8
View File
@@ -31,8 +31,10 @@
namespace olive
{
namespace plugin{
namespace detail {
namespace plugin
{
namespace detail
{
// 作用:将字节行跨度转换为像素跨度,便于纹理读写。
// Purpose: Convert byte stride to pixel stride for texture I/O.
int BytesToPixels(int byte_linesize, const olive::VideoParams &params);
@@ -46,9 +48,15 @@ int BytesToPixels(int byte_linesize, const olive::VideoParams &params);
class PluginRenderer : public QObject {
Q_OBJECT
public:
explicit PluginRenderer(olive::Renderer *renderer, QObject *parent = nullptr)
: QObject(parent), renderer_(renderer) {}
virtual ~PluginRenderer() override {}
explicit PluginRenderer(olive::Renderer *renderer,
QObject *parent = nullptr)
: QObject(parent)
, renderer_(renderer)
{
}
virtual ~PluginRenderer() override
{
}
olive::Renderer *renderer() const
{
@@ -63,7 +71,7 @@ public:
void DetachOutputTexture();
// 作用:执行插件渲染流程(参数配置、输入/输出、调用渲染动作)。
// Purpose: Execute plugin render flow (params, inputs/outputs, render actions).
void RenderPlugin(TexturePtr src, olive::plugin::PluginJob& job,
void RenderPlugin(TexturePtr src, olive::plugin::PluginJob &job,
olive::TexturePtr destination,
olive::VideoParams destination_params,
bool clear_destination, bool interactive);
@@ -74,6 +82,4 @@ private:
}
}
#endif //PLUGINRENDERER_H
+6 -3
View File
@@ -561,7 +561,8 @@ void PreviewAutoCacher::TryRender()
video_immediate_passthroughs_[watcher].append(t);
}
} else {
qWarning() << "Failed to find copied node for SFR ticket, requeueing";
qWarning()
<< "Failed to find copied node for SFR ticket, requeueing";
single_frame_render_ = t;
if (!delayed_requeue_timer_.isActive()) {
delayed_requeue_timer_.start();
@@ -594,7 +595,8 @@ void PreviewAutoCacher::TryRender()
}
}
} else {
qWarning() << "Failed to find node copy for video job, retrying";
qWarning()
<< "Failed to find node copy for video job, retrying";
if (!delayed_requeue_timer_.isActive()) {
delayed_requeue_timer_.start();
}
@@ -636,7 +638,8 @@ void PreviewAutoCacher::TryRender()
RenderAudio(copy, d.context, use_range, d.cache);
} else {
qWarning() << "Failed to find node copy for audio job, retrying";
qWarning()
<< "Failed to find node copy for audio job, retrying";
pop = false;
if (!delayed_requeue_timer_.isActive()) {
delayed_requeue_timer_.start();
+7 -4
View File
@@ -265,7 +265,9 @@ void ProjectCopier::InsertIntoCopyMap(Node *node, Node *copy)
if (Footage *src_footage = dynamic_cast<Footage *>(node)) {
if (dynamic_cast<Footage *>(copy)) {
connect(src_footage, &Footage::ProxySettingsChanged, this,
[this, src_footage]() { SyncFootageProxySettings(src_footage); });
[this, src_footage]() {
SyncFootageProxySettings(src_footage);
});
SyncFootageProxySettings(src_footage);
}
}
@@ -283,10 +285,11 @@ void ProjectCopier::SyncFootageProxySettings(Footage *source)
return;
}
qDebug() << "ProjectCopier::SyncFootageProxySettings:" << source->filename()
qDebug()
<< "ProjectCopier::SyncFootageProxySettings:" << source->filename()
<< "enabled=" << source->proxy_enabled() << "->"
<< copy->proxy_enabled() << "state="
<< ProxyManager::ProxyStateToString(source->proxy_state());
<< copy->proxy_enabled()
<< "state=" << ProxyManager::ProxyStateToString(source->proxy_state());
copy->SetProxy(source->proxy_path(), source->proxy_state(),
source->proxy_video_stream_index(),
+10 -8
View File
@@ -34,7 +34,8 @@
#include "texture.h"
// Forward declarations to keep the render core header lightweight
namespace olive {
namespace olive
{
class ColorTransformJob;
class Node;
}
@@ -57,17 +58,16 @@ public:
void DestroyTexture(Texture *texture);
virtual void BlitToTexture(QVariant shader, olive::AcceleratedJob& job,
virtual void BlitToTexture(QVariant shader, olive::AcceleratedJob &job,
olive::Texture *destination,
bool clear_destination = true)
{
Blit(shader, job, destination, destination->params(),
clear_destination);
}
void Blit(QVariant shader, olive::AcceleratedJob& job, olive::VideoParams params,
bool clear_destination = true)
void Blit(QVariant shader, olive::AcceleratedJob &job,
olive::VideoParams params, bool clear_destination = true)
{
Blit(shader, job, nullptr, params, clear_destination);
}
@@ -147,10 +147,12 @@ public:
*
* Default implementation is a no-op.
*/
virtual void DetachOutputTexture() {}
virtual void DetachOutputTexture()
{
}
protected:
virtual void Blit(QVariant shader, olive::AcceleratedJob& job,
virtual void Blit(QVariant shader, olive::AcceleratedJob &job,
olive::Texture *destination,
olive::VideoParams destination_params,
bool clear_destination) = 0;
@@ -164,7 +166,7 @@ protected:
virtual void DestroyInternal() = 0;
private:
std::atomic<bool> destroyed_{false};
std::atomic<bool> destroyed_{ false };
std::shared_ptr<RendererLifetime> lifetime_;
struct ColorContext {
struct LUT {
+13 -8
View File
@@ -78,24 +78,26 @@ QString RenderManager::BackendToString(Backend backend)
}
RenderManager::RenderManager(QObject *parent)
: backend_(BackendFromString(
OLIVE_CONFIG("GraphicsBackend").toString()))
: backend_(BackendFromString(OLIVE_CONFIG("GraphicsBackend").toString()))
, requested_backend_(backend_)
, aggressive_gc_(0)
, worker_pool_(nullptr)
{
if (backend_ == kVulkan) {
#ifndef OAK_ENABLE_DYNAMIC_RENDER_BACKEND
qWarning() << "Vulkan backend requested but dynamic render backend is not enabled. Falling back to OpenGL.";
qWarning()
<< "Vulkan backend requested but dynamic render backend is not enabled. Falling back to OpenGL.";
backend_ = kOpenGL;
#endif
}
if (backend_ == kOpenGL || backend_ == kVulkan) {
#ifdef OAK_ENABLE_DYNAMIC_RENDER_BACKEND
auto *dynamic_renderer = new DynamicRenderer(BackendToString(requested_backend_));
auto *dynamic_renderer =
new DynamicRenderer(BackendToString(requested_backend_));
if (!dynamic_renderer->Load()) {
qWarning() << "Failed to load dynamic render backend" << BackendToString(requested_backend_)
qWarning() << "Failed to load dynamic render backend"
<< BackendToString(requested_backend_)
<< ", falling back to OpenGL";
delete dynamic_renderer;
backend_ = kOpenGL;
@@ -104,7 +106,8 @@ RenderManager::RenderManager(QObject *parent)
context_ = dynamic_renderer;
// DynamicRenderer may internally fall back (e.g. Vulkan -> OpenGL).
// Synchronize RenderManager's view of the actual runtime backend.
Backend actual_backend = BackendFromString(dynamic_renderer->backend_name());
Backend actual_backend =
BackendFromString(dynamic_renderer->backend_name());
if (actual_backend != backend_) {
qWarning() << "Dynamic render backend fell back from"
<< BackendToString(backend_) << "to"
@@ -218,10 +221,12 @@ RenderTicketPtr RenderManager::RenderFrame(const RenderVideoParams &params)
if (worker_params.return_type == ReturnType::kNull) {
dry_run_thread_->AddTicket(ticket);
} else if (worker_pool_ && worker_pool_->SubmitFrame(ticket, worker_params)) {
} else if (worker_pool_ &&
worker_pool_->SubmitFrame(ticket, worker_params)) {
return ticket;
} else {
qWarning() << "RenderManager: worker pool unavailable, finishing ticket "
qWarning()
<< "RenderManager: worker pool unavailable, finishing ticket "
"without result";
ticket->Finish();
}
+1 -1
View File
@@ -82,7 +82,7 @@ public:
/// Vulkan requested by the user. Falls back to OpenGL until VulkanRenderer is implemented.
kVulkan,
/// Video frames are rendered by an external olive-render-worker process.
/// Video frames are rendered by an external oak-render-worker process.
kMultiProcess,
/// No graphics rendering - used to test core threading logic
+43 -41
View File
@@ -28,7 +28,6 @@
#include <QVector3D>
#include <QVector4D>
#include "audio/audioprocessor.h"
#include "node/block/clip/clip.h"
#include "node/block/transition/transition.h"
@@ -159,7 +158,6 @@ FramePtr RenderProcessor::GenerateFrame(TexturePtr texture,
QString::fromUtf8(output_color_transform->id()));
frame->set_video_params(display_params);
}
}
return frame;
@@ -173,7 +171,7 @@ void RenderProcessor::Run()
SetCancelPointer(ticket_->GetCancelAtom());
VideoParams params=ticket_->property("vparam").value<VideoParams>();
VideoParams params = ticket_->property("vparam").value<VideoParams>();
params.set_format(PixelFormat::F32);
SetCacheVideoParams(params);
SetCacheAudioParams(ticket_->property("aparam").value<AudioParams>());
@@ -416,7 +414,8 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination,
}
if (using_colorspace.isEmpty()) {
qWarning() << "RenderProcessor ProcessVideoFootage: no input colorspace available";
qWarning()
<< "RenderProcessor ProcessVideoFootage: no input colorspace available";
}
auto blit_color_managed = [&](const TexturePtr &unmanaged_texture,
@@ -427,8 +426,8 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination,
// We convert to our rendering pixel format, since that will always be float-based which
// is necessary for correct color conversion
ColorProcessorPtr processor = ColorProcessor::Create(
color_manager, using_colorspace,
ColorProcessorPtr processor =
ColorProcessor::Create(color_manager, using_colorspace,
color_manager->GetReferenceColorSpace());
ColorTransformJob job;
@@ -436,7 +435,8 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination,
job.SetInputTexture(unmanaged_texture);
if (texture_params.channel_count() != VideoParams::kRGBAChannelCount ||
texture_params.colorspace() == color_manager->GetReferenceColorSpace()) {
texture_params.colorspace() ==
color_manager->GetReferenceColorSpace()) {
job.SetInputAlphaAssociation(kAlphaNone);
} else if (texture_params.premultiplied_alpha()) {
job.SetInputAlphaAssociation(kAlphaAssociated);
@@ -450,12 +450,14 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination,
render_ctx_->Flush();
};
auto *input_pool =
QtUtils::ValueToPtr<ipc::FrameSlotPool>(ticket_->property("ipc_input_pool"));
auto *input_pool = QtUtils::ValueToPtr<ipc::FrameSlotPool>(
ticket_->property("ipc_input_pool"));
int input_slot = -1;
const QVariantList input_slots = ticket_->property("ipc_input_slots").toList();
const QVariantList input_slots =
ticket_->property("ipc_input_slots").toList();
if (!input_slots.isEmpty()) {
const QVariant cursor_value = ticket_->property("ipc_input_slot_cursor");
const QVariant cursor_value =
ticket_->property("ipc_input_slot_cursor");
const int cursor = cursor_value.isValid() ? cursor_value.toInt() : 0;
if (cursor >= 0 && cursor < input_slots.size()) {
input_slot = input_slots.at(cursor).toInt();
@@ -467,13 +469,15 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination,
}
if (render_ctx_ && input_pool && input_slot >= 0) {
if (input_slot >= int(input_pool->slot_count())) {
qWarning() << "RenderProcessor received out-of-range IPC input frame slot"
qWarning()
<< "RenderProcessor received out-of-range IPC input frame slot"
<< input_slot;
return;
}
const ipc::FrameSlotMeta *meta = input_pool->Meta(uint32_t(input_slot));
if (meta && meta->width > 0 && meta->height > 0 && meta->data_size > 0 &&
if (meta && meta->width > 0 && meta->height > 0 &&
meta->data_size > 0 &&
meta->data_size <= int(input_pool->slot_data_bytes())) {
VideoParams input_params = stream_data;
input_params.set_width(meta->width);
@@ -486,7 +490,6 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination,
input_params.set_depth(1);
}
// Prefer the colorspace that the main process used when decoding this
// frame. The FootageJob reconstructed in the worker may have stale or
// empty colorspace if the project snapshot was saved before stream
@@ -498,9 +501,9 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination,
}
const int bytes_per_pixel = input_params.GetBytesPerPixel();
const int linesize_pixels = bytes_per_pixel > 0
? meta->linesize / bytes_per_pixel
: input_params.effective_width();
const int linesize_pixels = bytes_per_pixel > 0 ?
meta->linesize / bytes_per_pixel :
input_params.effective_width();
const void *slot_data = input_pool->SlotData(uint32_t(input_slot));
TexturePtr unmanaged_texture = render_ctx_->CreateTexture(
@@ -509,12 +512,14 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination,
blit_color_managed(unmanaged_texture, input_params);
return;
}
qWarning() << "RenderProcessor received invalid IPC input frame slot" << input_slot;
qWarning() << "RenderProcessor received invalid IPC input frame slot"
<< input_slot;
return;
}
if (!decoder_cache_) {
qWarning() << "RenderProcessor has no decoder cache or IPC input frame for"
qWarning()
<< "RenderProcessor has no decoder cache or IPC input frame for"
<< stream->filename();
return;
}
@@ -523,15 +528,15 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination,
static_cast<RenderMode::Mode>(ticket_->property("mode").toInt()) ==
RenderMode::kOffline &&
stream->has_proxy() && QFileInfo::exists(stream->proxy_filename());
const QString decode_filename =
use_proxy ? stream->proxy_filename() : stream->filename();
const QString decoder_id =
use_proxy ? stream->proxy_decoder() : stream->decoder();
const int stream_index =
use_proxy ? stream->proxy_stream_index() : stream_data.stream_index();
const QString decode_filename = use_proxy ? stream->proxy_filename() :
stream->filename();
const QString decoder_id = use_proxy ? stream->proxy_decoder() :
stream->decoder();
const int stream_index = use_proxy ? stream->proxy_stream_index() :
stream_data.stream_index();
Decoder::CodecStream default_codec_stream(
decode_filename, stream_index, GetCurrentBlock());
Decoder::CodecStream default_codec_stream(decode_filename, stream_index,
GetCurrentBlock());
DecoderPtr decoder = nullptr;
@@ -553,8 +558,8 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination,
decode_filename, frame_number);
// Decoder will close automatically since it's a stream_ptr
decoder->Open(Decoder::CodecStream(
frame_filename, stream_index, GetCurrentBlock()));
decoder->Open(Decoder::CodecStream(frame_filename, stream_index,
GetCurrentBlock()));
}
break;
}
@@ -643,7 +648,8 @@ void RenderProcessor::ProcessShader(TexturePtr destination, const Node *node,
locker.unlock();
// Run shader
render_ctx_->BlitToTexture(shader, const_cast<ShaderJob&>(*job), destination.get());
render_ctx_->BlitToTexture(shader, const_cast<ShaderJob &>(*job),
destination.get());
}
void RenderProcessor::ProcessSamples(SampleBuffer &destination,
@@ -720,8 +726,7 @@ TexturePtr RenderProcessor::ProcessPluginJob(TexturePtr texture,
return destination;
}
auto *plugin_job =
dynamic_cast<plugin::PluginJob *>(texture->job());
auto *plugin_job = dynamic_cast<plugin::PluginJob *>(texture->job());
if (!plugin_job) {
return destination;
}
@@ -779,13 +784,8 @@ TexturePtr RenderProcessor::ProcessPluginJob(TexturePtr texture,
}
}
plugin_renderer.RenderPlugin(
src,
*plugin_job,
destination,
destination->params(),
true,
false);
plugin_renderer.RenderPlugin(src, *plugin_job, destination,
destination->params(), true, false);
return destination;
}
@@ -797,7 +797,8 @@ TexturePtr RenderProcessor::ProcessVideoCacheJob(const CacheJob *val)
// Auto-detect and discard black/empty cached frames (macOS TBDR artifact)
bool all_black = true;
if (frame->data() && frame->allocated_size() > 0) {
const uint8_t *pixels = reinterpret_cast<const uint8_t *>(frame->data());
const uint8_t *pixels =
reinterpret_cast<const uint8_t *>(frame->data());
size_t alloc_size = static_cast<size_t>(frame->allocated_size());
size_t check_bytes = std::min(alloc_size, size_t(4096));
for (size_t i = 0; i < check_bytes; ++i) {
@@ -808,7 +809,8 @@ TexturePtr RenderProcessor::ProcessVideoCacheJob(const CacheJob *val)
}
}
if (all_black) {
qWarning() << "[CACHE] Discarding black cached frame:" << val->GetFilename()
qWarning() << "[CACHE] Discarding black cached frame:"
<< val->GetFilename()
<< "time=" << frame->timestamp().toDouble()
<< "size=" << frame->allocated_size();
QFile::remove(val->GetFilename());
+2 -1
View File
@@ -32,7 +32,8 @@
namespace olive
{
namespace plugin {
namespace plugin
{
class PluginRenderer;
}
+213 -135
View File
@@ -62,8 +62,8 @@ struct FootageInput {
class FootageInputCollector : public NodeTraverser {
public:
QVector<FootageInput> Collect(const RenderManager::RenderVideoParams &params,
CancelAtom *cancel)
QVector<FootageInput>
Collect(const RenderManager::RenderVideoParams &params, CancelAtom *cancel)
{
SetCancelPointer(cancel);
VideoParams cache_params = params.video_params;
@@ -75,16 +75,14 @@ public:
if (cache_params.interlacing() != VideoParams::kInterlaceNone) {
frame_length /= 2;
}
NodeValueTable table = GenerateTable(params.node,
TimeRange(params.time,
params.time + frame_length));
NodeValueTable table = GenerateTable(
params.node, TimeRange(params.time, params.time + frame_length));
NodeValue texture = table.Get(NodeValue::kTexture);
ResolveJobs(texture);
if (cache_params.interlacing() != VideoParams::kInterlaceNone) {
NodeValueTable second_table =
GenerateTable(params.node,
TimeRange(params.time + frame_length,
NodeValueTable second_table = GenerateTable(
params.node, TimeRange(params.time + frame_length,
params.time + frame_length * 2));
NodeValue second_texture = second_table.Get(NodeValue::kTexture);
ResolveJobs(second_texture);
@@ -94,13 +92,12 @@ public:
}
protected:
void ProcessVideoFootage(TexturePtr destination,
const FootageJob *stream,
void ProcessVideoFootage(TexturePtr destination, const FootageJob *stream,
const rational &input_time) override
{
Q_UNUSED(destination)
if (stream) {
inputs_.append({*stream, input_time});
inputs_.append({ *stream, input_time });
}
}
@@ -140,8 +137,7 @@ DecoderPtr ResolveDecoderFromCache(DecoderCache *decoder_cache,
}
FramePtr DecodeInputFrame(DecoderCache *decoder_cache,
const FootageInput &input,
CancelAtom *cancel)
const FootageInput &input, CancelAtom *cancel)
{
VideoParams stream_data = input.job.video_params();
QString filename = input.job.filename();
@@ -162,19 +158,17 @@ FramePtr DecodeInputFrame(DecoderCache *decoder_cache,
case VideoParams::kVideoTypeVideo:
case VideoParams::kVideoTypeStill:
decoder = ResolveDecoderFromCache(
decoder_cache,
decoder_id,
decoder_cache, decoder_id,
Decoder::CodecStream(filename, stream_index, nullptr));
break;
case VideoParams::kVideoTypeImageSequence: {
const int64_t frame_number =
stream_data.get_time_in_timebase_units(input.time);
filename = Decoder::TransformImageSequenceFileName(filename, frame_number);
filename =
Decoder::TransformImageSequenceFileName(filename, frame_number);
decoder = Decoder::CreateFromID(decoder_id);
if (decoder &&
!decoder->Open(Decoder::CodecStream(filename,
stream_index,
nullptr))) {
if (decoder && !decoder->Open(Decoder::CodecStream(
filename, stream_index, nullptr))) {
decoder = nullptr;
}
break;
@@ -188,9 +182,9 @@ FramePtr DecodeInputFrame(DecoderCache *decoder_cache,
Decoder::RetrieveVideoParams retrieve;
retrieve.divider = stream_data.divider();
retrieve.maximum_format = PixelFormat::U16;
retrieve.time = stream_data.video_type() == VideoParams::kVideoTypeVideo
? input.time
: Decoder::kAnyTimecode;
retrieve.time = stream_data.video_type() == VideoParams::kVideoTypeVideo ?
input.time :
Decoder::kAnyTimecode;
retrieve.cancelled = cancel;
retrieve.force_range = stream_data.color_range();
retrieve.src_interlacing = stream_data.interlacing();
@@ -207,15 +201,13 @@ FramePtr DecodeInputFrame(DecoderCache *decoder_cache,
frame_params.set_colorspace(stream_data.colorspace());
frame->set_video_params(frame_params);
}
}
return frame;
}
bool DecodeInputFrames(DecoderCache *decoder_cache,
const RenderManager::RenderVideoParams &params,
CancelAtom *cancel,
QVector<FramePtr> *frames)
CancelAtom *cancel, QVector<FramePtr> *frames)
{
frames->clear();
@@ -241,9 +233,9 @@ bool DecodeInputFrames(DecoderCache *decoder_cache,
QString WorkerProgramPath()
{
#if defined(Q_OS_WIN)
const QString file = QStringLiteral("olive-render-worker.exe");
const QString file = QStringLiteral("oak-render-worker.exe");
#else
const QString file = QStringLiteral("olive-render-worker");
const QString file = QStringLiteral("oak-render-worker");
#endif
const QString app_dir = QCoreApplication::applicationDirPath();
@@ -271,7 +263,8 @@ bool WriteControlMessage(QProcess *process, const QJsonObject &obj)
return false;
}
const QByteArray line = QJsonDocument(obj).toJson(QJsonDocument::Compact) + '\n';
const QByteArray line =
QJsonDocument(obj).toJson(QJsonDocument::Compact) + '\n';
const qint64 written = process->write(line);
if (written != line.size()) {
return false;
@@ -285,7 +278,8 @@ void TryWriteControlMessage(QProcess *process, const QJsonObject &obj)
return;
}
const QByteArray line = QJsonDocument(obj).toJson(QJsonDocument::Compact) + '\n';
const QByteArray line =
QJsonDocument(obj).toJson(QJsonDocument::Compact) + '\n';
process->write(line);
}
@@ -315,12 +309,14 @@ bool IsProcessAlive(qint64 process_id)
}
#if defined(Q_OS_WIN)
HANDLE handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, DWORD(process_id));
HANDLE handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE,
DWORD(process_id));
if (!handle) {
return false;
}
DWORD exit_code = 0;
const bool alive = GetExitCodeProcess(handle, &exit_code) && exit_code == STILL_ACTIVE;
const bool alive = GetExitCodeProcess(handle, &exit_code) &&
exit_code == STILL_ACTIVE;
CloseHandle(handle);
return alive;
#else
@@ -334,11 +330,11 @@ QString WorkerProcessDetails(const QProcess *process)
return QStringLiteral("worker process unavailable");
}
const QString exit_status =
process->exitStatus() == QProcess::CrashExit
? QStringLiteral("crash")
: QStringLiteral("normal");
return QStringLiteral("state=%1 exit_status=%2 exit_code=%3 process_error=%4 error=\"%5\"")
const QString exit_status = process->exitStatus() == QProcess::CrashExit ?
QStringLiteral("crash") :
QStringLiteral("normal");
return QStringLiteral(
"state=%1 exit_status=%2 exit_code=%3 process_error=%4 error=\"%5\"")
.arg(int(process->state()))
.arg(exit_status)
.arg(process->exitCode())
@@ -355,7 +351,8 @@ bool ReadControlMessage(QProcess *process, QJsonObject *out, QString *error,
*error = QStringLiteral("worker exited before response: %1")
.arg(WorkerProcessDetails(process));
} else {
*error = QStringLiteral("timeout waiting for worker response: %1")
*error =
QStringLiteral("timeout waiting for worker response: %1")
.arg(WorkerProcessDetails(process));
}
}
@@ -372,7 +369,8 @@ bool ReadControlMessage(QProcess *process, QJsonObject *out, QString *error,
const QJsonDocument doc = QJsonDocument::fromJson(line, &parse_error);
if (parse_error.error != QJsonParseError::NoError || !doc.isObject()) {
if (error) {
*error = QStringLiteral("worker emitted malformed control JSON");
*error =
QStringLiteral("worker emitted malformed control JSON");
}
return false;
}
@@ -397,8 +395,7 @@ bool ReadControlMessage(QProcess *process, QJsonObject *out, QString *error,
} // namespace
RenderWorkerPool::RenderWorkerPool(DecoderCache *decoder_cache,
const QString &gpu_backend,
QObject *parent)
const QString &gpu_backend, QObject *parent)
: QThread(parent)
, decoder_cache_(decoder_cache)
, gpu_backend_(gpu_backend)
@@ -410,8 +407,8 @@ RenderWorkerPool::~RenderWorkerPool()
Shutdown();
}
bool RenderWorkerPool::SubmitFrame(RenderTicketPtr ticket,
const RenderManager::RenderVideoParams &params)
bool RenderWorkerPool::SubmitFrame(
RenderTicketPtr ticket, const RenderManager::RenderVideoParams &params)
{
Job job(ticket, params);
if (!PrepareJob(ticket, params, &job)) {
@@ -460,7 +457,7 @@ bool RenderWorkerPool::RemoveTicket(RenderTicketPtr ticket)
}
if (!queued_graph_path.isEmpty()) {
CleanupGraphFile(queued_graph_path);
ReleaseGraphPathRef(queued_graph_path);
return true;
}
@@ -469,8 +466,6 @@ bool RenderWorkerPool::RemoveTicket(RenderTicketPtr ticket)
void RenderWorkerPool::Shutdown()
{
QVector<QString> graph_paths_to_clean;
{
QMutexLocker locker(&mutex_);
stopping_ = true;
@@ -478,6 +473,7 @@ void RenderWorkerPool::Shutdown()
if (job.ticket) {
job.ticket->Cancel();
}
ReleaseGraphPathRefLocked(job.graph_path);
}
queue_.clear();
for (ActiveJob &active : active_jobs_) {
@@ -487,16 +483,12 @@ void RenderWorkerPool::Shutdown()
}
}
for (auto it = graph_cache_.begin(); it != graph_cache_.end(); ++it) {
graph_paths_to_clean.append(it->path);
SetGraphPathCachedLocked(it->path, false);
}
graph_cache_.clear();
wait_.wakeAll();
}
for (const QString &path : graph_paths_to_clean) {
CleanupGraphFile(path);
}
if (isRunning()) {
wait();
}
@@ -510,13 +502,13 @@ void RenderWorkerPool::run()
active_jobs_.resize(worker_count);
}
std::vector<std::vector<std::unique_ptr<PooledWorker>>> local_pools(worker_count);
std::vector<std::vector<std::unique_ptr<PooledWorker>>> local_pools(
worker_count);
std::vector<std::thread> workers;
workers.reserve(size_t(worker_count));
for (int i = 0; i < worker_count; i++) {
workers.emplace_back([this, i, &local_pools]() {
WorkerLoop(i, &local_pools[i]);
});
workers.emplace_back(
[this, i, &local_pools]() { WorkerLoop(i, &local_pools[i]); });
}
for (std::thread &worker : workers) {
@@ -534,8 +526,7 @@ void RenderWorkerPool::run()
}
void RenderWorkerPool::WorkerLoop(
int worker_index,
std::vector<std::unique_ptr<PooledWorker>> *local_pool)
int worker_index, std::vector<std::unique_ptr<PooledWorker>> *local_pool)
{
while (true) {
mutex_.lock();
@@ -552,6 +543,7 @@ void RenderWorkerPool::WorkerLoop(
mutex_.unlock();
ProcessJob(job, worker_index, local_pool);
ReleaseGraphPathRef(job.graph_path);
}
}
@@ -565,7 +557,8 @@ bool RenderWorkerPool::PrepareJob(RenderTicketPtr ticket,
Project *project = Project::GetProjectFromObject(params.node);
if (!project) {
qWarning() << "RenderWorkerPool could not resolve project for render node";
qWarning()
<< "RenderWorkerPool could not resolve project for render node";
return false;
}
@@ -585,13 +578,16 @@ bool RenderWorkerPool::PrepareJob(RenderTicketPtr ticket,
auto it = graph_cache_.find(project_uuid);
if (it != graph_cache_.end() && !project->is_modified()) {
graph_path = it->path;
//qDebug() << "RenderWorkerPool::PrepareJob: using cached graph snapshot"
// << graph_path;
AddGraphPathRefLocked(graph_path);
qDebug()
<< "RenderWorkerPool::PrepareJob: using cached graph snapshot"
<< graph_path;
} else {
if (it != graph_cache_.end()) {
qDebug() << "RenderWorkerPool::PrepareJob: graph stale, rewriting"
qDebug()
<< "RenderWorkerPool::PrepareJob: graph stale, rewriting"
<< project->is_modified();
CleanupGraphFile(it->path);
SetGraphPathCachedLocked(it->path, false);
graph_cache_.erase(it);
}
locker.unlock();
@@ -606,7 +602,9 @@ bool RenderWorkerPool::PrepareJob(RenderTicketPtr ticket,
project->set_modified(false);
}
locker.relock();
graph_cache_.insert(project_uuid, {graph_path});
graph_cache_.insert(project_uuid, { graph_path });
SetGraphPathCachedLocked(graph_path, true);
AddGraphPathRefLocked(graph_path);
}
}
@@ -621,17 +619,26 @@ bool RenderWorkerPool::PrepareJob(RenderTicketPtr ticket,
bool RenderWorkerPool::WriteGraphSnapshot(Project *project, QString *path)
{
QTemporaryFile file(QDir::temp().filePath(QStringLiteral("oak-render-graph-XXXXXX.ove")));
// Keep snapshots in the system temp directory. The previous bug was not the
// temp location itself, but stale snapshots being deleted while queued jobs
// still referenced them.
const QString graph_dir = QDir::tempPath();
QTemporaryFile file(QDir(graph_dir).filePath(
QStringLiteral("oak-render-graph-XXXXXX.ove")));
file.setAutoRemove(false);
if (!file.open()) {
qWarning() << "RenderWorkerPool failed to create graph snapshot temp file"
qWarning()
<< "RenderWorkerPool failed to create graph snapshot temp file"
<< file.errorString();
return false;
}
QXmlStreamWriter writer(&file);
ProjectSerializer::SaveData data(ProjectSerializer::kProject, project, file.fileName());
const ProjectSerializer::Result result = ProjectSerializer::Save(&writer, data);
ProjectSerializer::SaveData data(ProjectSerializer::kProject, project,
file.fileName());
const ProjectSerializer::Result result =
ProjectSerializer::Save(&writer, data);
file.close();
if (result.code() != ProjectSerializer::kSuccess || writer.hasError()) {
@@ -641,11 +648,15 @@ bool RenderWorkerPool::WriteGraphSnapshot(Project *project, QString *path)
return false;
}
qDebug() << "RenderWorkerPool wrote graph snapshot" << file.fileName()
<< "size" << QFileInfo(file.fileName()).size();
*path = file.fileName();
return true;
}
bool RenderWorkerPool::IsSupported(const RenderManager::RenderVideoParams &params) const
bool RenderWorkerPool::IsSupported(
const RenderManager::RenderVideoParams &params) const
{
return params.node && params.return_type == RenderManager::kFrame &&
params.video_params.is_valid();
@@ -655,7 +666,8 @@ void RenderWorkerPool::ProcessJob(
const Job &job, int worker_index,
std::vector<std::unique_ptr<PooledWorker>> *local_pool)
{
const qint64 ticket_id = qint64(reinterpret_cast<quintptr>(job.ticket.get()));
const qint64 ticket_id =
qint64(reinterpret_cast<quintptr>(job.ticket.get()));
SetActiveWorker(worker_index, job.ticket, nullptr, ticket_id);
job.ticket->Start();
@@ -665,7 +677,8 @@ void RenderWorkerPool::ProcessJob(
return;
}
std::unique_ptr<PooledWorker> worker = AcquireWorker(local_pool, job.graph_path);
std::unique_ptr<PooledWorker> worker =
AcquireWorker(local_pool, job.graph_path);
if (!worker) {
qWarning() << "RenderWorkerPool failed to acquire worker for ticket"
<< ticket_id;
@@ -678,22 +691,24 @@ void RenderWorkerPool::ProcessJob(
if (attempt > 0) {
worker = AcquireWorker(local_pool, job.graph_path);
if (!worker) {
qWarning() << "RenderWorkerPool failed to acquire worker for retry"
qWarning()
<< "RenderWorkerPool failed to acquire worker for retry"
<< ticket_id;
break;
}
}
const JobResult result = ProcessJobAttempt(job, worker_index, attempt,
worker.get());
const qint64 worker_pid = worker && worker->process
? worker->process->processId()
: 0;
const JobResult result =
ProcessJobAttempt(job, worker_index, attempt, worker.get());
const qint64 worker_pid =
worker && worker->process ? worker->process->processId() : 0;
const bool process_state_running = worker && worker->process &&
worker->process->state() == QProcess::Running;
worker->process->state() ==
QProcess::Running;
const bool os_alive = worker_pid > 0 && IsProcessAlive(worker_pid);
const bool worker_healthy = process_state_running || os_alive;
const bool keep_alive = (result == JobResult::kFinished) && worker_healthy;
const bool keep_alive = (result == JobResult::kFinished) &&
worker_healthy;
ReturnWorker(local_pool, std::move(worker), keep_alive);
worker.reset();
@@ -726,11 +741,12 @@ void RenderWorkerPool::ProcessJob(
ClearActiveWorker(worker_index, 0);
}
RenderWorkerPool::JobResult RenderWorkerPool::ProcessJobAttempt(
const Job &job, int worker_index, int attempt_index,
PooledWorker *worker)
RenderWorkerPool::JobResult
RenderWorkerPool::ProcessJobAttempt(const Job &job, int worker_index,
int attempt_index, PooledWorker *worker)
{
const qint64 ticket_id = qint64(reinterpret_cast<quintptr>(job.ticket.get()));
const qint64 ticket_id =
qint64(reinterpret_cast<quintptr>(job.ticket.get()));
if (job.ticket->IsCancelled()) {
return JobResult::kCancelled;
}
@@ -741,27 +757,25 @@ RenderWorkerPool::JobResult RenderWorkerPool::ProcessJobAttempt(
const qint64 worker_process_id = worker->process->processId();
const int output_width = job.params.force_size.width() > 0
? job.params.force_size.width()
: job.params.video_params.effective_width();
const int output_height = job.params.force_size.height() > 0
? job.params.force_size.height()
: job.params.video_params.effective_height();
const int output_width = job.params.force_size.width() > 0 ?
job.params.force_size.width() :
job.params.video_params.effective_width();
const int output_height = job.params.force_size.height() > 0 ?
job.params.force_size.height() :
job.params.video_params.effective_height();
const PixelFormat::Format output_format =
job.params.force_format != PixelFormat::INVALID
? PixelFormat::Format(job.params.force_format)
: PixelFormat::F32;
const int output_channels = job.params.force_channel_count > 0
? job.params.force_channel_count
: VideoParams::kRGBAChannelCount;
const int output_linesize =
Frame::generate_linesize_bytes(output_width, output_format,
output_channels);
job.params.force_format != PixelFormat::INVALID ?
PixelFormat::Format(job.params.force_format) :
PixelFormat::F32;
const int output_channels = job.params.force_channel_count > 0 ?
job.params.force_channel_count :
VideoParams::kRGBAChannelCount;
const int output_linesize = Frame::generate_linesize_bytes(
output_width, output_format, output_channels);
const size_t estimated_output_slot_bytes =
size_t(output_linesize) * size_t(output_height);
const int f32_rgba_linesize =
Frame::generate_linesize_bytes(output_width, PixelFormat::F32,
VideoParams::kRGBAChannelCount);
const int f32_rgba_linesize = Frame::generate_linesize_bytes(
output_width, PixelFormat::F32, VideoParams::kRGBAChannelCount);
const size_t f32_rgba_slot_bytes =
size_t(f32_rgba_linesize) * size_t(output_height);
const size_t output_slot_bytes =
@@ -790,7 +804,8 @@ RenderWorkerPool::JobResult RenderWorkerPool::ProcessJobAttempt(
if (!worker->output_region.Open(worker->output_shm_key,
output_region_bytes,
ipc::SharedMemoryRegion::kCreate)) {
qWarning() << "RenderWorkerPool failed to create output shared memory"
qWarning()
<< "RenderWorkerPool failed to create output shared memory"
<< worker->output_region.error();
return JobResult::kFatalFailure;
}
@@ -816,17 +831,19 @@ RenderWorkerPool::JobResult RenderWorkerPool::ProcessJobAttempt(
ipc::SharedMemoryRegion::MakeKey(worker_process_id, 1) +
QStringLiteral("-in");
}
const size_t input_region_bytes =
ipc::FrameSlotPool::BytesNeeded(input_slot_count, input_slot_bytes);
const size_t input_region_bytes = ipc::FrameSlotPool::BytesNeeded(
input_slot_count, input_slot_bytes);
if (!worker->input_region.Open(worker->input_shm_key,
input_region_bytes,
ipc::SharedMemoryRegion::kCreate)) {
qWarning() << "RenderWorkerPool failed to create input shared memory"
qWarning()
<< "RenderWorkerPool failed to create input shared memory"
<< worker->input_region.error();
return JobResult::kFatalFailure;
}
worker->input_pool = ipc::FrameSlotPool::Create(
worker->input_region.data(), input_slot_count, input_slot_bytes);
worker->input_pool =
ipc::FrameSlotPool::Create(worker->input_region.data(),
input_slot_count, input_slot_bytes);
worker->input_slot_bytes = input_slot_bytes;
}
}
@@ -836,7 +853,8 @@ RenderWorkerPool::JobResult RenderWorkerPool::ProcessJobAttempt(
if (input_slot_count > 0) {
for (const FramePtr &frame : job.input_frames) {
if (frame->allocated_size() > int(worker->input_slot_bytes)) {
qWarning() << "RenderWorkerPool decoded input frame exceeds slot size";
qWarning()
<< "RenderWorkerPool decoded input frame exceeds slot size";
return JobResult::kFatalFailure;
}
@@ -862,8 +880,8 @@ RenderWorkerPool::JobResult RenderWorkerPool::ProcessJobAttempt(
const QString cs = frame->video_params().colorspace();
if (!cs.isEmpty()) {
const QByteArray cs_utf8 = cs.toUtf8();
const size_t copy_len = qMin(
static_cast<size_t>(cs_utf8.size()),
const size_t copy_len =
qMin(static_cast<size_t>(cs_utf8.size()),
sizeof(meta->colorspace) - 1);
memcpy(meta->colorspace, cs_utf8.constData(), copy_len);
meta->colorspace[copy_len] = '\0';
@@ -898,16 +916,16 @@ RenderWorkerPool::JobResult RenderWorkerPool::ProcessJobAttempt(
handshake.input_slots = input_slots.size();
handshake.output_slots = int(kOutputSlots);
handshake.slot_data_bytes = qint64(output_slot_bytes);
handshake.input_slot_data_bytes = input_slots.isEmpty()
? 0
: qint64(input_slot_bytes);
handshake.input_slot_data_bytes =
input_slots.isEmpty() ? 0 : qint64(input_slot_bytes);
if (!WriteControlMessage(worker->process, handshake.ToJson())) {
if (!job.ticket->IsCancelled()) {
qWarning() << "RenderWorkerPool failed to send shared-memory handshake";
qWarning()
<< "RenderWorkerPool failed to send shared-memory handshake";
}
ClearActiveWorker(worker_index, worker_process_id);
return job.ticket->IsCancelled() ? JobResult::kCancelled
: JobResult::kRetryableFailure;
return job.ticket->IsCancelled() ? JobResult::kCancelled :
JobResult::kRetryableFailure;
}
if (worker->loaded_graph_path != job.graph_path) {
@@ -922,12 +940,11 @@ RenderWorkerPool::JobResult RenderWorkerPool::ProcessJobAttempt(
<< error << worker->process->readAllStandardError();
}
ClearActiveWorker(worker_index, worker_process_id);
return job.ticket->IsCancelled() ? JobResult::kCancelled
: JobResult::kRetryableFailure;
return job.ticket->IsCancelled() ? JobResult::kCancelled :
JobResult::kRetryableFailure;
}
worker->loaded_graph_path = job.graph_path;
}
}
ipc::RenderFrameMsg render;
render.ticket_id = ticket_id;
render.node_uuid = job.node_token;
@@ -953,8 +970,8 @@ RenderWorkerPool::JobResult RenderWorkerPool::ProcessJobAttempt(
qWarning() << "RenderWorkerPool failed to send render_frame";
}
ClearActiveWorker(worker_index, worker_process_id);
return job.ticket->IsCancelled() ? JobResult::kCancelled
: JobResult::kRetryableFailure;
return job.ticket->IsCancelled() ? JobResult::kCancelled :
JobResult::kRetryableFailure;
}
QString error;
@@ -967,8 +984,8 @@ RenderWorkerPool::JobResult RenderWorkerPool::ProcessJobAttempt(
<< error << worker->process->readAllStandardError();
}
ClearActiveWorker(worker_index, worker_process_id);
return job.ticket->IsCancelled() ? JobResult::kCancelled
: JobResult::kRetryableFailure;
return job.ticket->IsCancelled() ? JobResult::kCancelled :
JobResult::kRetryableFailure;
}
if (ipc::FrameReadyMsg::FromJson(response, &ready)) {
@@ -1061,8 +1078,8 @@ std::unique_ptr<RenderWorkerPool::PooledWorker> RenderWorkerPool::AcquireWorker(
local_pool->erase(local_pool->begin() + i);
continue;
}
const bool candidate_state_running =
candidate->process->state() == QProcess::Running;
const bool candidate_state_running = candidate->process->state() ==
QProcess::Running;
const bool candidate_os_alive =
IsProcessAlive(candidate->process->processId());
if (!candidate_state_running && !candidate_os_alive) {
@@ -1078,7 +1095,8 @@ std::unique_ptr<RenderWorkerPool::PooledWorker> RenderWorkerPool::AcquireWorker(
if (best_index < 0 ||
(!candidate->loaded_graph_path.isEmpty() &&
candidate->loaded_graph_path == graph_path &&
((*local_pool)[size_t(best_index)]->loaded_graph_path != graph_path))) {
((*local_pool)[size_t(best_index)]->loaded_graph_path !=
graph_path))) {
best_index = int(i);
}
++i;
@@ -1093,14 +1111,14 @@ std::unique_ptr<RenderWorkerPool::PooledWorker> RenderWorkerPool::AcquireWorker(
return worker;
}
// No idle worker available: start a new one.
auto *process = new QProcess();
process->setProgram(WorkerProgramPath());
process->setArguments({QStringLiteral("--backend"), gpu_backend_});
process->setArguments({ QStringLiteral("--backend"), gpu_backend_ });
const QString worker_stderr_path = QDir(QDir::tempPath()).filePath(
QStringLiteral("oak-render-worker-%1-%2.stderr.log")
const QString worker_stderr_path =
QDir(QDir::tempPath())
.filePath(QStringLiteral("oak-render-worker-%1-%2.stderr.log")
.arg(QCoreApplication::applicationPid())
.arg(QDateTime::currentMSecsSinceEpoch()));
process->setStandardErrorFile(worker_stderr_path);
@@ -1133,8 +1151,7 @@ std::unique_ptr<RenderWorkerPool::PooledWorker> RenderWorkerPool::AcquireWorker(
void RenderWorkerPool::ReturnWorker(
std::vector<std::unique_ptr<PooledWorker>> *local_pool,
std::unique_ptr<PooledWorker> worker,
bool keep_alive)
std::unique_ptr<PooledWorker> worker, bool keep_alive)
{
if (!worker || !worker->process) {
return;
@@ -1190,9 +1207,11 @@ void RenderWorkerPool::ClearGraphCache()
{
QMutexLocker locker(&mutex_);
for (auto it = graph_cache_.begin(); it != graph_cache_.end(); ++it) {
CleanupGraphFile(it->path);
SetGraphPathCachedLocked(it->path, false);
}
graph_cache_.clear();
graph_path_ref_count_.clear();
cached_graph_paths_.clear();
}
void RenderWorkerPool::FinishWithFrame(RenderTicketPtr ticket,
@@ -1207,8 +1226,7 @@ void RenderWorkerPool::FinishWithFrame(RenderTicketPtr ticket,
}
VideoParams params(meta->width, meta->height,
PixelFormat::Format(meta->format),
meta->channel_count);
PixelFormat::Format(meta->format), meta->channel_count);
FramePtr frame = Frame::Create();
frame->set_timestamp(rational(int(meta->time_num), int(meta->time_den)));
frame->set_video_params(params);
@@ -1224,8 +1242,68 @@ void RenderWorkerPool::FinishWithFrame(RenderTicketPtr ticket,
void RenderWorkerPool::CleanupGraphFile(const QString &path)
{
if (!path.isEmpty()) {
qDebug() << "RenderWorkerPool cleaning up graph file" << path;
QFile::remove(path);
}
}
void RenderWorkerPool::AddGraphPathRef(const QString &path)
{
QMutexLocker locker(&mutex_);
AddGraphPathRefLocked(path);
}
void RenderWorkerPool::AddGraphPathRefLocked(const QString &path)
{
if (path.isEmpty()) {
return;
}
++graph_path_ref_count_[path];
}
void RenderWorkerPool::ReleaseGraphPathRef(const QString &path)
{
QMutexLocker locker(&mutex_);
ReleaseGraphPathRefLocked(path);
}
void RenderWorkerPool::ReleaseGraphPathRefLocked(const QString &path)
{
if (path.isEmpty()) {
return;
}
auto it = graph_path_ref_count_.find(path);
if (it == graph_path_ref_count_.end()) {
return;
}
if (--(*it) <= 0) {
graph_path_ref_count_.erase(it);
if (!cached_graph_paths_.contains(path)) {
CleanupGraphFile(path);
}
}
}
void RenderWorkerPool::SetGraphPathCached(const QString &path, bool cached)
{
QMutexLocker locker(&mutex_);
SetGraphPathCachedLocked(path, cached);
}
void RenderWorkerPool::SetGraphPathCachedLocked(const QString &path,
bool cached)
{
if (path.isEmpty()) {
return;
}
if (cached) {
cached_graph_paths_.insert(path);
} else {
cached_graph_paths_.remove(path);
if (!graph_path_ref_count_.contains(path)) {
CleanupGraphFile(path);
}
}
}
} // namespace olive

Some files were not shown because too many files have changed in this diff Show More