workspace: kebab-case crates, app under crates/oak-app, shared versions
CI / Build & test (Windows) (push) Failing after 7s
CI / Build & test (Windows) (push) Failing after 7s
All crates take the oak-* kebab-case naming (oak-audio, oak-codec, oak-common, oak-core, oak-ffmpeg-link, oak-node, oak-otio, oak-plugin, oak-render, oak-storage, oak-task, oak-timeline, oak-undo), with the lib identifiers rewritten (oakrender:: -> oak_render::, oakcore_rs:: -> oak_core::, ...) across all 226 referencing files. The GUI application moves from the workspace root into crates/oak-app/: src/, build.rs (paths fixed for the new location) and tests/ travel with it, the root Cargo.toml becomes workspace-only ([workspace] + workspace.package + profiles), and the app package inherits the workspace version. The screenshots example becomes a standalone crate examples/simple_player/ with its own Cargo.toml. Every crate now inherits the single workspace version (version.workspace = true), and the workflows' crate paths and the build docs follow the renames. Validated with a clean cargo check --workspace.
This commit is contained in:
@@ -49,8 +49,8 @@ jobs:
|
||||
# # ------------------------------------------------------------------
|
||||
# # Build environment
|
||||
# # ------------------------------------------------------------------
|
||||
# # ocio-sys builds a stub bridge unless these are set; the oakcommon
|
||||
# # ocioutils tests need the real library (see crates/oakcommon/.cargo/
|
||||
# # ocio-sys builds a stub bridge unless these are set; the oak-common
|
||||
# # ocioutils tests need the real library (see crates/oak-common/.cargo/
|
||||
# # config.toml, which only applies to builds run from that directory).
|
||||
# # ocio-sys builds its vendored OpenColorIO from source on Linux (the
|
||||
# # `bundled` feature; the distro package is too old for the bridge),
|
||||
@@ -167,17 +167,17 @@ jobs:
|
||||
# # machines have no system-wide OFX plugins, so the fixture is the
|
||||
# # only discovery.
|
||||
# - name: Build OFX fixture plugin
|
||||
# run: crates/oakplugin/tests/fixtures/build_fixture.sh .cache/ofx-fixture
|
||||
# run: crates/oak-plugin/tests/fixtures/build_fixture.sh .cache/ofx-fixture
|
||||
#
|
||||
# - name: Probe OFX plugin discovery
|
||||
# run: |
|
||||
# OFX_PLUGIN_PATH="$PWD/.cache/ofx-fixture" \
|
||||
# cargo run --locked -p oakplugin --example scan_probe > probe.log 2>&1
|
||||
# cargo run --locked -p oak-plugin --example scan_probe > probe.log 2>&1
|
||||
# grep -q 'type_id=rs.oak.CiTestPlugin' probe.log
|
||||
# # A project carrying a plugin node must survive save/load (the
|
||||
# # serializer resolves plugin types via the dynamic factory).
|
||||
# OAK_OFX_FIXTURE_DIR="$PWD/.cache/ofx-fixtre" \
|
||||
# cargo test --locked -p oakplugin --test ofx_roundtrip
|
||||
# cargo test --locked -p oak-plugin --test ofx_roundtrip
|
||||
|
||||
windows:
|
||||
name: Build & test (Windows)
|
||||
|
||||
Generated
+112
-102
@@ -4719,7 +4719,7 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oak"
|
||||
name = "oak-app"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"gpui",
|
||||
@@ -4727,79 +4727,66 @@ dependencies = [
|
||||
"gpui_platform",
|
||||
"gpui_widgets",
|
||||
"image",
|
||||
"oakaudio",
|
||||
"oakcodec",
|
||||
"oakcommon",
|
||||
"oakcore-rs",
|
||||
"oaknode",
|
||||
"oakplugin",
|
||||
"oakrender",
|
||||
"oakstorage",
|
||||
"oaktask",
|
||||
"oaktimeline",
|
||||
"oakundo",
|
||||
"oak-audio",
|
||||
"oak-codec",
|
||||
"oak-common",
|
||||
"oak-core",
|
||||
"oak-node",
|
||||
"oak-plugin",
|
||||
"oak-render",
|
||||
"oak-storage",
|
||||
"oak-task",
|
||||
"oak-timeline",
|
||||
"oak-undo",
|
||||
"serde_yaml",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oak-cli"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"oakcodec",
|
||||
"oakcommon",
|
||||
"oakcore-rs",
|
||||
"oaknode",
|
||||
"oakrender",
|
||||
"oaktask",
|
||||
"oaktimeline",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oak-worker"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"oakcore-rs",
|
||||
"oaknode",
|
||||
"oakplugin",
|
||||
"oakrender",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oakaudio"
|
||||
version = "0.1.0"
|
||||
name = "oak-audio"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"cpal",
|
||||
"ffmpeg-next",
|
||||
"oakcodec",
|
||||
"oakcommon",
|
||||
"oakcore-rs",
|
||||
"oakffmpeg-link",
|
||||
"oak-codec",
|
||||
"oak-common",
|
||||
"oak-core",
|
||||
"oak-ffmpeg-link",
|
||||
"thiserror 2.0.20",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oakcodec"
|
||||
version = "0.1.0"
|
||||
name = "oak-cli"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"oak-codec",
|
||||
"oak-common",
|
||||
"oak-core",
|
||||
"oak-node",
|
||||
"oak-render",
|
||||
"oak-task",
|
||||
"oak-timeline",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oak-codec"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"ffmpeg-next",
|
||||
"oakcommon",
|
||||
"oakcore-rs",
|
||||
"oakffmpeg-link",
|
||||
"oak-common",
|
||||
"oak-core",
|
||||
"oak-ffmpeg-link",
|
||||
"thiserror 2.0.20",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oakcommon"
|
||||
version = "0.1.0"
|
||||
name = "oak-common"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"image",
|
||||
"log",
|
||||
"oakcore-rs",
|
||||
"oak-core",
|
||||
"ocio-rs",
|
||||
"quick-xml 0.41.0",
|
||||
"thiserror 2.0.20",
|
||||
@@ -4807,29 +4794,29 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oakcore-rs"
|
||||
version = "0.1.0"
|
||||
name = "oak-core"
|
||||
version = "0.5.0"
|
||||
|
||||
[[package]]
|
||||
name = "oakffmpeg-link"
|
||||
version = "0.1.0"
|
||||
name = "oak-ffmpeg-link"
|
||||
version = "0.5.0"
|
||||
|
||||
[[package]]
|
||||
name = "oaknode"
|
||||
version = "0.1.0"
|
||||
name = "oak-node"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"oakcodec",
|
||||
"oakcommon",
|
||||
"oakcore-rs",
|
||||
"oakundo",
|
||||
"oak-codec",
|
||||
"oak-common",
|
||||
"oak-core",
|
||||
"oak-undo",
|
||||
"thiserror 2.0.20",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oakotio"
|
||||
version = "0.1.0"
|
||||
name = "oak-otio"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"oakcore-rs",
|
||||
"oak-core",
|
||||
"quick-xml 0.41.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -4837,26 +4824,26 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oakplugin"
|
||||
version = "0.1.0"
|
||||
name = "oak-plugin"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"oakcore-rs",
|
||||
"oaknode",
|
||||
"oakrender",
|
||||
"oakundo",
|
||||
"oak-core",
|
||||
"oak-node",
|
||||
"oak-render",
|
||||
"oak-undo",
|
||||
"thiserror 2.0.20",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oakrender"
|
||||
version = "0.1.0"
|
||||
name = "oak-render"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"oakcodec",
|
||||
"oakcommon",
|
||||
"oakcore-rs",
|
||||
"oaknode",
|
||||
"oak-codec",
|
||||
"oak-common",
|
||||
"oak-core",
|
||||
"oak-node",
|
||||
"ocio-rs",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -4865,15 +4852,15 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oakstorage"
|
||||
version = "0.1.0"
|
||||
name = "oak-storage"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"oakcommon",
|
||||
"oakcore-rs",
|
||||
"oaknode",
|
||||
"oakotio",
|
||||
"oakundo",
|
||||
"oak-common",
|
||||
"oak-core",
|
||||
"oak-node",
|
||||
"oak-otio",
|
||||
"oak-undo",
|
||||
"sea-orm",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -4882,37 +4869,50 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oaktask"
|
||||
version = "0.1.0"
|
||||
name = "oak-task"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"oakcodec",
|
||||
"oakcommon",
|
||||
"oakcore-rs",
|
||||
"oaknode",
|
||||
"oakotio",
|
||||
"oakrender",
|
||||
"oakundo",
|
||||
"oak-codec",
|
||||
"oak-common",
|
||||
"oak-core",
|
||||
"oak-node",
|
||||
"oak-otio",
|
||||
"oak-render",
|
||||
"oak-undo",
|
||||
"thiserror 2.0.20",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oaktimeline"
|
||||
version = "0.1.0"
|
||||
name = "oak-timeline"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"oakcommon",
|
||||
"oakcore-rs",
|
||||
"oaknode",
|
||||
"oakundo",
|
||||
"oak-common",
|
||||
"oak-core",
|
||||
"oak-node",
|
||||
"oak-undo",
|
||||
"thiserror 2.0.20",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oakundo"
|
||||
version = "0.1.0"
|
||||
name = "oak-undo"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"thiserror 2.0.20",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oak-worker"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"oak-core",
|
||||
"oak-node",
|
||||
"oak-plugin",
|
||||
"oak-render",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc"
|
||||
version = "0.2.7"
|
||||
@@ -6861,6 +6861,16 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
|
||||
|
||||
[[package]]
|
||||
name = "simple_player"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"gpui",
|
||||
"gpui_platform",
|
||||
"image",
|
||||
"oak-app",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simplecss"
|
||||
version = "0.2.2"
|
||||
|
||||
+12
-132
@@ -14,34 +14,35 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Root manifest: the `oakapp` package (the gpui-based application) plus the
|
||||
# Cargo workspace over every crate under crates/.
|
||||
# Root manifest: the Cargo workspace only — every package lives under
|
||||
# crates/ (the GUI app in oak-app, the CLI in oak-cli, the render worker
|
||||
# in oak-worker, the module rlibs in oak-*) or examples/ (standalone
|
||||
# integration crates, each with its own Cargo.toml).
|
||||
#
|
||||
# gpui (the oak-gpui fork at gpui/) is excluded: it is a separate git
|
||||
# repository with its own workspace (resolver 3, edition 2024,
|
||||
# workspace.package/workspace.dependencies). Without the exclusion its
|
||||
# crates would be auto-included here via oakapp's path dependencies and
|
||||
# crates would be auto-included here via oak-app's path dependencies and
|
||||
# would inherit from THIS workspace's [workspace.package] (which lacks the
|
||||
# keys gpui expects). Excluded, each gpui crate resolves against gpui's own
|
||||
# workspace root, exactly as before the monorepo workspace existed.
|
||||
[workspace]
|
||||
members = ["crates/*"]
|
||||
members = ["crates/*", "examples/*"]
|
||||
exclude = ["gpui", "crates/oakengine.bk"]
|
||||
# NOTE: oakstorage (crates/oakstorage) is a workspace member but NOT a
|
||||
# default member (it stays out of the default-members test matrix to keep
|
||||
# `cargo test` at the root fast; the app links it as a normal path
|
||||
# dependency, so it builds with the app). Build/test it explicitly with
|
||||
# `cargo test -p oakstorage`.
|
||||
# NOTE: oak-storage is a workspace member but NOT a default member (it
|
||||
# stays out of the default-members test matrix to keep `cargo test` at
|
||||
# the root fast; the app links it as a normal path dependency, so it
|
||||
# builds with the app). Build/test it explicitly with `cargo test -p oak-storage`.
|
||||
# NOTE: `crates/oakengine` (the frozen C-ABI facade cdylib) is retired:
|
||||
# every consumer (app/cli/worker/plugins) links the module rlibs
|
||||
# directly, so nothing in the workspace referenced it. The sources are
|
||||
# kept at crates/oakengine.bk (excluded from the workspace) as a
|
||||
# reference snapshot; git history is the authoritative backup.
|
||||
default-members = [".", "crates/oak-cli", "crates/oak-worker"]
|
||||
default-members = ["crates/oak-app", "crates/oak-cli", "crates/oak-worker"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
# Single source of truth for the release version: the root `oak` package
|
||||
# Single source of truth for the release version: the app package
|
||||
# inherits it, and with it cargo-packager and the CD packaging scripts.
|
||||
version = "0.5.0"
|
||||
|
||||
@@ -54,127 +55,6 @@ version = "0.5.0"
|
||||
# ignores).
|
||||
panic = "unwind"
|
||||
|
||||
[package]
|
||||
name = "oak"
|
||||
version.workspace = true
|
||||
edition = "2021"
|
||||
description = "Oak Video Editor"
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[lib]
|
||||
name = "oakapp"
|
||||
path = "src/lib.rs"
|
||||
# Doctests are disabled: the app links the oak* module crates (which carry
|
||||
# media/codec dependencies); the doc examples' assertions are covered by
|
||||
# unit tests instead (see `oakui/timecode`).
|
||||
doctest = false
|
||||
|
||||
[[bin]]
|
||||
name = "oak-editor"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
# gpui: the GPU-accelerated UI framework (oak-gpui fork, git submodule at gpui/).
|
||||
gpui = { path = "gpui/crates/gpui" }
|
||||
# Convenience entry point: `gpui_platform::application()` picks the platform
|
||||
# backend. font-kit enables text shaping/rendering on macOS.
|
||||
gpui_platform = { path = "gpui/crates/gpui_platform", features = ["font-kit"] }
|
||||
# Oak's widget library: menus, viewer, form controls, project explorer.
|
||||
gpui_widgets = { path = "gpui/crates/gpui_widgets" }
|
||||
# The mock engine's synthetic viewer frames (`image::Frame` in a
|
||||
# `RenderImage`), matching the versions gpui itself uses.
|
||||
image = "0.25"
|
||||
smallvec = "1"
|
||||
# Editable-text widget (used by the file / export dialogs' path fields, the
|
||||
# same gpui-elements crate gpui_widgets builds on).
|
||||
gpui_elements = { path = "gpui/crates/gpui_elements" }
|
||||
# UI string tables: YAML language packs under assets/i18n/, loaded at
|
||||
# runtime (bundled with the app, user-extensible) with the compiled-in
|
||||
# English table as the fallback.
|
||||
serde_yaml = "0.9"
|
||||
|
||||
# M14 R3: the app is a PURE module-crate consumer — every engine call is a
|
||||
# direct Rust call into the oak* rlibs (oaknode for the project graph,
|
||||
# oaktimeline/oakundo for the edit commands and the global undo stack,
|
||||
# oakrender for the ticket arena, oakcodec for the export formats/test
|
||||
# media, oaktask for the export/interchange tasks, oakaudio for the
|
||||
# manager/waveforms, oakcommon/oakcore-rs for the config store and shared
|
||||
# value types, oakstorage for the write-through library). No liboakengine
|
||||
# dylib, no C ABI, no build.rs link step, no host shims.
|
||||
oakaudio = { path = "crates/oakaudio" }
|
||||
oakcodec = { path = "crates/oakcodec" }
|
||||
oakcommon = { path = "crates/oakcommon" }
|
||||
oakcore-rs = { path = "crates/oakcore" }
|
||||
oaknode = { path = "crates/oaknode" }
|
||||
oakplugin = { path = "crates/oakplugin" }
|
||||
oakrender = { path = "crates/oakrender" }
|
||||
oakstorage = { path = "crates/oakstorage" }
|
||||
oaktask = { path = "crates/oaktask" }
|
||||
oaktimeline = { path = "crates/oaktimeline" }
|
||||
oakundo = { path = "crates/oakundo" }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
# Force the mock engine even though the real facade is linked. Off by
|
||||
# default: the app runs on the real engine unless `--mock` / `OAK_ENGINE=mock`
|
||||
# is given at runtime (or this feature is enabled at build time).
|
||||
mock-engine = []
|
||||
|
||||
[dev-dependencies]
|
||||
# `#[gpui::test]` harness for engine-seam smoke tests (test-support feature).
|
||||
gpui = { path = "gpui/crates/gpui", features = ["test-support"] }
|
||||
# `test-support` also enables `gpui_macos/test-support`, which is what makes
|
||||
# `render_to_image` (the screenshot example) available.
|
||||
gpui_platform = { path = "gpui/crates/gpui_platform", features = ["test-support"] }
|
||||
# Screenshot capture: `examples/screenshot.rs` saves the rendered window PNG
|
||||
# (the `image` crate is already in the lockfile through gpui).
|
||||
image = "0.25"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# cargo-packager metadata (the CD workflow, .github/workflows/cd.yml, packages
|
||||
# the release binaries with `cargo packager`; the schema lives at
|
||||
# https://docs.crabnebula.dev/packager/configuration/).
|
||||
#
|
||||
# Binaries are resolved relative to `target/<profile>` (oak-editor is the
|
||||
# main binary; oak-cli / oak-worker are bundled alongside). They must exist
|
||||
# before packaging runs — `cargo packager` does NOT build the app itself, so
|
||||
# CD builds with `cargo build --release` first.
|
||||
#
|
||||
# `icons/icon.png` is NOT committed: it is generated from `Oak_Icon.svg`
|
||||
# (rsvg-convert) in CD right before packaging, because cargo-packager needs a
|
||||
# bitmap (it converts a square PNG into .icns / .ico / hicolor PNGs itself).
|
||||
# It must be 512x512: cargo-packager's tauri-icns 0.1.0 maps only 512x512@1x
|
||||
# (and 1024x1024@2x) to an ICNS type — a plain 1024x1024 PNG aborts with
|
||||
# "No matching IconType".
|
||||
# Keep the path here in sync with the CD workflow.
|
||||
# ---------------------------------------------------------------------------
|
||||
[package.metadata.packager]
|
||||
name = "oak"
|
||||
productName = "Oak Video Editor"
|
||||
identifier = "org.oakvideoeditor.Oak"
|
||||
description = "Oak Video Editor"
|
||||
longDescription = "Oak Video Editor: a free, open-source non-linear video editor written in Rust."
|
||||
authors = ["Oak Team"]
|
||||
copyright = "Copyright (C) 2026 Oak Team"
|
||||
licenseFile = "LICENSE"
|
||||
category = "Video"
|
||||
icons = ["icons/icon.png"]
|
||||
# The UI string tables (language packs); users can drop extra
|
||||
# <lang>.yaml files into the installed i18n directory.
|
||||
# The Windows DLL staging dir is filled by
|
||||
# tooling/package/bundle-dylibs-windows.sh before `cargo packager` runs
|
||||
# (NSIS/WiX place resources next to the executable, which is exactly
|
||||
# where Windows resolves DLLs from). Empty locally = no bundled DLLs.
|
||||
resources = [
|
||||
"assets/i18n",
|
||||
{ src = "target/pkg/win-dlls/*.dll", target = "." },
|
||||
]
|
||||
binaries = [
|
||||
{ path = "oak-editor", main = true },
|
||||
{ path = "oak-cli", main = false },
|
||||
{ path = "oak-worker", main = false },
|
||||
]
|
||||
|
||||
[profile.dev.package."*"]
|
||||
opt-level = 1
|
||||
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
# Oak Video Editor - Non-Linear Video Editor
|
||||
# Copyright (C) 2026 Oak Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# The GUI application: the gpui-based `oakapp` library and the
|
||||
# `oak-editor` binary. Everything under src/ is app-specific; the engine
|
||||
# and module crates live in the workspace under crates/.
|
||||
|
||||
[package]
|
||||
name = "oak-app"
|
||||
version.workspace = true
|
||||
edition = "2021"
|
||||
description = "Oak Video Editor"
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[lib]
|
||||
name = "oakapp"
|
||||
path = "src/lib.rs"
|
||||
# Doctests are disabled: the app links the oak* module crates (which carry
|
||||
# media/codec dependencies); the doc examples' assertions are covered by
|
||||
# unit tests instead (see `oakui/timecode`).
|
||||
doctest = false
|
||||
|
||||
[[bin]]
|
||||
name = "oak-editor"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
# gpui: the GPU-accelerated UI framework (oak-gpui fork, git submodule at gpui/).
|
||||
gpui = { path = "../../gpui/crates/gpui" }
|
||||
# Convenience entry point: `gpui_platform::application()` picks the platform
|
||||
# backend. font-kit enables text shaping/rendering on macOS.
|
||||
gpui_platform = { path = "../../gpui/crates/gpui_platform", features = ["font-kit"] }
|
||||
# Oak's widget library: menus, viewer, form controls, project explorer.
|
||||
gpui_widgets = { path = "../../gpui/crates/gpui_widgets" }
|
||||
# The mock engine's synthetic viewer frames (`image::Frame` in a
|
||||
# `RenderImage`), matching the versions gpui itself uses.
|
||||
image = "0.25"
|
||||
smallvec = "1"
|
||||
# Editable-text widget (used by the file / export dialogs' path fields, the
|
||||
# same gpui-elements crate gpui_widgets builds on).
|
||||
gpui_elements = { path = "../../gpui/crates/gpui_elements" }
|
||||
# UI string tables: YAML language packs under assets/i18n/, loaded at
|
||||
# runtime (bundled with the app, user-extensible) with the compiled-in
|
||||
# English table as the fallback.
|
||||
serde_yaml = "0.9"
|
||||
|
||||
# M14 R3: the app is a PURE module-crate consumer — every engine call is a
|
||||
# direct Rust call into the oak* rlibs (oak-node for the project graph,
|
||||
# oak-timeline/oak-undo for the edit commands and the global undo stack,
|
||||
# oak-render for the ticket arena, oak-codec for the export formats/test
|
||||
# media, oak-task for the export/interchange tasks, oak-audio for the
|
||||
# manager/waveforms, oak-common/oak-core for the config store and shared
|
||||
# value types, oak-storage for the write-through library). No liboakengine
|
||||
# dylib, no C ABI, no build.rs link step, no host shims.
|
||||
oak-audio = { path = "../oak-audio" }
|
||||
oak-codec = { path = "../oak-codec" }
|
||||
oak-common = { path = "../oak-common" }
|
||||
oak-core = { path = "../oak-core" }
|
||||
oak-node = { path = "../oak-node" }
|
||||
oak-plugin = { path = "../oak-plugin" }
|
||||
oak-render = { path = "../oak-render" }
|
||||
oak-storage = { path = "../oak-storage" }
|
||||
oak-task = { path = "../oak-task" }
|
||||
oak-timeline = { path = "../oak-timeline" }
|
||||
oak-undo = { path = "../oak-undo" }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
# Force the mock engine even though the real facade is linked. Off by
|
||||
# default: the app runs on the real engine unless `--mock` / `OAK_ENGINE=mock`
|
||||
# is given at runtime (or this feature is enabled at build time).
|
||||
mock-engine = []
|
||||
|
||||
[dev-dependencies]
|
||||
# `#[gpui::test]` harness for engine-seam smoke tests (test-support feature).
|
||||
gpui = { path = "../../gpui/crates/gpui", features = ["test-support"] }
|
||||
# `test-support` also enables `gpui_macos/test-support`, which is what makes
|
||||
# `render_to_image` (the screenshot example) available.
|
||||
gpui_platform = { path = "../../gpui/crates/gpui_platform", features = ["test-support"] }
|
||||
# Screenshot capture: `examples/screenshot.rs` saves the rendered window PNG
|
||||
# (the `image` crate is already in the lockfile through gpui).
|
||||
image = "0.25"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# cargo-packager metadata (the CD workflow packages the release binaries
|
||||
# with `cargo packager`; the schema lives at
|
||||
# https://docs.crabnebula.dev/packager/configuration/).
|
||||
#
|
||||
# Binaries are resolved relative to `target/<profile>` (oak-editor is the
|
||||
# main binary; oak-cli / oak-worker are bundled alongside). They must exist
|
||||
# before packaging runs — `cargo packager` does NOT build the app itself, so
|
||||
# CD builds with `cargo build --release` first.
|
||||
#
|
||||
# `icons/icon.png` is NOT committed: it is generated from `Oak_Icon.svg`
|
||||
# (rsvg-convert) in CD right before packaging, because cargo-packager needs a
|
||||
# bitmap (it converts a square PNG into .icns / .ico / hicolor PNGs itself).
|
||||
# It must be 512x512: cargo-packager's tauri-icns 0.1.0 maps only 512x512@1x
|
||||
# (and 1024x1024@2x) to an ICNS type — a plain 1024x1024 PNG aborts with
|
||||
# "No matching IconType".
|
||||
# Keep the path here in sync with the CD workflow.
|
||||
# ---------------------------------------------------------------------------
|
||||
[package.metadata.packager]
|
||||
name = "oak"
|
||||
productName = "Oak Video Editor"
|
||||
identifier = "org.oakvideoeditor.Oak"
|
||||
description = "Oak Video Editor"
|
||||
longDescription = "Oak Video Editor: a free, open-source non-linear video editor written in Rust."
|
||||
authors = ["Oak Team"]
|
||||
copyright = "Copyright (C) 2026 Oak Team"
|
||||
licenseFile = "LICENSE"
|
||||
category = "Video"
|
||||
icons = ["../../icons/icon.png"]
|
||||
# The UI string tables (language packs); users can drop extra
|
||||
# <lang>.yaml files into the installed i18n directory.
|
||||
# The Windows DLL staging dir is filled by
|
||||
# tooling/package/bundle-dylibs-windows.sh before `cargo packager` runs
|
||||
# (NSIS/WiX place resources next to the executable, which is exactly
|
||||
# where Windows resolves DLLs from). Empty locally = no bundled DLLs.
|
||||
resources = [
|
||||
"../../assets/i18n",
|
||||
{ src = "../../target/pkg/win-dlls/*.dll", target = "." },
|
||||
]
|
||||
binaries = [
|
||||
{ path = "oak-editor", main = true },
|
||||
{ path = "oak-cli", main = false },
|
||||
{ path = "oak-worker", main = false },
|
||||
]
|
||||
@@ -39,12 +39,12 @@ fn main() {
|
||||
}
|
||||
// --- OFX interact end-to-end test plugin ---------------------------------
|
||||
// The app-side interact tests (src/oakui/ofx.rs) drive the *real*
|
||||
// minimal test plugin (crates/oakplugin/cbits/oak_test_plugin.c) through
|
||||
// minimal test plugin (../../crates/oak-plugin/cbits/oak_test_plugin.c) through
|
||||
// the app's interact wiring. oakplugin compiles the same C file into its
|
||||
// own OUT_DIR, but build-script env vars do not cross crates, so compile
|
||||
// it here too — the test assembles a plugin bundle from the app's
|
||||
// OUT_DIR.
|
||||
println!("cargo:rerun-if-changed=crates/oakplugin/cbits/oak_test_plugin.c");
|
||||
println!("cargo:rerun-if-changed=../../crates/oak-plugin/cbits/oak_test_plugin.c");
|
||||
build_test_plugin(&os);
|
||||
}
|
||||
|
||||
@@ -63,8 +63,8 @@ fn build_test_plugin(os: &str) {
|
||||
};
|
||||
let mut args = vec![
|
||||
"-fPIC".to_string(),
|
||||
"-Icrates/oakplugin/ofx".to_string(),
|
||||
"crates/oakplugin/cbits/oak_test_plugin.c".to_string(),
|
||||
"-I../../crates/oak-plugin/ofx".to_string(),
|
||||
"../../crates/oak-plugin/cbits/oak_test_plugin.c".to_string(),
|
||||
link_flag.to_string(),
|
||||
"-o".to_string(),
|
||||
format!("{out}/oak_test_plugin.{ext}"),
|
||||
@@ -463,7 +463,7 @@ pub(crate) fn shortcuts_test_lock() -> &'static Mutex<()> {
|
||||
/// The path of the custom-shortcuts file: `<config>/shortcuts`, exactly like
|
||||
/// the C++ `MainWindow::get_custom_shortcuts_file`.
|
||||
pub fn custom_shortcuts_path() -> String {
|
||||
let dir = oakcommon::filefunctions::FileFunctions::new()
|
||||
let dir = oak_common::filefunctions::FileFunctions::new()
|
||||
.get_configuration_location()
|
||||
.unwrap_or_else(|_| std::env::temp_dir().to_string_lossy().into_owned());
|
||||
format!("{}/shortcuts", dir.trim_end_matches('/'))
|
||||
@@ -2339,7 +2339,7 @@ impl MenuState {
|
||||
loop_playback: false,
|
||||
show_all: false,
|
||||
full_screen: false,
|
||||
use_proxy_media: oakcommon::configstore::ConfigStore::instance()
|
||||
use_proxy_media: oak_common::configstore::ConfigStore::instance()
|
||||
.get_bool(None, "UseProxyMedia", 1)
|
||||
!= 0,
|
||||
open_panels: WINDOW_PANELS
|
||||
@@ -32,8 +32,8 @@
|
||||
|
||||
use std::sync::{Arc, LazyLock, Mutex};
|
||||
|
||||
use oakcommon::configstore::ConfigStore;
|
||||
use oakrender::color::ColorProcessor;
|
||||
use oak_common::configstore::ConfigStore;
|
||||
use oak_render::color::ColorProcessor;
|
||||
|
||||
/// Config key: the display color management mode ("icc" / "off").
|
||||
pub const CONFIG_KEY_COLOR_MODE: &str = "DisplayColorMode";
|
||||
@@ -118,7 +118,7 @@ fn current() -> Option<State> {
|
||||
}
|
||||
// The custom override wins; empty = the platform's display profile.
|
||||
let icc = if icc_path.is_empty() {
|
||||
oakcommon::displayicc::system_display_icc()
|
||||
oak_common::displayicc::system_display_icc()
|
||||
} else {
|
||||
Some(icc_path.clone())
|
||||
};
|
||||
@@ -24,15 +24,15 @@
|
||||
//! `oakengine_node_effect_remove` / `..._move` / `..._set_enabled`), rebuilt
|
||||
//! over the direct graph API: chain nodes are `NodeId`s in the host's own
|
||||
//! project, and the undoable edits are one stack entry each (a multi
|
||||
//! command or a closure command from [`oakundo::undocommand::UndoCommand`]).
|
||||
//! command or a closure command from [`oak_undo::undocommand::UndoCommand`]).
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use oaknode::graph::{Graph, NodeEntry};
|
||||
use oaknode::id::NodeId;
|
||||
use oaknode::node::ENABLED_INPUT;
|
||||
use oaknode::value::NodeValue;
|
||||
use oakundo::undocommand::UndoCommand;
|
||||
use oak_node::graph::{Graph, NodeEntry};
|
||||
use oak_node::id::NodeId;
|
||||
use oak_node::node::ENABLED_INPUT;
|
||||
use oak_node::value::NodeValue;
|
||||
use oak_undo::undocommand::UndoCommand;
|
||||
|
||||
use super::graphops::{connect_command, disconnect_command, lock, push_command as push, ProjectRef};
|
||||
|
||||
@@ -100,9 +100,9 @@ pub fn is_enabled(g: &Graph, node: NodeId) -> bool {
|
||||
/// General — the C++ `factorymenu.cpp` OpenFX branch).
|
||||
pub fn addable_effects() -> Vec<super::engine::EffectEntry> {
|
||||
use super::engine::EffectEntry;
|
||||
use oaknode::node::flags as node_flags;
|
||||
use oak_node::node::flags as node_flags;
|
||||
let mut out = Vec::new();
|
||||
for meta in oaknode::factory::Factory::global().entries() {
|
||||
for meta in oak_node::factory::Factory::global().entries() {
|
||||
// A scratch instance per entry just to read its flags (the factory
|
||||
// metadata carries no flag copy).
|
||||
let (core, _behavior) = (meta.create)();
|
||||
@@ -122,7 +122,7 @@ pub fn addable_effects() -> Vec<super::engine::EffectEntry> {
|
||||
});
|
||||
}
|
||||
}
|
||||
for meta in oaknode::factory::Factory::global().dynamic_entries() {
|
||||
for meta in oak_node::factory::Factory::global().dynamic_entries() {
|
||||
// OpenFX plugin entries: grouped by sub-category. The factory
|
||||
// metadata carries no flags; plugin nodes are always video effects.
|
||||
let name = if meta.name.is_empty() {
|
||||
@@ -157,7 +157,7 @@ pub fn addable_effects() -> Vec<super::engine::EffectEntry> {
|
||||
/// persistent-message badge and to trigger push buttons.
|
||||
pub fn plugin_instance_handle(g: &Graph, node: NodeId) -> Option<u64> {
|
||||
let behavior = g.get(node)?.behavior.as_any()?;
|
||||
let plugin = behavior.downcast_ref::<oaknode::nodes::plugin::PluginNode>()?;
|
||||
let plugin = behavior.downcast_ref::<oak_node::nodes::plugin::PluginNode>()?;
|
||||
let handle = plugin.instance_handle();
|
||||
(!handle.is_null()).then_some(handle.0)
|
||||
}
|
||||
@@ -172,8 +172,8 @@ pub fn effect_params(
|
||||
g: &Graph,
|
||||
node: NodeId,
|
||||
) -> Option<Vec<super::engine::EffectParam>> {
|
||||
use oaknode::input::flags as input_flags;
|
||||
use oaknode::value::ValueType;
|
||||
use oak_node::input::flags as input_flags;
|
||||
use oak_node::value::ValueType;
|
||||
|
||||
let entry = g.get(node)?;
|
||||
let mut out = Vec::new();
|
||||
@@ -190,7 +190,7 @@ pub fn effect_params(
|
||||
continue;
|
||||
}
|
||||
// The standard enabled input is structural, not a parameter.
|
||||
if input.id == oaknode::node::ENABLED_INPUT {
|
||||
if input.id == oak_node::node::ENABLED_INPUT {
|
||||
continue;
|
||||
}
|
||||
// Display name: the behavior's localized input name (C++
|
||||
@@ -211,7 +211,7 @@ pub fn effect_params(
|
||||
for option in entry.behavior.input_combo_strings(&input.id) {
|
||||
properties.push((
|
||||
"combo_option".to_string(),
|
||||
oaknode::value::NodeValue::Text(option.to_string()),
|
||||
oak_node::value::NodeValue::Text(option.to_string()),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -236,7 +236,7 @@ pub fn combo_options(p: &super::engine::EffectParam) -> Vec<String> {
|
||||
.iter()
|
||||
.filter(|(k, _)| k == "combo_option")
|
||||
.filter_map(|(_, v)| match v {
|
||||
oaknode::value::NodeValue::Text(s) => Some(s.clone()),
|
||||
oak_node::value::NodeValue::Text(s) => Some(s.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
@@ -248,7 +248,7 @@ pub fn combo_values(p: &super::engine::EffectParam) -> Vec<String> {
|
||||
.iter()
|
||||
.filter(|(k, _)| k == "combo_value")
|
||||
.filter_map(|(_, v)| match v {
|
||||
oaknode::value::NodeValue::Text(s) => Some(s.clone()),
|
||||
oak_node::value::NodeValue::Text(s) => Some(s.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
@@ -262,7 +262,7 @@ pub fn ui_section_of(p: &super::engine::EffectParam) -> Option<(String, String)>
|
||||
.iter()
|
||||
.find(|(k, _)| k == "ui_group")
|
||||
.and_then(|(_, v)| match v {
|
||||
oaknode::value::NodeValue::Text(s) => Some(s.clone()),
|
||||
oak_node::value::NodeValue::Text(s) => Some(s.clone()),
|
||||
_ => None,
|
||||
});
|
||||
let page = p
|
||||
@@ -270,7 +270,7 @@ pub fn ui_section_of(p: &super::engine::EffectParam) -> Option<(String, String)>
|
||||
.iter()
|
||||
.find(|(k, _)| k == "ui_page")
|
||||
.and_then(|(_, v)| match v {
|
||||
oaknode::value::NodeValue::Text(s) => Some(s.clone()),
|
||||
oak_node::value::NodeValue::Text(s) => Some(s.clone()),
|
||||
_ => None,
|
||||
});
|
||||
match (group, page) {
|
||||
@@ -335,14 +335,14 @@ fn add_node_command(p: &ProjectRef, entry: NodeEntry) -> (UndoCommand, AddNodeSt
|
||||
/// ONE undo row on success / aborts (undoing the executed children) on
|
||||
/// error.
|
||||
fn grouped<T>(name: &str, f: impl FnOnce() -> Result<T, String>) -> Result<T, String> {
|
||||
oakundo::global::group_begin(name).map_err(|e| e.to_string())?;
|
||||
oak_undo::global::group_begin(name).map_err(|e| e.to_string())?;
|
||||
match f() {
|
||||
Ok(v) => {
|
||||
oakundo::global::group_end().map_err(|e| e.to_string())?;
|
||||
oak_undo::global::group_end().map_err(|e| e.to_string())?;
|
||||
Ok(v)
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = oakundo::global::group_abort();
|
||||
let _ = oak_undo::global::group_abort();
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
@@ -461,7 +461,7 @@ pub fn insert(p: &ProjectRef, host: NodeId, index: usize, type_id: &str) -> Resu
|
||||
if effect_input_of(&g.graph, host).is_none() {
|
||||
return Err("node cannot host effects (no effect input)".to_string());
|
||||
}
|
||||
let Some((core, behavior)) = oaknode::factory::Factory::global().create_any(type_id) else {
|
||||
let Some((core, behavior)) = oak_node::factory::Factory::global().create_any(type_id) else {
|
||||
return Err(format!("unknown node type id \"{type_id}\""));
|
||||
};
|
||||
if core.effect_input.is_empty() {
|
||||
@@ -680,7 +680,7 @@ mod tests {
|
||||
let project = graphops::create_project();
|
||||
let clip = {
|
||||
let mut g = lock(&project);
|
||||
let (core, behavior) = oaknode::block::clip_create();
|
||||
let (core, behavior) = oak_node::block::clip_create();
|
||||
g.graph.add_node(core, behavior)
|
||||
};
|
||||
(project, clip)
|
||||
@@ -698,7 +698,7 @@ mod tests {
|
||||
#[test]
|
||||
fn insert_walks_the_chain_and_undoes() {
|
||||
let _g = stack_lock();
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
let (project, host) = project_with_clip();
|
||||
let ty = some_effect_type();
|
||||
|
||||
@@ -710,19 +710,19 @@ mod tests {
|
||||
assert_eq!(chain(&lock(&project).graph, host), vec![second, third, first]);
|
||||
|
||||
// One undo row per insert.
|
||||
oakundo::global::undo().unwrap();
|
||||
oak_undo::global::undo().unwrap();
|
||||
assert_eq!(chain(&lock(&project).graph, host), vec![second, first]);
|
||||
oakundo::global::undo().unwrap();
|
||||
oak_undo::global::undo().unwrap();
|
||||
assert_eq!(chain(&lock(&project).graph, host), vec![first]);
|
||||
oakundo::global::redo().unwrap();
|
||||
oak_undo::global::redo().unwrap();
|
||||
assert_eq!(chain(&lock(&project).graph, host), vec![second, first]);
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_bridges_the_gap() {
|
||||
let _g = stack_lock();
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
let (project, host) = project_with_clip();
|
||||
let ty = some_effect_type();
|
||||
let a = insert(&project, host, 0, &ty).unwrap();
|
||||
@@ -732,15 +732,15 @@ mod tests {
|
||||
|
||||
remove(&project, host, b).expect("remove the middle effect");
|
||||
assert_eq!(chain(&lock(&project).graph, host), vec![a, c]);
|
||||
oakundo::global::undo().unwrap();
|
||||
oak_undo::global::undo().unwrap();
|
||||
assert_eq!(chain(&lock(&project).graph, host), vec![a, b, c]);
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn move_reorders_the_chain() {
|
||||
let _g = stack_lock();
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
let (project, host) = project_with_clip();
|
||||
let ty = some_effect_type();
|
||||
let a = insert(&project, host, 0, &ty).unwrap();
|
||||
@@ -749,15 +749,15 @@ mod tests {
|
||||
|
||||
move_effect(&project, host, c, 0).expect("move the last effect to the source side");
|
||||
assert_eq!(chain(&lock(&project).graph, host), vec![c, a, b]);
|
||||
oakundo::global::undo().unwrap();
|
||||
oak_undo::global::undo().unwrap();
|
||||
assert_eq!(chain(&lock(&project).graph, host), vec![a, b, c]);
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_enabled_toggles_and_undoes() {
|
||||
let _g = stack_lock();
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
let (project, host) = project_with_clip();
|
||||
let ty = some_effect_type();
|
||||
let eff = insert(&project, host, 0, &ty).unwrap();
|
||||
@@ -765,9 +765,9 @@ mod tests {
|
||||
|
||||
set_enabled(&project, eff, false).unwrap();
|
||||
assert!(!is_enabled(&lock(&project).graph, eff));
|
||||
oakundo::global::undo().unwrap();
|
||||
oak_undo::global::undo().unwrap();
|
||||
assert!(is_enabled(&lock(&project).graph, eff));
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -800,7 +800,7 @@ mod tests {
|
||||
#[test]
|
||||
fn set_input_value_undoes() {
|
||||
let _g = stack_lock();
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
let (project, host) = project_with_clip();
|
||||
|
||||
// Choose a built-in effect whose scratch core exposes a float input
|
||||
@@ -808,12 +808,12 @@ mod tests {
|
||||
let ty = addable_effects()
|
||||
.into_iter()
|
||||
.find(|entry| {
|
||||
let (core, _behavior) = oaknode::factory::Factory::global()
|
||||
let (core, _behavior) = oak_node::factory::Factory::global()
|
||||
.create_any(&entry.type_id)
|
||||
.expect("the entry resolves");
|
||||
core.inputs.iter().any(|i| {
|
||||
i.value_type == oaknode::value::ValueType::Float
|
||||
&& i.flags & oaknode::input::flags::HIDDEN == 0
|
||||
i.value_type == oak_node::value::ValueType::Float
|
||||
&& i.flags & oak_node::input::flags::HIDDEN == 0
|
||||
})
|
||||
})
|
||||
.expect("at least one built-in effect exposes a float input")
|
||||
@@ -831,8 +831,8 @@ mod tests {
|
||||
.get(eff)
|
||||
.and_then(|e| {
|
||||
e.core.inputs.iter().find(|i| {
|
||||
i.value_type == oaknode::value::ValueType::Float
|
||||
&& i.flags & oaknode::input::flags::HIDDEN == 0
|
||||
i.value_type == oak_node::value::ValueType::Float
|
||||
&& i.flags & oak_node::input::flags::HIDDEN == 0
|
||||
})
|
||||
})
|
||||
.map(|i| i.id.clone())
|
||||
@@ -849,12 +849,12 @@ mod tests {
|
||||
lock(&project).graph.get(eff).unwrap().core.standard_value(&input_id, -1),
|
||||
NodeValue::Float(42.0)
|
||||
);
|
||||
oakundo::global::undo().unwrap();
|
||||
oak_undo::global::undo().unwrap();
|
||||
assert_eq!(
|
||||
lock(&project).graph.get(eff).unwrap().core.standard_value(&input_id, -1),
|
||||
before
|
||||
);
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
}
|
||||
|
||||
/// The combo-option collector reads the repeated `("combo_option", _)`
|
||||
@@ -862,7 +862,7 @@ mod tests {
|
||||
#[test]
|
||||
fn combo_option_collectors_read_repeated_properties() {
|
||||
use crate::oakui::engine::EffectParam;
|
||||
use oaknode::value::{NodeValue, ValueType};
|
||||
use oak_node::value::{NodeValue, ValueType};
|
||||
let param = EffectParam {
|
||||
input_id: "mode".into(),
|
||||
display_name: "Mode".into(),
|
||||
@@ -885,7 +885,7 @@ mod tests {
|
||||
#[test]
|
||||
fn ui_section_collects_group_and_page() {
|
||||
use crate::oakui::engine::EffectParam;
|
||||
use oaknode::value::{NodeValue, ValueType};
|
||||
use oak_node::value::{NodeValue, ValueType};
|
||||
let plain = EffectParam {
|
||||
input_id: "p".into(),
|
||||
display_name: "P".into(),
|
||||
@@ -179,20 +179,20 @@ pub struct EffectParam {
|
||||
/// The display name (the OFX param label).
|
||||
pub display_name: String,
|
||||
/// The value type.
|
||||
pub value_type: oaknode::value::ValueType,
|
||||
pub value_type: oak_node::value::ValueType,
|
||||
/// The current value.
|
||||
pub value: oaknode::value::NodeValue,
|
||||
/// The input flag bits (`oaknode::input::flags::*`).
|
||||
pub value: oak_node::value::NodeValue,
|
||||
/// The input flag bits (`oak_node::input::flags::*`).
|
||||
pub flags: u32,
|
||||
/// The input properties (`combo_option` / `combo_value` / `ui_group` /
|
||||
/// `ui_page` / `min` / `max` / ...).
|
||||
pub properties: Vec<(String, oaknode::value::NodeValue)>,
|
||||
pub properties: Vec<(String, oak_node::value::NodeValue)>,
|
||||
}
|
||||
|
||||
/// The i18n key of a node category submenu, or `None` for categories that
|
||||
/// never appear in the node editor's Add menu (timeline-structural nodes).
|
||||
pub fn node_category_key(category: oaknode::node::Category) -> Option<&'static str> {
|
||||
use oaknode::node::Category as C;
|
||||
pub fn node_category_key(category: oak_node::node::Category) -> Option<&'static str> {
|
||||
use oak_node::node::Category as C;
|
||||
Some(match category {
|
||||
C::Output => "node.category.output",
|
||||
C::Effect => "node.category.effect",
|
||||
@@ -374,7 +374,7 @@ pub trait AppEngine:
|
||||
&mut self,
|
||||
_effect: EffectId,
|
||||
_input_id: &str,
|
||||
_value: oaknode::value::NodeValue,
|
||||
_value: oak_node::value::NodeValue,
|
||||
_cx: &mut Context<Self>,
|
||||
) -> Result<(), String> {
|
||||
Err("effect params not supported".into())
|
||||
@@ -431,12 +431,12 @@ pub trait AppEngine:
|
||||
/// included.
|
||||
fn node_library(&self) -> Vec<NodeLibraryEntry> {
|
||||
let mut out = Vec::new();
|
||||
let factory = oaknode::factory::Factory::global();
|
||||
let factory = oak_node::factory::Factory::global();
|
||||
for meta in factory.entries() {
|
||||
// A scratch instance per entry just to read its flags (the
|
||||
// factory metadata carries no flag copy).
|
||||
let (core, _behavior) = (meta.create)();
|
||||
if core.flags & oaknode::node::flags::DONT_SHOW_IN_CREATE_MENU != 0 {
|
||||
if core.flags & oak_node::node::flags::DONT_SHOW_IN_CREATE_MENU != 0 {
|
||||
continue;
|
||||
}
|
||||
let Some(category_key) = meta.categories.first().copied().and_then(node_category_key)
|
||||
@@ -757,7 +757,7 @@ pub trait AppEngine:
|
||||
/// The global "Use Proxy Media" switch (the C++ `UseProxyMedia`
|
||||
/// config; preview-only — exports always decode the original media).
|
||||
fn use_proxy_media(&self) -> bool {
|
||||
oakcommon::configstore::ConfigStore::instance()
|
||||
oak_common::configstore::ConfigStore::instance()
|
||||
.get_bool(None, "UseProxyMedia", 1)
|
||||
!= 0
|
||||
}
|
||||
@@ -766,7 +766,7 @@ pub trait AppEngine:
|
||||
/// footage's rendered frames (the C++ toggles the config and
|
||||
/// re-renders; the preview path reads the switch on every montage).
|
||||
fn set_use_proxy_media(&mut self, enabled: bool, cx: &mut Context<Self>) {
|
||||
oakcommon::configstore::ConfigStore::instance().set(
|
||||
oak_common::configstore::ConfigStore::instance().set(
|
||||
None,
|
||||
"UseProxyMedia",
|
||||
if enabled { "true" } else { "false" },
|
||||
@@ -779,7 +779,7 @@ pub trait AppEngine:
|
||||
/// size, 2/4/8 = progressively smaller preview renders for machines
|
||||
/// that cannot keep up. Preview-only; exports always render native.
|
||||
fn playback_divider(&self) -> i64 {
|
||||
oakcommon::configstore::ConfigStore::instance()
|
||||
oak_common::configstore::ConfigStore::instance()
|
||||
.get_int(None, "PlaybackDivider", 1)
|
||||
.clamp(1, 8) as i64
|
||||
}
|
||||
@@ -787,7 +787,7 @@ pub trait AppEngine:
|
||||
/// Sets the playback resolution divider and invalidates the rendered
|
||||
/// frames so the next pull re-renders at the new geometry.
|
||||
fn set_playback_divider(&mut self, divider: i64, cx: &mut Context<Self>) {
|
||||
oakcommon::configstore::ConfigStore::instance().set(
|
||||
oak_common::configstore::ConfigStore::instance().set(
|
||||
None,
|
||||
"PlaybackDivider",
|
||||
÷r.clamp(1, 8).to_string(),
|
||||
@@ -817,7 +817,7 @@ pub trait AppEngine:
|
||||
}
|
||||
|
||||
/// Starts generating the proxy of footage `id` (a background ffmpeg
|
||||
/// transcode through `oaktask::ProxyTask`). Progress is reported
|
||||
/// transcode through `oak_task::ProxyTask`). Progress is reported
|
||||
/// through [`proxy_task_progress`](Self::proxy_task_progress) and
|
||||
/// drained on the engine tick; completion invalidates the footage's
|
||||
/// rendered frames.
|
||||
@@ -1005,7 +1005,7 @@ pub struct MulticamState {
|
||||
}
|
||||
|
||||
/// The lifecycle state of one footage's proxy (the UI mirror of
|
||||
/// `oakcodec::proxymanager::ProxyState`).
|
||||
/// `oak_codec::proxymanager::ProxyState`).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ProxyMediaState {
|
||||
/// No proxy on disk (the menu offers "Generate").
|
||||
@@ -1019,7 +1019,7 @@ pub enum ProxyMediaState {
|
||||
}
|
||||
|
||||
/// The proxy generation parameters the proxy dialog edits (the app-side
|
||||
/// mirror of `oakcodec::proxymanager::ProxyParams`, minus the fixed
|
||||
/// mirror of `oak_codec::proxymanager::ProxyParams`, minus the fixed
|
||||
/// version / extension).
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ProxyParamsUi {
|
||||
@@ -1041,7 +1041,7 @@ pub struct ProxyParamsUi {
|
||||
/// default values): `ProxyWidth`/`ProxyHeight`/`ProxyDivider`/`ProxyCRF`/
|
||||
/// `ProxyPreset`/`ProxyIncludeAudio`.
|
||||
pub fn proxy_params_from_config() -> ProxyParamsUi {
|
||||
let codec = oakcodec::proxymanager::ProxyManager::proxy_params_from_config();
|
||||
let codec = oak_codec::proxymanager::ProxyManager::proxy_params_from_config();
|
||||
let end = |a: &[u8; 32]| a.iter().position(|&b| b == 0).unwrap_or(a.len());
|
||||
let preset = std::str::from_utf8(&codec.preset[..end(&codec.preset)])
|
||||
.unwrap_or("")
|
||||
@@ -1058,8 +1058,8 @@ pub fn proxy_params_from_config() -> ProxyParamsUi {
|
||||
|
||||
impl ProxyParamsUi {
|
||||
/// The codec-side params these UI params stand for.
|
||||
pub fn to_codec(&self) -> oakcodec::proxymanager::ProxyParams {
|
||||
let mut p = oakcodec::proxymanager::ProxyParams::default();
|
||||
pub fn to_codec(&self) -> oak_codec::proxymanager::ProxyParams {
|
||||
let mut p = oak_codec::proxymanager::ProxyParams::default();
|
||||
p.width = self.width;
|
||||
p.height = self.height;
|
||||
p.divider = self.divider;
|
||||
@@ -1072,7 +1072,7 @@ impl ProxyParamsUi {
|
||||
}
|
||||
|
||||
/// UI params from codec-side params.
|
||||
pub fn from_codec(codec: &oakcodec::proxymanager::ProxyParams) -> ProxyParamsUi {
|
||||
pub fn from_codec(codec: &oak_codec::proxymanager::ProxyParams) -> ProxyParamsUi {
|
||||
let end = |a: &[u8; 32]| a.iter().position(|&b| b == 0).unwrap_or(a.len());
|
||||
ProxyParamsUi {
|
||||
width: codec.width,
|
||||
@@ -32,18 +32,18 @@
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
|
||||
use oakcore_rs::{Rational, TimeRange};
|
||||
use oaknode::block::ClipBlockBehavior;
|
||||
use oaknode::footage::FootageBehavior;
|
||||
use oaknode::graph::Graph;
|
||||
use oaknode::id::NodeId;
|
||||
use oaknode::project::Project;
|
||||
use oaknode::sequence::SequenceBehavior;
|
||||
use oaknode::track::{TrackBehavior, TrackListBehavior, TrackType};
|
||||
use oaktimeline::handle::CHandle;
|
||||
use oaktimeline::util::NodeRef;
|
||||
use oak_core::{Rational, TimeRange};
|
||||
use oak_node::block::ClipBlockBehavior;
|
||||
use oak_node::footage::FootageBehavior;
|
||||
use oak_node::graph::Graph;
|
||||
use oak_node::id::NodeId;
|
||||
use oak_node::project::Project;
|
||||
use oak_node::sequence::SequenceBehavior;
|
||||
use oak_node::track::{TrackBehavior, TrackListBehavior, TrackType};
|
||||
use oak_timeline::handle::CHandle;
|
||||
use oak_timeline::util::NodeRef;
|
||||
|
||||
use oakstorage::backend::StorageBackend;
|
||||
use oak_storage::backend::StorageBackend;
|
||||
|
||||
use super::engine::LibraryProject;
|
||||
|
||||
@@ -116,7 +116,7 @@ pub fn footage_behavior(g: &Graph, id: NodeId) -> Option<&FootageBehavior> {
|
||||
pub fn is_folder(g: &Graph, id: NodeId) -> bool {
|
||||
g.get(id)
|
||||
.and_then(|e| e.behavior.as_any())
|
||||
.and_then(|a| a.downcast_ref::<oaknode::folder::FolderBehavior>())
|
||||
.and_then(|a| a.downcast_ref::<oak_node::folder::FolderBehavior>())
|
||||
.is_some()
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ pub fn load_ove(path: &Path) -> Result<ProjectRef, String> {
|
||||
let xml = std::fs::read_to_string(path)
|
||||
.map_err(|e| format!("failed to read \"{}\": {e}", path.display()))?;
|
||||
let project =
|
||||
oaknode::serializer::load(&xml).map_err(|e| format!("failed to parse: {e}"))?;
|
||||
oak_node::serializer::load(&xml).map_err(|e| format!("failed to parse: {e}"))?;
|
||||
let abs = if path.is_absolute() {
|
||||
path.to_path_buf()
|
||||
} else {
|
||||
@@ -162,7 +162,7 @@ pub fn load_ove(path: &Path) -> Result<ProjectRef, String> {
|
||||
pub fn save_ove(project: &ProjectRef, path: &Path) -> Result<(), String> {
|
||||
let xml = {
|
||||
let guard = lock(project);
|
||||
oaknode::serializer::save(&guard).map_err(|e| format!("failed to serialize: {e}"))?
|
||||
oak_node::serializer::save(&guard).map_err(|e| format!("failed to serialize: {e}"))?
|
||||
};
|
||||
std::fs::write(path, &xml).map_err(|e| format!("failed to write \"{}\": {e}", path.display()))?;
|
||||
let mut guard = lock(project);
|
||||
@@ -217,7 +217,7 @@ pub fn create_sequence(project: &ProjectRef, name: &str) -> NodeId {
|
||||
let Some(list) = find_or_create_track_list(project, seq, kind) else {
|
||||
continue;
|
||||
};
|
||||
oaktimeline::undogeneral::TimelineAddTrackCommand::new(node_ref(project, list)).redo();
|
||||
oak_timeline::undogeneral::TimelineAddTrackCommand::new(node_ref(project, list)).redo();
|
||||
}
|
||||
seq
|
||||
}
|
||||
@@ -287,15 +287,15 @@ pub fn sequence_length(g: &Graph, seq: NodeId) -> Rational {
|
||||
}
|
||||
|
||||
/// Borrow a block's core (any block kind).
|
||||
fn block_core(entry: &oaknode::graph::NodeEntry) -> Option<&oaknode::block::BlockCore> {
|
||||
fn block_core(entry: &oak_node::graph::NodeEntry) -> Option<&oak_node::block::BlockCore> {
|
||||
let any = entry.behavior.as_any()?;
|
||||
if let Some(c) = any.downcast_ref::<ClipBlockBehavior>() {
|
||||
return Some(&c.core);
|
||||
}
|
||||
if let Some(g) = any.downcast_ref::<oaknode::block::GapBlockBehavior>() {
|
||||
if let Some(g) = any.downcast_ref::<oak_node::block::GapBlockBehavior>() {
|
||||
return Some(&g.core);
|
||||
}
|
||||
any.downcast_ref::<oaknode::block::TransitionBlockBehavior>()
|
||||
any.downcast_ref::<oak_node::block::TransitionBlockBehavior>()
|
||||
.map(|t| &t.core)
|
||||
}
|
||||
|
||||
@@ -560,7 +560,7 @@ pub fn import_footage(project: &ProjectRef, path: &Path) -> Result<NodeId, Strin
|
||||
return Err("the project has no root folder".to_string());
|
||||
}
|
||||
let (mut core, mut behavior) = FootageBehavior::create();
|
||||
core.set_standard_value("file_in", -1, oaknode::value::NodeValue::Text(filename.clone()));
|
||||
core.set_standard_value("file_in", -1, oak_node::value::NodeValue::Text(filename.clone()));
|
||||
let Some(f) = behavior
|
||||
.as_any_mut()
|
||||
.and_then(|a| a.downcast_mut::<FootageBehavior>())
|
||||
@@ -582,11 +582,11 @@ pub fn import_footage(project: &ProjectRef, path: &Path) -> Result<NodeId, Strin
|
||||
}
|
||||
(guard.root, id)
|
||||
};
|
||||
let cmd = oaktask::nodeops::folder_add_child_command(
|
||||
let cmd = oak_task::nodeops::folder_add_child_command(
|
||||
(project.clone(), root),
|
||||
(project.clone(), id),
|
||||
);
|
||||
oakundo::global::push(cmd, "Import Footage").map_err(|e| e.to_string())?;
|
||||
oak_undo::global::push(cmd, "Import Footage").map_err(|e| e.to_string())?;
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
@@ -601,12 +601,12 @@ pub fn import_footage(project: &ProjectRef, path: &Path) -> Result<NodeId, Strin
|
||||
|
||||
/// Create a marker-list handle (owned, refcount 1).
|
||||
pub fn marker_list_create() -> CHandle {
|
||||
oaktimeline::handle::make_owned(oaktimeline::marker::TimelineMarkerList::new())
|
||||
oak_timeline::handle::make_owned(oak_timeline::marker::TimelineMarkerList::new())
|
||||
}
|
||||
|
||||
/// Create a workarea handle (owned, refcount 1).
|
||||
pub fn workarea_create() -> CHandle {
|
||||
oaktimeline::handle::make_owned(oaktimeline::workarea::TimelineWorkArea::new())
|
||||
oak_timeline::handle::make_owned(oak_timeline::workarea::TimelineWorkArea::new())
|
||||
}
|
||||
|
||||
/// Release an owned marker-list / workarea handle (NULL-safe; the handle
|
||||
@@ -629,7 +629,7 @@ pub fn markers_of(list: &CHandle) -> Vec<(Rational, String, i32)> {
|
||||
// SAFETY: `list` boxes a `TimelineMarkerList` (created by
|
||||
// `marker_list_create`); the read is shared and brief.
|
||||
let Some(l) = (unsafe {
|
||||
oaktimeline::handle::get::<std::sync::Arc<std::sync::Mutex<oaktimeline::marker::TimelineMarkerList>>>(
|
||||
oak_timeline::handle::get::<std::sync::Arc<std::sync::Mutex<oak_timeline::marker::TimelineMarkerList>>>(
|
||||
list,
|
||||
)
|
||||
}) else {
|
||||
@@ -649,7 +649,7 @@ pub fn marker_index_at(list: &CHandle, time: Rational) -> Option<usize> {
|
||||
}
|
||||
// SAFETY: as `markers_of`.
|
||||
let l = unsafe {
|
||||
oaktimeline::handle::get::<std::sync::Arc<std::sync::Mutex<oaktimeline::marker::TimelineMarkerList>>>(
|
||||
oak_timeline::handle::get::<std::sync::Arc<std::sync::Mutex<oak_timeline::marker::TimelineMarkerList>>>(
|
||||
list,
|
||||
)
|
||||
}?;
|
||||
@@ -665,7 +665,7 @@ pub fn workarea_state(wa: &CHandle) -> Option<(bool, TimeRange)> {
|
||||
// SAFETY: `wa` boxes a `TimelineWorkArea` (created by
|
||||
// `workarea_create`); the read is shared and brief.
|
||||
let w = unsafe {
|
||||
oaktimeline::handle::get::<std::sync::Arc<std::sync::Mutex<oaktimeline::workarea::TimelineWorkArea>>>(
|
||||
oak_timeline::handle::get::<std::sync::Arc<std::sync::Mutex<oak_timeline::workarea::TimelineWorkArea>>>(
|
||||
wa,
|
||||
)
|
||||
}?;
|
||||
@@ -681,7 +681,7 @@ pub fn workarea_set(wa: &CHandle, enabled: bool, range: TimeRange) {
|
||||
// SAFETY: `wa` boxes a `TimelineWorkArea`; the engine writes it only
|
||||
// from the UI thread.
|
||||
if let Some(w) = unsafe {
|
||||
oaktimeline::handle::get_mut::<std::sync::Arc<std::sync::Mutex<oaktimeline::workarea::TimelineWorkArea>>>(
|
||||
oak_timeline::handle::get_mut::<std::sync::Arc<std::sync::Mutex<oak_timeline::workarea::TimelineWorkArea>>>(
|
||||
wa,
|
||||
)
|
||||
} {
|
||||
@@ -700,19 +700,19 @@ pub fn workarea_set(wa: &CHandle, enabled: bool, range: TimeRange) {
|
||||
|
||||
/// The configured default library as a parsed URI; an error when the
|
||||
/// write-through backend is disabled or the path does not resolve.
|
||||
fn library() -> Result<oakstorage::uri::StorageUri, String> {
|
||||
if !oakstorage::writethrough::storage_enabled() {
|
||||
fn library() -> Result<oak_storage::uri::StorageUri, String> {
|
||||
if !oak_storage::writethrough::storage_enabled() {
|
||||
return Err("the project library is not configured".to_string());
|
||||
}
|
||||
let uri = oakstorage::writethrough::library_uri()
|
||||
let uri = oak_storage::writethrough::library_uri()
|
||||
.ok_or_else(|| "the project library path does not resolve".to_string())?;
|
||||
oakstorage::uri::StorageUri::parse(&uri).map_err(|e| e.to_string())
|
||||
oak_storage::uri::StorageUri::parse(&uri).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// The library URI selecting one row (`…?project=<uuid>`).
|
||||
fn project_uri(uuid: &str) -> Result<oakstorage::uri::StorageUri, String> {
|
||||
fn project_uri(uuid: &str) -> Result<oak_storage::uri::StorageUri, String> {
|
||||
let uri = library()?;
|
||||
oakstorage::uri::StorageUri::parse(&format!("{}?project={uuid}", uri.to_uri_string()))
|
||||
oak_storage::uri::StorageUri::parse(&format!("{}?project={uuid}", uri.to_uri_string()))
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
@@ -720,11 +720,11 @@ fn project_uri(uuid: &str) -> Result<oakstorage::uri::StorageUri, String> {
|
||||
/// data source). With storage disabled the result is the empty list, not
|
||||
/// an error (the facade contract).
|
||||
pub fn library_list() -> Result<Vec<LibraryProject>, String> {
|
||||
if !oakstorage::writethrough::storage_enabled() {
|
||||
if !oak_storage::writethrough::storage_enabled() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
let uri = library()?;
|
||||
let backend = oakstorage::writethrough::backend();
|
||||
let backend = oak_storage::writethrough::backend();
|
||||
let infos = backend.list_projects(&uri).map_err(|e| e.to_string())?;
|
||||
Ok(infos
|
||||
.into_iter()
|
||||
@@ -760,7 +760,7 @@ pub fn library_create(name: &str) -> Result<String, String> {
|
||||
.insert("projectname".to_string(), name.to_string());
|
||||
guard.uuid.clone()
|
||||
};
|
||||
let result = oakstorage::writethrough::backend()
|
||||
let result = oak_storage::writethrough::backend()
|
||||
.save_project(&project, &uri, 0)
|
||||
.map_err(|e| e.to_string());
|
||||
result?;
|
||||
@@ -773,7 +773,7 @@ pub fn library_delete(uuid: &str) -> Result<(), String> {
|
||||
if uuid.is_empty() {
|
||||
return Err("invalid uuid".to_string());
|
||||
}
|
||||
oakstorage::writethrough::backend()
|
||||
oak_storage::writethrough::backend()
|
||||
.delete_project(&library()?, uuid)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
@@ -783,7 +783,7 @@ pub fn library_rename(uuid: &str, name: &str) -> Result<(), String> {
|
||||
if uuid.is_empty() || name.trim().is_empty() {
|
||||
return Err("invalid uuid or name".to_string());
|
||||
}
|
||||
oakstorage::writethrough::backend()
|
||||
oak_storage::writethrough::backend()
|
||||
.rename_project(&library()?, uuid, name.trim())
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
@@ -795,7 +795,7 @@ pub fn library_duplicate(uuid: &str) -> Result<String, String> {
|
||||
if uuid.is_empty() {
|
||||
return Err("invalid uuid".to_string());
|
||||
}
|
||||
let info = oakstorage::writethrough::backend()
|
||||
let info = oak_storage::writethrough::backend()
|
||||
.duplicate_project(&library()?, uuid, None)
|
||||
.map_err(|e| e.to_string())?;
|
||||
Ok(info.uuid)
|
||||
@@ -804,9 +804,9 @@ pub fn library_duplicate(uuid: &str) -> Result<String, String> {
|
||||
/// Import a `.ove` / `.otio` / `.fcpxml` project file as a new library
|
||||
/// row; returns the new row's uuid.
|
||||
pub fn library_import(path: &Path) -> Result<String, String> {
|
||||
let file_uri = oakstorage::uri::StorageUri::parse(&path.to_string_lossy())
|
||||
let file_uri = oak_storage::uri::StorageUri::parse(&path.to_string_lossy())
|
||||
.map_err(|e| e.to_string())?;
|
||||
oakstorage::writethrough::backend()
|
||||
oak_storage::writethrough::backend()
|
||||
.import_from_file(&library()?, &file_uri)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
@@ -817,9 +817,9 @@ pub fn library_export(uuid: &str, path: &Path) -> Result<(), String> {
|
||||
if uuid.is_empty() {
|
||||
return Err("invalid uuid".to_string());
|
||||
}
|
||||
let file_uri = oakstorage::uri::StorageUri::parse(&path.to_string_lossy())
|
||||
let file_uri = oak_storage::uri::StorageUri::parse(&path.to_string_lossy())
|
||||
.map_err(|e| e.to_string())?;
|
||||
oakstorage::writethrough::backend()
|
||||
oak_storage::writethrough::backend()
|
||||
.export_to_file(&library()?, uuid, &file_uri)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
@@ -831,7 +831,7 @@ pub fn library_open(uuid: &str) -> Result<ProjectRef, String> {
|
||||
if uuid.is_empty() {
|
||||
return Err("invalid uuid".to_string());
|
||||
}
|
||||
let result = oakstorage::writethrough::backend()
|
||||
let result = oak_storage::writethrough::backend()
|
||||
.load(&project_uri(uuid)?)
|
||||
.map_err(|e| e.to_string())?;
|
||||
if result.project.is_null() {
|
||||
@@ -841,7 +841,7 @@ pub fn library_open(uuid: &str) -> Result<ProjectRef, String> {
|
||||
));
|
||||
}
|
||||
let handle = result.project;
|
||||
let project = unsafe { oakstorage::nodeutil::project_arc(&handle) }
|
||||
let project = unsafe { oak_storage::nodeutil::project_arc(&handle) }
|
||||
.map_err(|e| e.to_string())?;
|
||||
lock(&project).set_modified(false);
|
||||
Ok(project)
|
||||
@@ -858,33 +858,33 @@ pub fn library_open(uuid: &str) -> Result<ProjectRef, String> {
|
||||
/// facade: an unconfigured library leaves the project unbound but the
|
||||
/// handle is still usable for queries.
|
||||
pub fn storage_bind(project: &ProjectRef) -> CHandle {
|
||||
let handle = oakstorage::nodeutil::make_project_owned(project.clone());
|
||||
oakstorage::writethrough::bind_project(handle);
|
||||
let handle = oak_storage::nodeutil::make_project_owned(project.clone());
|
||||
oak_storage::writethrough::bind_project(handle);
|
||||
handle
|
||||
}
|
||||
|
||||
/// Whether the project behind `handle` is bound to the write-through
|
||||
/// session (the status bar's write state).
|
||||
pub fn storage_bound(handle: &CHandle) -> bool {
|
||||
oakstorage::writethrough::is_bound(*handle)
|
||||
oak_storage::writethrough::is_bound(*handle)
|
||||
}
|
||||
|
||||
/// The last write-through / snapshot error of the project behind
|
||||
/// `handle`, if any.
|
||||
pub fn storage_last_error(handle: &CHandle) -> Option<String> {
|
||||
oakstorage::writethrough::last_error(*handle)
|
||||
oak_storage::writethrough::last_error(*handle)
|
||||
}
|
||||
|
||||
/// Flush the project's pending writes, drop its binding and release the
|
||||
/// query handle (the engine's project-close path).
|
||||
pub fn storage_unbind(handle: CHandle) {
|
||||
oakstorage::writethrough::unbind_project(handle);
|
||||
oakstorage::nodeutil::release_project(handle);
|
||||
oak_storage::writethrough::unbind_project(handle);
|
||||
oak_storage::nodeutil::release_project(handle);
|
||||
}
|
||||
|
||||
/// Flush every bound project and stop the snapshot thread (app exit).
|
||||
pub fn storage_flush() {
|
||||
oakstorage::writethrough::flush_all();
|
||||
oak_storage::writethrough::flush_all();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -892,25 +892,25 @@ pub fn storage_flush() {
|
||||
//
|
||||
// The timeline edits the facade's `oakengine_sequence_*` / `oakengine_clip_*`
|
||||
// exports performed, rebuilt over the oaktimeline command structs and the
|
||||
// oakundo global stack's safe [`oakundo::global::push`]. Every entry point
|
||||
// oakundo global stack's safe [`oak_undo::global::push`]. Every entry point
|
||||
// pushes exactly one undo row (composite edits assemble a multi command).
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Push one command onto the global undo stack (redo then record).
|
||||
pub fn push_command(cmd: oakundo::undocommand::UndoCommand, name: &str) -> Result<(), String> {
|
||||
oakundo::global::push(cmd, name).map_err(|e| e.to_string())
|
||||
pub fn push_command(cmd: oak_undo::undocommand::UndoCommand, name: &str) -> Result<(), String> {
|
||||
oak_undo::global::push(cmd, name).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// Assemble `children` into ONE multi command and push it (an empty set
|
||||
/// is a no-op, mirroring the stack's empty-multi discard).
|
||||
pub fn push_multi_command(
|
||||
children: Vec<oakundo::undocommand::UndoCommand>,
|
||||
children: Vec<oak_undo::undocommand::UndoCommand>,
|
||||
name: &str,
|
||||
) -> Result<(), String> {
|
||||
if children.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let mut multi = oakundo::undocommand::UndoCommand::multi();
|
||||
let mut multi = oak_undo::undocommand::UndoCommand::multi();
|
||||
for child in children {
|
||||
multi.multi_add_child(child);
|
||||
}
|
||||
@@ -918,12 +918,12 @@ pub fn push_multi_command(
|
||||
}
|
||||
|
||||
/// Push one command (private alias).
|
||||
fn push(cmd: oakundo::undocommand::UndoCommand, name: &str) -> Result<(), String> {
|
||||
fn push(cmd: oak_undo::undocommand::UndoCommand, name: &str) -> Result<(), String> {
|
||||
push_command(cmd, name)
|
||||
}
|
||||
|
||||
/// Assemble and push a multi command (private alias).
|
||||
fn push_multi(children: Vec<oakundo::undocommand::UndoCommand>, name: &str) -> Result<(), String> {
|
||||
fn push_multi(children: Vec<oak_undo::undocommand::UndoCommand>, name: &str) -> Result<(), String> {
|
||||
push_multi_command(children, name)
|
||||
}
|
||||
|
||||
@@ -935,7 +935,7 @@ pub fn connect_command(
|
||||
from: NodeId,
|
||||
to: NodeId,
|
||||
input_id: &str,
|
||||
) -> Result<oakundo::undocommand::UndoCommand, String> {
|
||||
) -> Result<oak_undo::undocommand::UndoCommand, String> {
|
||||
{
|
||||
let g = lock(p);
|
||||
if !g.graph.is_valid(from) || !g.graph.is_valid(to) {
|
||||
@@ -955,7 +955,7 @@ pub fn connect_command(
|
||||
}
|
||||
let (p1, p2) = (p.clone(), p.clone());
|
||||
let (id1, id2) = (input_id.to_string(), input_id.to_string());
|
||||
Ok(oakundo::undocommand::UndoCommand::from_closures(
|
||||
Ok(oak_undo::undocommand::UndoCommand::from_closures(
|
||||
move || {
|
||||
let mut g = lock(&p1);
|
||||
let _ = g.graph.connect(from, to, &id1, -1);
|
||||
@@ -977,7 +977,7 @@ pub fn disconnect_command(
|
||||
p: &ProjectRef,
|
||||
to: NodeId,
|
||||
input_id: &str,
|
||||
) -> Result<oakundo::undocommand::UndoCommand, String> {
|
||||
) -> Result<oak_undo::undocommand::UndoCommand, String> {
|
||||
let source = {
|
||||
let g = lock(p);
|
||||
let has_input = g
|
||||
@@ -992,7 +992,7 @@ pub fn disconnect_command(
|
||||
};
|
||||
let (p1, p2) = (p.clone(), p.clone());
|
||||
let (id1, id2) = (input_id.to_string(), input_id.to_string());
|
||||
Ok(oakundo::undocommand::UndoCommand::from_closures(
|
||||
Ok(oak_undo::undocommand::UndoCommand::from_closures(
|
||||
move || {
|
||||
let mut g = lock(&p1);
|
||||
g.graph.disconnect_input(to, &id1, -1);
|
||||
@@ -1016,7 +1016,7 @@ pub fn set_context_position_command(
|
||||
context: NodeId,
|
||||
x: f64,
|
||||
y: f64,
|
||||
) -> Result<oakundo::undocommand::UndoCommand, String> {
|
||||
) -> Result<oak_undo::undocommand::UndoCommand, String> {
|
||||
let old = {
|
||||
let g = lock(p);
|
||||
if !g.graph.is_valid(node) || !g.graph.is_valid(context) {
|
||||
@@ -1033,7 +1033,7 @@ pub fn set_context_position_command(
|
||||
})
|
||||
};
|
||||
let (p1, p2) = (p.clone(), p.clone());
|
||||
Ok(oakundo::undocommand::UndoCommand::from_closures(
|
||||
Ok(oak_undo::undocommand::UndoCommand::from_closures(
|
||||
move || {
|
||||
let mut g = lock(&p1);
|
||||
if let Some(e) = g.graph.get_mut(node) {
|
||||
@@ -1068,7 +1068,7 @@ pub fn add_track(p: &ProjectRef, seq: NodeId, kind: TrackType) -> Result<usize,
|
||||
.unwrap_or_default()
|
||||
};
|
||||
push(
|
||||
oaktimeline::undogeneral::TimelineAddTrackCommand::new(node_ref(p, list)).to_command(),
|
||||
oak_timeline::undogeneral::TimelineAddTrackCommand::new(node_ref(p, list)).to_command(),
|
||||
"Add Track",
|
||||
)?;
|
||||
let g = lock(p);
|
||||
@@ -1086,7 +1086,7 @@ pub fn add_track(p: &ProjectRef, seq: NodeId, kind: TrackType) -> Result<usize,
|
||||
/// removal, so no live compensation is needed).
|
||||
pub fn remove_track(p: &ProjectRef, track: NodeId) -> Result<(), String> {
|
||||
push(
|
||||
oaktimeline::undogeneral::TimelineRemoveTrackCommand::new(node_ref(p, track)).to_command(),
|
||||
oak_timeline::undogeneral::TimelineRemoveTrackCommand::new(node_ref(p, track)).to_command(),
|
||||
"Remove Track",
|
||||
)
|
||||
}
|
||||
@@ -1134,7 +1134,7 @@ fn set_track_flag(
|
||||
}
|
||||
let (p1, p2) = (p.clone(), p.clone());
|
||||
push(
|
||||
oakundo::undocommand::UndoCommand::from_closures(
|
||||
oak_undo::undocommand::UndoCommand::from_closures(
|
||||
move || {
|
||||
let mut g = lock(&p1);
|
||||
if let Some(t) = g
|
||||
@@ -1265,7 +1265,7 @@ pub fn place_footage_clip(
|
||||
// `oaknode_clip_set_media_in` + `oaknode_block_set_length_and_media_in`).
|
||||
let clip = {
|
||||
let mut g = lock(p);
|
||||
let (core, behavior) = oaknode::block::clip_create();
|
||||
let (core, behavior) = oak_node::block::clip_create();
|
||||
let id = g.graph.add_node(core, behavior);
|
||||
if let Some(c) = g
|
||||
.graph
|
||||
@@ -1279,14 +1279,14 @@ pub fn place_footage_clip(
|
||||
id
|
||||
};
|
||||
|
||||
let place = oaktimeline::undopointer::TrackPlaceBlockCommand::new(
|
||||
let place = oak_timeline::undopointer::TrackPlaceBlockCommand::new(
|
||||
node_ref(p, list),
|
||||
track_index as i32,
|
||||
node_ref(p, clip),
|
||||
in_r,
|
||||
)
|
||||
.to_command();
|
||||
let edge = connect_command(p, footage, clip, oaknode::block::clip_input::TEXTURE_INPUT)?;
|
||||
let edge = connect_command(p, footage, clip, oak_node::block::clip_input::TEXTURE_INPUT)?;
|
||||
push_multi(vec![place, edge], "Add Clip")?;
|
||||
Ok(clip)
|
||||
}
|
||||
@@ -1328,7 +1328,7 @@ pub fn place_footage_clips_linked(
|
||||
let mut clips = Vec::with_capacity(placements.len());
|
||||
for _ in placements {
|
||||
let mut g = lock(p);
|
||||
let (core, behavior) = oaknode::block::clip_create();
|
||||
let (core, behavior) = oak_node::block::clip_create();
|
||||
let id = g.graph.add_node(core, behavior);
|
||||
if let Some(c) = g
|
||||
.graph
|
||||
@@ -1350,7 +1350,7 @@ pub fn place_footage_clips_linked(
|
||||
.ok_or_else(|| "sequence has no track list for this type".to_string())?
|
||||
};
|
||||
commands.push(
|
||||
oaktimeline::undopointer::TrackPlaceBlockCommand::new(
|
||||
oak_timeline::undopointer::TrackPlaceBlockCommand::new(
|
||||
node_ref(p, list),
|
||||
track_index as i32,
|
||||
node_ref(p, clip),
|
||||
@@ -1358,7 +1358,7 @@ pub fn place_footage_clips_linked(
|
||||
)
|
||||
.to_command(),
|
||||
);
|
||||
commands.push(connect_command(p, footage, clip, oaknode::block::clip_input::TEXTURE_INPUT)?);
|
||||
commands.push(connect_command(p, footage, clip, oak_node::block::clip_input::TEXTURE_INPUT)?);
|
||||
}
|
||||
// Link the group both ways (closure commands capture the current
|
||||
// links for undo).
|
||||
@@ -1372,7 +1372,7 @@ pub fn place_footage_clips_linked(
|
||||
g.graph.links_of(a)
|
||||
};
|
||||
let (p1, p2) = (p.clone(), p.clone());
|
||||
commands.push(oakundo::undocommand::UndoCommand::from_closures(
|
||||
commands.push(oak_undo::undocommand::UndoCommand::from_closures(
|
||||
move || {
|
||||
let mut g = lock(&p1);
|
||||
if let Some(entry) = g.graph.get_mut(a) {
|
||||
@@ -1416,7 +1416,7 @@ pub fn split_clip(p: &ProjectRef, clip: NodeId, time_ts: i64) -> Result<(), Stri
|
||||
return Err(format!("split time {time_ts} is not strictly inside the clip"));
|
||||
}
|
||||
push(
|
||||
oaktimeline::undosplit::BlockSplitCommand::new(node_ref(p, clip), point).to_command(),
|
||||
oak_timeline::undosplit::BlockSplitCommand::new(node_ref(p, clip), point).to_command(),
|
||||
"Split Clip",
|
||||
)
|
||||
}
|
||||
@@ -1427,7 +1427,7 @@ pub fn split_clip(p: &ProjectRef, clip: NodeId, time_ts: i64) -> Result<(), Stri
|
||||
/// module's own `BlockTrimCommand` applies its length setters with
|
||||
/// inverted semantics, so the closures carry the correct mapping).
|
||||
pub fn trim_clip(p: &ProjectRef, clip: NodeId, new_in_ts: i64, new_out_ts: i64) -> Result<(), String> {
|
||||
use oaknode::block::BlockCore;
|
||||
use oak_node::block::BlockCore;
|
||||
if new_in_ts < 0 || new_out_ts <= new_in_ts {
|
||||
return Err("invalid trim range (need 0 <= new_in < new_out)".to_string());
|
||||
}
|
||||
@@ -1459,9 +1459,9 @@ pub fn trim_clip(p: &ProjectRef, clip: NodeId, new_in_ts: i64, new_out_ts: i64)
|
||||
out_anchored: bool,
|
||||
old: Rational,
|
||||
new: Rational,
|
||||
) -> oakundo::undocommand::UndoCommand {
|
||||
) -> oak_undo::undocommand::UndoCommand {
|
||||
let (p1, p2) = (p.clone(), p.clone());
|
||||
oakundo::undocommand::UndoCommand::from_closures(
|
||||
oak_undo::undocommand::UndoCommand::from_closures(
|
||||
move || {
|
||||
let mut g = lock(&p1);
|
||||
if let Some(c) = g
|
||||
@@ -1517,7 +1517,7 @@ fn move_clip_command(
|
||||
p: &ProjectRef,
|
||||
clip: NodeId,
|
||||
new_in_ts: i64,
|
||||
) -> Result<oakundo::undocommand::UndoCommand, String> {
|
||||
) -> Result<oak_undo::undocommand::UndoCommand, String> {
|
||||
let (tb, list, track_index) = {
|
||||
let g = lock(p);
|
||||
let track = clip_track(&g.graph, clip).ok_or_else(|| "the clip is not on a track".to_string())?;
|
||||
@@ -1531,7 +1531,7 @@ fn move_clip_command(
|
||||
.ok_or_else(|| "the sequence has no valid frame rate".to_string())?;
|
||||
(tb, list, track_index)
|
||||
};
|
||||
Ok(oaktimeline::undopointer::TrackMoveBlockCommand::new(
|
||||
Ok(oak_timeline::undopointer::TrackMoveBlockCommand::new(
|
||||
node_ref(p, list),
|
||||
track_index,
|
||||
node_ref(p, clip),
|
||||
@@ -1557,7 +1557,7 @@ fn move_clip_to_track_commands(
|
||||
clip: NodeId,
|
||||
dest_track: NodeId,
|
||||
new_in_ts: i64,
|
||||
) -> Result<Vec<oakundo::undocommand::UndoCommand>, String> {
|
||||
) -> Result<Vec<oak_undo::undocommand::UndoCommand>, String> {
|
||||
let (tb, list, dest_index, source_track) = {
|
||||
let g = lock(p);
|
||||
let source_track =
|
||||
@@ -1576,7 +1576,7 @@ fn move_clip_to_track_commands(
|
||||
};
|
||||
let in_r = ts_to_rational(new_in_ts, tb);
|
||||
|
||||
let gap = oaktimeline::undogeneral::TrackReplaceBlockWithGapCommand::new(
|
||||
let gap = oak_timeline::undogeneral::TrackReplaceBlockWithGapCommand::new(
|
||||
node_ref(p, source_track),
|
||||
node_ref(p, clip),
|
||||
true,
|
||||
@@ -1592,7 +1592,7 @@ fn move_clip_to_track_commands(
|
||||
.ok_or_else(|| "the node is not a clip".to_string())?
|
||||
};
|
||||
let (p1, p2) = (p.clone(), p.clone());
|
||||
let rehome = oakundo::undocommand::UndoCommand::from_closures(
|
||||
let rehome = oak_undo::undocommand::UndoCommand::from_closures(
|
||||
move || {
|
||||
let mut g = lock(&p1);
|
||||
if let Some(c) = g
|
||||
@@ -1616,7 +1616,7 @@ fn move_clip_to_track_commands(
|
||||
}
|
||||
},
|
||||
);
|
||||
let place = oaktimeline::undopointer::TrackPlaceBlockCommand::new(
|
||||
let place = oak_timeline::undopointer::TrackPlaceBlockCommand::new(
|
||||
node_ref(p, list),
|
||||
dest_index,
|
||||
node_ref(p, clip),
|
||||
@@ -1720,13 +1720,13 @@ pub fn delete_clip(p: &ProjectRef, clip: NodeId) -> Result<(), String> {
|
||||
let g = lock(p);
|
||||
clip_track(&g.graph, clip).ok_or_else(|| "the clip is not on a track".to_string())?
|
||||
};
|
||||
let gap = oaktimeline::undogeneral::TrackReplaceBlockWithGapCommand::new(
|
||||
let gap = oak_timeline::undogeneral::TrackReplaceBlockWithGapCommand::new(
|
||||
node_ref(p, source_track),
|
||||
node_ref(p, clip),
|
||||
true,
|
||||
)
|
||||
.to_command();
|
||||
let remove = oaktask::nodeops::remove_node_command(p.clone(), clip);
|
||||
let remove = oak_task::nodeops::remove_node_command(p.clone(), clip);
|
||||
push_multi(vec![gap, remove], "Delete Clips")
|
||||
}
|
||||
|
||||
@@ -1743,7 +1743,7 @@ pub fn ripple_delete_clip(p: &ProjectRef, clip: NodeId) -> Result<(), String> {
|
||||
(track, TimeRange::new(in_r, out_r))
|
||||
};
|
||||
push(
|
||||
oaktimeline::undoripple::TrackRippleRemoveAreaCommand::new(node_ref(p, track), range)
|
||||
oak_timeline::undoripple::TrackRippleRemoveAreaCommand::new(node_ref(p, track), range)
|
||||
.to_command(),
|
||||
"Ripple Delete Clip",
|
||||
)
|
||||
@@ -1757,7 +1757,7 @@ pub fn marker_add(markers: &CHandle, time: Rational, name: &str, color: i32) ->
|
||||
return Err("a marker already exists at that time".to_string());
|
||||
}
|
||||
push(
|
||||
oaktimeline::marker::MarkerAddCommand::new(
|
||||
oak_timeline::marker::MarkerAddCommand::new(
|
||||
*markers,
|
||||
TimeRange::new(time, time),
|
||||
name,
|
||||
@@ -1775,7 +1775,7 @@ pub fn marker_remove(markers: &CHandle, time: Rational) -> Result<(), String> {
|
||||
return Err("no marker at that time".to_string());
|
||||
};
|
||||
push(
|
||||
oaktimeline::marker::MarkerRemoveCommand::new(*markers, index).to_command(),
|
||||
oak_timeline::marker::MarkerRemoveCommand::new(*markers, index).to_command(),
|
||||
"Remove Marker",
|
||||
)
|
||||
}
|
||||
@@ -1793,8 +1793,8 @@ pub fn workarea_set_undoable(
|
||||
) -> Result<(), String> {
|
||||
push_multi(
|
||||
vec![
|
||||
oaktimeline::workarea::WorkareaSetEnabledCommand::new(*wa, enabled).to_command(),
|
||||
oaktimeline::workarea::WorkareaSetRangeCommand::new_with_old(*wa, range, old_range)
|
||||
oak_timeline::workarea::WorkareaSetEnabledCommand::new(*wa, enabled).to_command(),
|
||||
oak_timeline::workarea::WorkareaSetRangeCommand::new_with_old(*wa, range, old_range)
|
||||
.to_command(),
|
||||
],
|
||||
"Set Workarea",
|
||||
@@ -1807,7 +1807,7 @@ pub fn workarea_set_undoable(
|
||||
/// removed.
|
||||
pub fn remove_node(p: &ProjectRef, node: NodeId) -> Result<(), String> {
|
||||
push(
|
||||
oaktimeline::undocommon::create_remove_command(&node_ref(p, node)),
|
||||
oak_timeline::undocommon::create_remove_command(&node_ref(p, node)),
|
||||
"Remove Node",
|
||||
)
|
||||
}
|
||||
@@ -1954,7 +1954,7 @@ pub fn paste_clips(
|
||||
|
||||
let clip = {
|
||||
let mut g = lock(p);
|
||||
let (core, behavior) = oaknode::block::clip_create();
|
||||
let (core, behavior) = oak_node::block::clip_create();
|
||||
let id = g.graph.add_node(core, behavior);
|
||||
if let Some(c) = g
|
||||
.graph
|
||||
@@ -1970,7 +1970,7 @@ pub fn paste_clips(
|
||||
};
|
||||
new_ids.push(clip);
|
||||
commands.push(
|
||||
oaktimeline::undopointer::TrackPlaceBlockCommand::new(
|
||||
oak_timeline::undopointer::TrackPlaceBlockCommand::new(
|
||||
node_ref(p, list),
|
||||
track_index as i32,
|
||||
node_ref(p, clip),
|
||||
@@ -1982,7 +1982,7 @@ pub fn paste_clips(
|
||||
p,
|
||||
item.footage,
|
||||
clip,
|
||||
oaknode::block::clip_input::TEXTURE_INPUT,
|
||||
oak_node::block::clip_input::TEXTURE_INPUT,
|
||||
)?);
|
||||
}
|
||||
// Link the pasted group both ways (the C++ pastes linked selections as
|
||||
@@ -1991,7 +1991,7 @@ pub fn paste_clips(
|
||||
let first = new_ids[0];
|
||||
for &other in &new_ids[1..] {
|
||||
let (p1, p2) = (p.clone(), p.clone());
|
||||
commands.push(oakundo::undocommand::UndoCommand::from_closures(
|
||||
commands.push(oak_undo::undocommand::UndoCommand::from_closures(
|
||||
move || {
|
||||
let mut g = lock(&p1);
|
||||
for (a, b) in [(first, other), (other, first)] {
|
||||
@@ -2057,7 +2057,7 @@ mod tests {
|
||||
#[test]
|
||||
fn track_flag_setters_toggle_and_undo() {
|
||||
let _g = test_lock();
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
let (project, _seq, track) = project_with_track(TrackType::Video);
|
||||
|
||||
assert_eq!(track_muted(&project, track), Some(false));
|
||||
@@ -2068,27 +2068,27 @@ mod tests {
|
||||
set_track_locked(&project, track, true).expect("lock the track");
|
||||
assert_eq!(track_locked(&project, track), Some(true));
|
||||
|
||||
oakundo::global::undo().unwrap();
|
||||
oak_undo::global::undo().unwrap();
|
||||
assert_eq!(track_locked(&project, track), Some(false));
|
||||
oakundo::global::undo().unwrap();
|
||||
oak_undo::global::undo().unwrap();
|
||||
assert_eq!(track_muted(&project, track), Some(false));
|
||||
oakundo::global::redo().unwrap();
|
||||
oak_undo::global::redo().unwrap();
|
||||
assert_eq!(track_muted(&project, track), Some(true));
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
}
|
||||
|
||||
/// Setting a flag to its current value pushes no undo row.
|
||||
#[test]
|
||||
fn track_flag_setter_noop_when_unchanged() {
|
||||
let _g = test_lock();
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
let (project, _seq, track) = project_with_track(TrackType::Audio);
|
||||
let before = oakundo::global::count().unwrap();
|
||||
let before = oak_undo::global::count().unwrap();
|
||||
|
||||
set_track_muted(&project, track, false).expect("mute already false");
|
||||
set_track_locked(&project, track, false).expect("lock already false");
|
||||
assert_eq!(oakundo::global::count().unwrap(), before);
|
||||
oakundo::global::clear().unwrap();
|
||||
assert_eq!(oak_undo::global::count().unwrap(), before);
|
||||
oak_undo::global::clear().unwrap();
|
||||
}
|
||||
|
||||
/// Undo/redo stability: undo → redo → undo → redo must converge to the
|
||||
@@ -2098,11 +2098,11 @@ mod tests {
|
||||
#[test]
|
||||
fn undo_redo_cycles_converge_to_the_same_state() {
|
||||
let _g = test_lock();
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
let project = create_project();
|
||||
let seq = create_sequence(&project, "Undo Cycles");
|
||||
let media = std::env::temp_dir().join(format!("oak_undo_cycle_{}.mp4", std::process::id()));
|
||||
oakcodec::testmedia::write_test_clip(&media, 64, 64, 10, 10).expect("generate test media");
|
||||
oak_codec::testmedia::write_test_clip(&media, 64, 64, 10, 10).expect("generate test media");
|
||||
let footage = import_footage(&project, &media).expect("import");
|
||||
place_footage_clips_linked(
|
||||
&project,
|
||||
@@ -2133,7 +2133,7 @@ mod tests {
|
||||
// Two full undo/redo cycles; the state must be identical after
|
||||
// every redo and match the pre-undo state after every undo.
|
||||
for cycle in 0..2 {
|
||||
oakundo::global::undo().expect("undo");
|
||||
oak_undo::global::undo().expect("undo");
|
||||
let g = lock(&project);
|
||||
let video_blocks: usize = track_ids(&g.graph, seq, TrackType::Video)
|
||||
.iter()
|
||||
@@ -2141,11 +2141,11 @@ mod tests {
|
||||
.sum();
|
||||
assert_eq!(video_blocks, 0, "cycle {cycle}: undo removes the clips");
|
||||
drop(g);
|
||||
oakundo::global::redo().expect("redo");
|
||||
oak_undo::global::redo().expect("redo");
|
||||
let state = snapshot(&project);
|
||||
assert_eq!(state, before, "cycle {cycle}: redo must restore the exact state");
|
||||
}
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
let _ = std::fs::remove_file(&media);
|
||||
}
|
||||
|
||||
@@ -2153,11 +2153,11 @@ mod tests {
|
||||
#[test]
|
||||
fn track_flag_setters_reject_non_tracks() {
|
||||
let _g = test_lock();
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
let (project, seq, _track) = project_with_track(TrackType::Video);
|
||||
assert!(set_track_muted(&project, seq, true).is_err());
|
||||
assert!(set_track_locked(&project, seq, true).is_err());
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2171,7 +2171,7 @@ mod undo_cycle_track_tests {
|
||||
#[test]
|
||||
fn add_track_undo_redo_cycles_converge() {
|
||||
let _g = test_lock();
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
let project = create_project();
|
||||
let seq = create_sequence(&project, "Add Track Cycles");
|
||||
let count_of = |p: &ProjectRef, kind: TrackType| {
|
||||
@@ -2188,13 +2188,13 @@ mod undo_cycle_track_tests {
|
||||
};
|
||||
|
||||
for cycle in 0..3 {
|
||||
oakundo::global::undo().expect("undo");
|
||||
oak_undo::global::undo().expect("undo");
|
||||
assert_eq!(
|
||||
count_of(&project, TrackType::Video),
|
||||
2,
|
||||
"cycle {cycle}: undo removes only the added track"
|
||||
);
|
||||
oakundo::global::redo().expect("redo");
|
||||
oak_undo::global::redo().expect("redo");
|
||||
assert_eq!(
|
||||
count_of(&project, TrackType::Video),
|
||||
3,
|
||||
@@ -2212,7 +2212,7 @@ mod undo_cycle_track_tests {
|
||||
);
|
||||
drop(g);
|
||||
}
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2250,8 +2250,8 @@ mod undo_cycle_ops_tests {
|
||||
|
||||
fn cycle_assert(p: &ProjectRef, seq: NodeId, post: &(usize, Vec<(NodeId, Vec<(NodeId, i128, i128)>)>), what: &str) {
|
||||
for cycle in 0..3 {
|
||||
oakundo::global::undo().unwrap_or_else(|e| panic!("{what}: undo failed: {e:?}"));
|
||||
oakundo::global::redo().unwrap_or_else(|e| panic!("{what}: redo failed: {e:?}"));
|
||||
oak_undo::global::undo().unwrap_or_else(|e| panic!("{what}: undo failed: {e:?}"));
|
||||
oak_undo::global::redo().unwrap_or_else(|e| panic!("{what}: redo failed: {e:?}"));
|
||||
let state = snapshot(p, seq);
|
||||
assert_eq!(&state, post, "{what}: cycle {cycle} diverged");
|
||||
}
|
||||
@@ -2281,9 +2281,9 @@ mod undo_cycle_ops_tests {
|
||||
#[test]
|
||||
fn move_trim_delete_split_cycles_converge() {
|
||||
let _g = test_lock();
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
let media = std::env::temp_dir().join(format!("oak_cycle_ops_{}.mp4", std::process::id()));
|
||||
oakcodec::testmedia::write_test_clip(&media, 64, 64, 10, 10).expect("generate");
|
||||
oak_codec::testmedia::write_test_clip(&media, 64, 64, 10, 10).expect("generate");
|
||||
|
||||
// --- move ---
|
||||
let (project, seq, _footage) = project_with_two_clips(&media);
|
||||
@@ -2297,25 +2297,25 @@ mod undo_cycle_ops_tests {
|
||||
move_clip(&project, clip, 20).expect("move");
|
||||
let post = snapshot(&project, seq);
|
||||
cycle_assert(&project, seq, &post, "move");
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
|
||||
// --- trim ---
|
||||
trim_clip(&project, clip, 22, 28).expect("trim");
|
||||
let post = snapshot(&project, seq);
|
||||
cycle_assert(&project, seq, &post, "trim");
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
|
||||
// --- split ---
|
||||
split_clip(&project, clip, 25).expect("split");
|
||||
let post = snapshot(&project, seq);
|
||||
cycle_assert(&project, seq, &post, "split");
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
|
||||
// --- delete ---
|
||||
delete_clip(&project, clip).expect("delete");
|
||||
let post = snapshot(&project, seq);
|
||||
cycle_assert(&project, seq, &post, "delete");
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
|
||||
// --- ripple delete (on a fresh project, then check) ---
|
||||
let (project, seq, _footage) = project_with_two_clips(&media);
|
||||
@@ -2329,7 +2329,7 @@ mod undo_cycle_ops_tests {
|
||||
ripple_delete_clip(&project, clip).expect("ripple delete");
|
||||
let post = snapshot(&project, seq);
|
||||
cycle_assert(&project, seq, &post, "ripple delete");
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
|
||||
let _ = std::fs::remove_file(&media);
|
||||
}
|
||||
@@ -59,7 +59,7 @@ pub fn icon_path(name: &str, cx: &App) -> PathBuf {
|
||||
"light"
|
||||
};
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("assets/icons")
|
||||
.join("../../assets/icons")
|
||||
.join(family)
|
||||
.join(format!("{name}.png"))
|
||||
}
|
||||
@@ -103,7 +103,7 @@ mod tests {
|
||||
] {
|
||||
for family in ["dark", "light"] {
|
||||
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("assets/icons")
|
||||
.join("../../assets/icons")
|
||||
.join(family)
|
||||
.join(format!("{name}.png"));
|
||||
assert!(
|
||||
@@ -61,10 +61,10 @@ use gpui_widgets::audio_meter::AudioMeterDataSource;
|
||||
use gpui_widgets::project_explorer::{ProjectDataSource, ProjectEntry};
|
||||
use gpui_widgets::viewer::PlaybackClock;
|
||||
|
||||
use oakcore_rs::Rational;
|
||||
use oaknode::block::clip_input;
|
||||
use oaknode::track::TrackType;
|
||||
use oaktimeline::util::{block_clip_create, track_append_block};
|
||||
use oak_core::Rational;
|
||||
use oak_node::block::clip_input;
|
||||
use oak_node::track::TrackType;
|
||||
use oak_timeline::util::{block_clip_create, track_append_block};
|
||||
|
||||
use super::engine::{
|
||||
AppEngine, EngineGateway, ExportEvent, ExportSession, LibraryProject, Monitor, MulticamState,
|
||||
@@ -543,7 +543,7 @@ pub struct MockEngine {
|
||||
/// sequence's video tracks are the angles. Created lazily so the demo
|
||||
/// panel shows a genuine graph behind its synthetic frames — and the
|
||||
/// switch / enable / disable commands run on the real command path
|
||||
/// (`oaktimeline::multicam` + the global undo stack).
|
||||
/// (`oak_timeline::multicam` + the global undo stack).
|
||||
multicam_graph: Mutex<Option<DemoMulticamGraph>>,
|
||||
/// The demo multicam angle-frame cache: source → (playhead, image), so
|
||||
/// a paused cell never regenerates its picture.
|
||||
@@ -2079,20 +2079,20 @@ impl AppEngine for MockEngine {
|
||||
return; // The demo starts enabled; enabling again is a no-op.
|
||||
}
|
||||
let sequence = demo.sequence.clone();
|
||||
let cmd = oaktimeline::multicam::multicam_enable(vec![clip], sequence);
|
||||
let label = oaktimeline::multicam::enable_label(1);
|
||||
let cmd = oak_timeline::multicam::multicam_enable(vec![clip], sequence);
|
||||
let label = oak_timeline::multicam::enable_label(1);
|
||||
if let Err(e) = super::graphops::push_command(cmd, &label) {
|
||||
println!("[mock] multicam enable failed: {e}");
|
||||
}
|
||||
} else {
|
||||
let cmd = oaktimeline::multicam::multicam_disable(vec![clip]);
|
||||
let label = oaktimeline::multicam::disable_label(1);
|
||||
let cmd = oak_timeline::multicam::multicam_disable(vec![clip]);
|
||||
let label = oak_timeline::multicam::disable_label(1);
|
||||
if let Err(e) = super::graphops::push_command(cmd, &label) {
|
||||
println!("[mock] multicam disable failed: {e}");
|
||||
}
|
||||
}
|
||||
// Re-resolve the multicam node after the command.
|
||||
demo.multicam = oaktimeline::multicam::clip_find_multicam(&demo.clip);
|
||||
demo.multicam = oak_timeline::multicam::clip_find_multicam(&demo.clip);
|
||||
self.multicam_frames.lock().unwrap().clear();
|
||||
cx.notify();
|
||||
}
|
||||
@@ -2111,14 +2111,14 @@ impl AppEngine for MockEngine {
|
||||
}
|
||||
let playhead_frame = self.clock_frame(Monitor::Program, cx).0;
|
||||
let playhead = Rational::new(playhead_frame.max(0), 25);
|
||||
let cmd = oaktimeline::multicam::multicam_switch(
|
||||
let cmd = oak_timeline::multicam::multicam_switch(
|
||||
demo.clip.clone(),
|
||||
source,
|
||||
split_clip,
|
||||
playhead,
|
||||
);
|
||||
if let Err(e) =
|
||||
super::graphops::push_command(cmd, oaktimeline::multicam::SWITCH_LABEL)
|
||||
super::graphops::push_command(cmd, oak_timeline::multicam::SWITCH_LABEL)
|
||||
{
|
||||
println!("[mock] multicam switch failed: {e}");
|
||||
}
|
||||
@@ -2268,17 +2268,17 @@ impl AudioMeterDataSource for MockEngine {
|
||||
/// The mock's demo multicam: a real oaknode graph whose source sequence's
|
||||
/// video tracks are the angles. The panel's frames are synthetic colored
|
||||
/// cells, but the switch / enable / disable commands run on the REAL command
|
||||
/// path (`oaktimeline::multicam` + the global undo stack), so the demo
|
||||
/// path (`oak_timeline::multicam` + the global undo stack), so the demo
|
||||
/// exercises the same machinery the real engine uses.
|
||||
struct DemoMulticamGraph {
|
||||
/// The project holding the graph.
|
||||
project: graphops::ProjectRef,
|
||||
/// The clip whose texture input the multicam feeds.
|
||||
clip: oaktimeline::util::NodeRef,
|
||||
clip: oak_timeline::util::NodeRef,
|
||||
/// The source sequence (its video tracks are the angles).
|
||||
sequence: oaktimeline::util::NodeRef,
|
||||
sequence: oak_timeline::util::NodeRef,
|
||||
/// The multicam node (present while enabled).
|
||||
multicam: Option<oaktimeline::util::NodeRef>,
|
||||
multicam: Option<oak_timeline::util::NodeRef>,
|
||||
}
|
||||
|
||||
impl DemoMulticamGraph {
|
||||
@@ -2287,9 +2287,9 @@ impl DemoMulticamGraph {
|
||||
/// tracks are built directly in the graph (no `Add Track` undo entries —
|
||||
/// the demo's initial state is not a user edit).
|
||||
fn build() -> Self {
|
||||
use oaknode::node::NodeCore;
|
||||
use oaknode::sequence::SequenceBehavior;
|
||||
use oaknode::track::{TrackBehavior, TrackListBehavior};
|
||||
use oak_node::node::NodeCore;
|
||||
use oak_node::sequence::SequenceBehavior;
|
||||
use oak_node::track::{TrackBehavior, TrackListBehavior};
|
||||
|
||||
let project = graphops::create_project();
|
||||
let sequence = graphops::create_sequence(&project, "Multicam Demo");
|
||||
@@ -2353,16 +2353,16 @@ impl DemoMulticamGraph {
|
||||
.behavior
|
||||
.as_any_mut()
|
||||
.unwrap()
|
||||
.downcast_mut::<oaknode::block::ClipBlockBehavior>()
|
||||
.downcast_mut::<oak_node::block::ClipBlockBehavior>()
|
||||
.unwrap();
|
||||
c.core.range = oakcore_rs::TimeRange::new(Rational::new(0, 1), Rational::new(200, 1));
|
||||
c.core.range = oak_core::TimeRange::new(Rational::new(0, 1), Rational::new(200, 1));
|
||||
c.core.media_in = Rational::new(0, 1);
|
||||
}
|
||||
let track0 = {
|
||||
let g = graphops::lock(&project);
|
||||
graphops::track_ids(&g.graph, sequence, TrackType::Video)[0]
|
||||
};
|
||||
let track0 = oaktimeline::util::NodeRef::new(project.clone(), track0);
|
||||
let track0 = oak_timeline::util::NodeRef::new(project.clone(), track0);
|
||||
track_append_block(&track0, &clip);
|
||||
{
|
||||
let mut g = graphops::lock(&project);
|
||||
@@ -2372,13 +2372,13 @@ impl DemoMulticamGraph {
|
||||
}
|
||||
// Enable multicam through the real command (kept out of the undo
|
||||
// stack — it is the demo's initial state, not a user edit).
|
||||
let mut enable = oaktimeline::multicam::MultiCamEnableCommand::new(
|
||||
let mut enable = oak_timeline::multicam::MultiCamEnableCommand::new(
|
||||
vec![clip.clone()],
|
||||
oaktimeline::util::NodeRef::new(project.clone(), sequence),
|
||||
oak_timeline::util::NodeRef::new(project.clone(), sequence),
|
||||
);
|
||||
enable.redo();
|
||||
let multicam = oaktimeline::multicam::clip_find_multicam(&clip);
|
||||
let sequence = oaktimeline::util::NodeRef::new(project.clone(), sequence);
|
||||
let multicam = oak_timeline::multicam::clip_find_multicam(&clip);
|
||||
let sequence = oak_timeline::util::NodeRef::new(project.clone(), sequence);
|
||||
DemoMulticamGraph {
|
||||
project,
|
||||
clip,
|
||||
@@ -15,7 +15,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! App-side multicam resolution: the bridge between the
|
||||
//! [`oaktimeline::multicam`](oaktimeline::multicam) commands and the
|
||||
//! [`oak_timeline::multicam`](oak_timeline::multicam) commands and the
|
||||
//! UI's detection / menu needs.
|
||||
//!
|
||||
//! The C++ `MulticamWidget` detects a multicam by asking the viewer, then
|
||||
@@ -32,13 +32,13 @@
|
||||
//! clip under the playhead on the video tracks), used when nothing is
|
||||
//! selected.
|
||||
|
||||
use oakcore_rs::Rational;
|
||||
use oaknode::block::clip_input;
|
||||
use oaknode::graph::Graph;
|
||||
use oaknode::id::NodeId;
|
||||
use oaknode::nodes::multicamnode::{SEQUENCE_INPUT, SEQUENCE_TYPE_INPUT};
|
||||
use oaknode::sequence::SequenceBehavior;
|
||||
use oaknode::track::TrackType;
|
||||
use oak_core::Rational;
|
||||
use oak_node::block::clip_input;
|
||||
use oak_node::graph::Graph;
|
||||
use oak_node::id::NodeId;
|
||||
use oak_node::nodes::multicamnode::{SEQUENCE_INPUT, SEQUENCE_TYPE_INPUT};
|
||||
use oak_node::sequence::SequenceBehavior;
|
||||
use oak_node::track::TrackType;
|
||||
|
||||
use super::engine::MulticamState;
|
||||
use super::graphops::{self, lock, ProjectRef};
|
||||
@@ -144,7 +144,7 @@ pub fn multicam_current_source(p: &ProjectRef, mc: NodeId) -> i32 {
|
||||
let g = lock(p);
|
||||
g.graph
|
||||
.get(mc)
|
||||
.map(|e| e.core.standard_value(oaknode::nodes::multicamnode::CURRENT_INPUT, -1).to_double() as i32)
|
||||
.map(|e| e.core.standard_value(oak_node::nodes::multicamnode::CURRENT_INPUT, -1).to_double() as i32)
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
|
||||
@@ -153,8 +153,8 @@ pub fn multicam_current_source(p: &ProjectRef, mc: NodeId) -> i32 {
|
||||
/// source. `None` when the clip has no multicam or the multicam has no
|
||||
/// connected sequence.
|
||||
pub fn multicam_state_for_clip(p: &ProjectRef, clip: NodeId) -> Option<MulticamState> {
|
||||
let clip_ref = oaktimeline::util::NodeRef::new(p.clone(), clip);
|
||||
let mc = oaktimeline::multicam::clip_find_multicam(&clip_ref)?;
|
||||
let clip_ref = oak_timeline::util::NodeRef::new(p.clone(), clip);
|
||||
let mc = oak_timeline::multicam::clip_find_multicam(&clip_ref)?;
|
||||
let sequence_id = multicam_sequence(p, mc.id)?;
|
||||
let source_count = multicam_source_count(p, mc.id);
|
||||
Some(MulticamState {
|
||||
@@ -193,29 +193,29 @@ pub fn clip_at_playhead_with_multicam(p: &ProjectRef, seq: NodeId, time: Rationa
|
||||
out
|
||||
};
|
||||
candidates.into_iter().find(|block_id| {
|
||||
let clip_ref = oaktimeline::util::NodeRef::new(p.clone(), *block_id);
|
||||
oaktimeline::multicam::clip_find_multicam(&clip_ref).is_some()
|
||||
let clip_ref = oak_timeline::util::NodeRef::new(p.clone(), *block_id);
|
||||
oak_timeline::multicam::clip_find_multicam(&clip_ref).is_some()
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use oaknode::block::ClipBlockBehavior;
|
||||
use oaknode::node::NodeCore;
|
||||
use oaknode::project::Project;
|
||||
use oaknode::sequence::SequenceBehavior;
|
||||
use oaknode::track::{TrackBehavior, TrackListBehavior};
|
||||
use oak_node::block::ClipBlockBehavior;
|
||||
use oak_node::node::NodeCore;
|
||||
use oak_node::project::Project;
|
||||
use oak_node::sequence::SequenceBehavior;
|
||||
use oak_node::track::{TrackBehavior, TrackListBehavior};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use oaktimeline::util::{
|
||||
use oak_timeline::util::{
|
||||
block_clip_create, block_in, track_append_block, NodeRef,
|
||||
};
|
||||
|
||||
/// Project fixture: a sequence owning one video track list with one
|
||||
/// video track.
|
||||
struct Fixture {
|
||||
project: Arc<Mutex<oaknode::project::Project>>,
|
||||
project: Arc<Mutex<oak_node::project::Project>>,
|
||||
seq: NodeRef,
|
||||
track: NodeRef,
|
||||
}
|
||||
@@ -284,7 +284,7 @@ mod tests {
|
||||
.unwrap()
|
||||
.downcast_mut::<ClipBlockBehavior>()
|
||||
.unwrap();
|
||||
c.core.range = oakcore_rs::TimeRange::new(Rational::new(0, 1), Rational::new(50, 1));
|
||||
c.core.range = oak_core::TimeRange::new(Rational::new(0, 1), Rational::new(50, 1));
|
||||
}
|
||||
track_append_block(&fx.track, &clip);
|
||||
clip
|
||||
@@ -328,7 +328,7 @@ mod tests {
|
||||
assert!(multicam_state_for_clip(&fx.project, clip.id).is_none());
|
||||
|
||||
// Enable multicam through the real command, then resolve.
|
||||
let mut cmd = oaktimeline::multicam::MultiCamEnableCommand::new(
|
||||
let mut cmd = oak_timeline::multicam::MultiCamEnableCommand::new(
|
||||
vec![clip.clone()],
|
||||
fx.seq.clone(),
|
||||
);
|
||||
@@ -339,7 +339,7 @@ mod tests {
|
||||
assert_eq!(state.current_source, 0);
|
||||
|
||||
// The connected sequence still resolves through the multicam.
|
||||
let mc = oaktimeline::multicam::clip_find_multicam(&clip).unwrap();
|
||||
let mc = oak_timeline::multicam::clip_find_multicam(&clip).unwrap();
|
||||
assert_eq!(multicam_sequence(&fx.project, mc.id), Some(fx.seq.id));
|
||||
}
|
||||
|
||||
@@ -402,7 +402,7 @@ mod tests {
|
||||
.unwrap()
|
||||
.downcast_mut::<ClipBlockBehavior>()
|
||||
.unwrap();
|
||||
c.core.range = oakcore_rs::TimeRange::new(Rational::new(0, 1), Rational::new(50, 1));
|
||||
c.core.range = oak_core::TimeRange::new(Rational::new(0, 1), Rational::new(50, 1));
|
||||
}
|
||||
track_append_block(&track2, &mc_clip);
|
||||
{
|
||||
@@ -411,7 +411,7 @@ mod tests {
|
||||
.connect(fx.seq.id, mc_clip.id, clip_input::TEXTURE_INPUT, -1)
|
||||
.unwrap();
|
||||
}
|
||||
let mut cmd = oaktimeline::multicam::MultiCamEnableCommand::new(
|
||||
let mut cmd = oak_timeline::multicam::MultiCamEnableCommand::new(
|
||||
vec![mc_clip.clone()],
|
||||
fx.seq.clone(),
|
||||
);
|
||||
@@ -55,7 +55,7 @@ use gpui::node_graph::{
|
||||
};
|
||||
use gpui::{hsla, point, px, Hsla, Pixels, Point, SharedString};
|
||||
|
||||
use oaknode::id::NodeId as DomainNodeId;
|
||||
use oak_node::id::NodeId as DomainNodeId;
|
||||
|
||||
use crate::oakui::graphops::{self, ProjectRef};
|
||||
|
||||
@@ -296,7 +296,7 @@ struct TypedNode {
|
||||
/// the full `footage → effects → clip` chain. The sequence node is NOT part
|
||||
/// of a clip's context — the chain is self-contained, so the filtered view
|
||||
/// shows no output card and no synthesized clip→output wires.
|
||||
fn clip_context_nodes(g: &oaknode::graph::Graph, clip: DomainNodeId) -> Vec<TypedNode> {
|
||||
fn clip_context_nodes(g: &oak_node::graph::Graph, clip: DomainNodeId) -> Vec<TypedNode> {
|
||||
let mut out: Vec<TypedNode> = Vec::new();
|
||||
// The clip block card (the chain's output end).
|
||||
out.push(TypedNode {
|
||||
@@ -318,7 +318,7 @@ fn clip_context_nodes(g: &oaknode::graph::Graph, clip: DomainNodeId) -> Vec<Type
|
||||
|
||||
/// The displayable nodes of the sequence's graph, in identity order (the
|
||||
/// sequence node itself exactly once).
|
||||
fn graph_nodes(g: &oaknode::graph::Graph, seq: DomainNodeId) -> Vec<TypedNode> {
|
||||
fn graph_nodes(g: &oak_node::graph::Graph, seq: DomainNodeId) -> Vec<TypedNode> {
|
||||
let mut out: Vec<TypedNode> = g
|
||||
.node_ids()
|
||||
.into_iter()
|
||||
@@ -348,7 +348,7 @@ fn graph_nodes(g: &oaknode::graph::Graph, seq: DomainNodeId) -> Vec<TypedNode> {
|
||||
/// The context position of `node` in the sequence's map, or the (0,0)
|
||||
/// sentinel the fallback layout replaces.
|
||||
fn context_position(
|
||||
g: &oaknode::graph::Graph,
|
||||
g: &oak_node::graph::Graph,
|
||||
seq: DomainNodeId,
|
||||
node: DomainNodeId,
|
||||
) -> Point<Pixels> {
|
||||
@@ -587,7 +587,7 @@ fn role_of(type_id: &str, is_output: bool) -> u32 {
|
||||
/// sorted display order (mirrors the fallback grid in `build_graph`).
|
||||
/// `apply_edit` uses it so the first drag release writes
|
||||
/// `(displayed base + delta)` instead of `(origin + delta)`.
|
||||
fn fallback_base(g: &oaknode::graph::Graph, seq: DomainNodeId, ident: u64) -> (f64, f64) {
|
||||
fn fallback_base(g: &oak_node::graph::Graph, seq: DomainNodeId, ident: u64) -> (f64, f64) {
|
||||
let seq_ident = seq.identity();
|
||||
let all = graph_nodes(g, seq);
|
||||
let target = all
|
||||
@@ -627,7 +627,7 @@ fn fallback_base(g: &oaknode::graph::Graph, seq: DomainNodeId, ident: u64) -> (f
|
||||
|
||||
/// Resolve a domain node id from a widget identity, validating it against
|
||||
/// the graph.
|
||||
fn find_node(g: &oaknode::graph::Graph, ident: u64) -> Result<DomainNodeId, String> {
|
||||
fn find_node(g: &oak_node::graph::Graph, ident: u64) -> Result<DomainNodeId, String> {
|
||||
let Some(id) = graphops::id_of(ident) else {
|
||||
return Err(format!("node {ident} not found"));
|
||||
};
|
||||
@@ -802,7 +802,7 @@ mod tests {
|
||||
let seq = graphops::create_sequence(&project, "Chain");
|
||||
let clip = {
|
||||
let mut g = graphops::lock(&project);
|
||||
let (core, behavior) = oaknode::block::clip_create();
|
||||
let (core, behavior) = oak_node::block::clip_create();
|
||||
g.graph.add_node(core, behavior)
|
||||
};
|
||||
// Insert the effect on the bare clip first (the chain is empty, so
|
||||
@@ -823,7 +823,7 @@ mod tests {
|
||||
.unwrap_or_default();
|
||||
let footage = {
|
||||
let mut g = graphops::lock(&project);
|
||||
let (core, behavior) = oaknode::footage::FootageBehavior::create();
|
||||
let (core, behavior) = oak_node::footage::FootageBehavior::create();
|
||||
g.graph.add_node(core, behavior)
|
||||
};
|
||||
{
|
||||
@@ -842,7 +842,7 @@ mod tests {
|
||||
#[test]
|
||||
fn clip_context_build_is_the_clip_chain_only() {
|
||||
let _g = stack_lock();
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
let (project, seq, clip, effect, footage) = project_with_chained_clip();
|
||||
|
||||
let (nodes, edges) = build_graph_for_clip(&project, seq, clip.identity());
|
||||
@@ -886,6 +886,6 @@ mod tests {
|
||||
"the full graph synthesizes the clip→output wire"
|
||||
);
|
||||
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@
|
||||
//! The app is the only place that holds both the oakplugin host and the UI
|
||||
//! services the OFX suites consult at runtime, so the wiring lives here:
|
||||
//!
|
||||
//! - [`init`] scans the standard plugin paths ([`oakplugin::host::Host`]
|
||||
//! - [`init`] scans the standard plugin paths ([`oak_plugin::host::Host`]
|
||||
//! default path set, `host.rs:440-449`), registers every discovered
|
||||
//! plugin into the node factory (the effect library and the add-effect
|
||||
//! menu consume those entries), installs the render executor and the
|
||||
@@ -51,10 +51,10 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex, OnceLock};
|
||||
|
||||
use gpui::{Keystroke, Point, RenderImage, Size};
|
||||
use oakplugin::progress::{ReporterFactory, UiProgressReporter};
|
||||
use oakplugin::suites::interact::Interact;
|
||||
use oakplugin::suites::status;
|
||||
use oakplugin::suites::timeline::{ActiveViewerProvider, ViewerTimeInfo};
|
||||
use oak_plugin::progress::{ReporterFactory, UiProgressReporter};
|
||||
use oak_plugin::suites::interact::Interact;
|
||||
use oak_plugin::suites::status;
|
||||
use oak_plugin::suites::timeline::{ActiveViewerProvider, ViewerTimeInfo};
|
||||
|
||||
/// One progress event a plugin reporter pushed to the app channel (drained
|
||||
/// by the app tick, which drives a progress dialog).
|
||||
@@ -136,7 +136,7 @@ pub fn update_viewer_time(time: f64, range_min: f64, range_max: f64) {
|
||||
pub fn update_project_extent(width: f64, height: f64) {
|
||||
let (w, h) = (width.max(1.0), height.max(1.0));
|
||||
*extent_slot().lock().unwrap_or_else(|e| e.into_inner()) = (w, h);
|
||||
oakplugin::node_factory::set_project_extent(w, h);
|
||||
oak_plugin::node_factory::set_project_extent(w, h);
|
||||
}
|
||||
|
||||
/// Requests cancellation of the running plugin render (the progress
|
||||
@@ -148,7 +148,7 @@ pub fn cancel_plugin_render() {
|
||||
// The worker-process plugin renders are cancelled through the control
|
||||
// plane (`plugin_cancel`); the in-flight frame completes (batch
|
||||
// granularity — the worker processes messages between batches).
|
||||
oakrender::procpool::request_plugin_cancel_all();
|
||||
oak_render::procpool::request_plugin_cancel_all();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -210,21 +210,21 @@ pub fn init() -> usize {
|
||||
// plus the OLIVE_OFX_PLUGIN_PATH / OLIVE_PLUGIN_PATH /
|
||||
// OFX_PLUGIN_PATH environment variables). A scan failure only
|
||||
// logs — plugins are optional.
|
||||
if let Err(e) = oakplugin::host::Host::global().cache.scan() {
|
||||
if let Err(e) = oak_plugin::host::Host::global().cache.scan() {
|
||||
eprintln!("[ofx] plugin scan failed: {e}");
|
||||
}
|
||||
// 2. Register discovered plugins into the node factory (idempotent;
|
||||
// also installs the render executor and the plugin-node duplicator).
|
||||
let registered = oakplugin::node_factory::register_plugin_nodes();
|
||||
let registered = oak_plugin::node_factory::register_plugin_nodes();
|
||||
// 3. Progress reporter factory -> the app progress channel.
|
||||
oakplugin::progress::set_reporter_factory(Some(reporter_factory()));
|
||||
oak_plugin::progress::set_reporter_factory(Some(reporter_factory()));
|
||||
// 4. Active-viewer time provider (timeline suite fallback).
|
||||
oakplugin::suites::timeline::set_active_viewer_provider(Some(viewer_provider()));
|
||||
oak_plugin::suites::timeline::set_active_viewer_provider(Some(viewer_provider()));
|
||||
// 5. Worker-forwarded plugin progress (the render workers run plugin
|
||||
// renders in their own process and stream `plugin_progress` NDJSON
|
||||
// events over the control plane; the dispatcher hands them to this
|
||||
// callback, which feeds the same channel the inline reporter uses).
|
||||
oakrender::procpool::set_plugin_progress_cb(Some(Arc::new(|label, message, fraction| {
|
||||
oak_render::procpool::set_plugin_progress_cb(Some(Arc::new(|label, message, fraction| {
|
||||
if let Some(tx) = progress_tx() {
|
||||
let _ = tx.send(PluginProgressEvent {
|
||||
label,
|
||||
@@ -236,7 +236,7 @@ pub fn init() -> usize {
|
||||
// 6. Project extent (the engine refreshes it whenever the sequence
|
||||
// changes; keep the oakplugin side in sync with the default).
|
||||
let (w, h) = *extent_slot().lock().unwrap_or_else(|e| e.into_inner());
|
||||
oakplugin::node_factory::set_project_extent(w, h);
|
||||
oak_plugin::node_factory::set_project_extent(w, h);
|
||||
registered.len()
|
||||
}
|
||||
|
||||
@@ -272,7 +272,7 @@ impl InteractViewport {
|
||||
|
||||
/// The app-side holder of the selected effect's live interact.
|
||||
///
|
||||
/// The interact is created on the *main process* `oakplugin::Instance` of
|
||||
/// The interact is created on the *main process* `oak_plugin::Instance` of
|
||||
/// the selected plugin effect node (the same registry the inspector's
|
||||
/// push-button path uses), distinct from the worker-process render
|
||||
/// instances — OFX allows a plugin to have several instances, and the
|
||||
@@ -328,7 +328,7 @@ pub fn sync_active_interact(instance: Option<u64>) {
|
||||
let Some(id) = instance else {
|
||||
return;
|
||||
};
|
||||
let Some(inst) = oakplugin::node_factory::instance_from_id(id) else {
|
||||
let Some(inst) = oak_plugin::node_factory::instance_from_id(id) else {
|
||||
// The node/instance is gone (effect deleted): nothing to attach to.
|
||||
return;
|
||||
};
|
||||
@@ -399,7 +399,7 @@ fn bgra_image_to_f32_rgba(img: &RenderImage) -> Option<(u32, u32, Vec<f32>)> {
|
||||
}
|
||||
|
||||
/// Reads an `oakplugin` F32 RGBA image into a tightly packed `Vec<f32>`.
|
||||
fn read_image_f32(img: &oakplugin::image::Image) -> Vec<f32> {
|
||||
fn read_image_f32(img: &oak_plugin::image::Image) -> Vec<f32> {
|
||||
img.pixels()
|
||||
.chunks_exact(4)
|
||||
.map(|c| f32::from_ne_bytes(c[0..4].try_into().unwrap()))
|
||||
@@ -477,27 +477,27 @@ pub fn draw_interact_composite(
|
||||
if bw != w as u32 || bh != h as u32 {
|
||||
return None;
|
||||
}
|
||||
let mut params = oakplugin::render::VideoParams::default();
|
||||
let mut params = oak_plugin::render::VideoParams::default();
|
||||
params.width = w;
|
||||
params.height = h;
|
||||
params.format = oakplugin::render::PIXEL_FORMAT_F32;
|
||||
params.format = oak_plugin::render::PIXEL_FORMAT_F32;
|
||||
|
||||
let _guard = oakplugin::gl_bridge::acquire().ok()?;
|
||||
let tex = oakplugin::gl_bridge::create_output_texture(w, h, ¶ms).ok()?;
|
||||
let fbo = match oakplugin::gl_bridge::create_fbo(tex, w, h) {
|
||||
let _guard = oak_plugin::gl_bridge::acquire().ok()?;
|
||||
let tex = oak_plugin::gl_bridge::create_output_texture(w, h, ¶ms).ok()?;
|
||||
let fbo = match oak_plugin::gl_bridge::create_fbo(tex, w, h) {
|
||||
Ok(fbo) => fbo,
|
||||
Err(_) => {
|
||||
oakplugin::gl_bridge::delete_gl_texture(tex);
|
||||
oak_plugin::gl_bridge::delete_gl_texture(tex);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
oakplugin::gl_bridge::bind_fbo(fbo);
|
||||
oakplugin::gl_bridge::set_viewport(w, h);
|
||||
oak_plugin::gl_bridge::bind_fbo(fbo);
|
||||
oak_plugin::gl_bridge::set_viewport(w, h);
|
||||
// Clear to transparent black: the plugin's strokes composite "over"
|
||||
// nothing, so the readback holds straight alpha (see
|
||||
// [`composite_overlay`]).
|
||||
oakplugin::gl_bridge::gl_clear_color(0.0, 0.0, 0.0, 0.0);
|
||||
oakplugin::gl_bridge::gl_clear();
|
||||
oak_plugin::gl_bridge::gl_clear_color(0.0, 0.0, 0.0, 0.0);
|
||||
oak_plugin::gl_bridge::gl_clear();
|
||||
|
||||
let st = interact.draw(
|
||||
(viewport.width, viewport.height),
|
||||
@@ -508,9 +508,9 @@ pub fn draw_interact_composite(
|
||||
// composited on the app side afterwards).
|
||||
None,
|
||||
);
|
||||
let overlay = oakplugin::gl_bridge::read_pixels_to_image(w, h, ¶ms);
|
||||
oakplugin::gl_bridge::delete_fbo(fbo);
|
||||
oakplugin::gl_bridge::delete_gl_texture(tex);
|
||||
let overlay = oak_plugin::gl_bridge::read_pixels_to_image(w, h, ¶ms);
|
||||
oak_plugin::gl_bridge::delete_fbo(fbo);
|
||||
oak_plugin::gl_bridge::delete_gl_texture(tex);
|
||||
let (Ok(overlay), st) = (overlay, st) else {
|
||||
return None;
|
||||
};
|
||||
@@ -554,15 +554,15 @@ pub fn viewport_pixel_to_pen(
|
||||
}
|
||||
|
||||
/// Maps a gpui keystroke to the OFX key symbol (`ofxKeySyms.h` values from
|
||||
/// `oakplugin::host::KEY_*`) and the key-string character
|
||||
/// `oak_plugin::host::KEY_*`) and the key-string character
|
||||
/// (`kOfxPropKeyString`: the UTF-8 character, empty for keys without one).
|
||||
///
|
||||
/// Covers the common keys — alphanumerics, the arrows, return, escape,
|
||||
/// backspace/delete, tab, home/end, page up/down and the function keys;
|
||||
/// anything else maps to [`oakplugin::host::KEY_UNKNOWN`] with an empty
|
||||
/// anything else maps to [`oak_plugin::host::KEY_UNKNOWN`] with an empty
|
||||
/// string.
|
||||
pub fn key_symbol(keystroke: &Keystroke) -> (i32, String) {
|
||||
use oakplugin::host as ofx_key;
|
||||
use oak_plugin::host as ofx_key;
|
||||
let key = keystroke.key.as_str();
|
||||
let named = match key {
|
||||
"space" => Some(ofx_key::KEY_SPACE),
|
||||
@@ -703,7 +703,7 @@ mod tests {
|
||||
/// single characters carry their ASCII symbol + the character string.
|
||||
#[test]
|
||||
fn key_symbol_maps_common_keys() {
|
||||
use oakplugin::host as ofx_key;
|
||||
use oak_plugin::host as ofx_key;
|
||||
let ks = |key: &str| gpui::Keystroke::parse(key).unwrap();
|
||||
// Alphanumerics: symbol = ASCII code, string = the char.
|
||||
assert_eq!(key_symbol(&ks("a")), (ofx_key::KEY_A, "a".to_string()));
|
||||
@@ -823,11 +823,11 @@ mod tests {
|
||||
println!("SKIP: minimal test plugin not built");
|
||||
return false;
|
||||
};
|
||||
if oakplugin::host::Host::global().cache.scan_path(&dir).is_err() {
|
||||
if oak_plugin::host::Host::global().cache.scan_path(&dir).is_err() {
|
||||
println!("SKIP: test plugin scan failed");
|
||||
return false;
|
||||
}
|
||||
oakplugin::node_factory::register_plugin_nodes();
|
||||
oak_plugin::node_factory::register_plugin_nodes();
|
||||
true
|
||||
}
|
||||
|
||||
@@ -863,10 +863,10 @@ mod tests {
|
||||
// (1) app-layer creation: `sync_active_interact` on the plugin
|
||||
// instance handle creates the interact (new_interact → describe →
|
||||
// create_instance).
|
||||
let inst = oakplugin::host::Host::global()
|
||||
let inst = oak_plugin::host::Host::global()
|
||||
.create_instance(INTERACT_PLUGIN_ID, None)
|
||||
.expect("interact variant instance");
|
||||
let handle = oakplugin::node_factory::register_instance(inst);
|
||||
let handle = oak_plugin::node_factory::register_instance(inst);
|
||||
sync_active_interact(Some(handle));
|
||||
|
||||
let (active_handle, interact, _viewport) =
|
||||
@@ -887,11 +887,11 @@ mod tests {
|
||||
|
||||
// (3) keys and idle.
|
||||
assert_eq!(
|
||||
interact.key_down(oakplugin::host::KEY_A, "a", 5.0),
|
||||
interact.key_down(oak_plugin::host::KEY_A, "a", 5.0),
|
||||
status::OK
|
||||
);
|
||||
assert_eq!(
|
||||
interact.key_up(oakplugin::host::KEY_A, "a", 5.0),
|
||||
interact.key_up(oak_plugin::host::KEY_A, "a", 5.0),
|
||||
status::OK
|
||||
);
|
||||
assert_eq!(interact.idle(), status::OK);
|
||||
@@ -959,7 +959,7 @@ mod tests {
|
||||
"idle not recorded: {lines:?}"
|
||||
);
|
||||
|
||||
oakplugin::host::Host::global().shutdown();
|
||||
oak_plugin::host::Host::global().shutdown();
|
||||
}
|
||||
|
||||
/// End-to-end overlay: the plugin's draw action really renders into the
|
||||
@@ -979,10 +979,10 @@ mod tests {
|
||||
let _ = std::fs::remove_file(&marker);
|
||||
unsafe { std::env::set_var(MARKER_ENV, &marker) };
|
||||
|
||||
let inst = oakplugin::host::Host::global()
|
||||
let inst = oak_plugin::host::Host::global()
|
||||
.create_instance(INTERACT_PLUGIN_ID, None)
|
||||
.expect("interact variant instance");
|
||||
let handle = oakplugin::node_factory::register_instance(inst);
|
||||
let handle = oak_plugin::node_factory::register_instance(inst);
|
||||
sync_active_interact(Some(handle));
|
||||
let (_, interact, _) = active_interact().expect("active interact created");
|
||||
|
||||
@@ -1035,6 +1035,6 @@ mod tests {
|
||||
);
|
||||
|
||||
sync_active_interact(None);
|
||||
oakplugin::host::Host::global().shutdown();
|
||||
oak_plugin::host::Host::global().shutdown();
|
||||
}
|
||||
}
|
||||
@@ -25,13 +25,13 @@
|
||||
|
||||
use gpui_widgets::project_explorer::ProjectEntry;
|
||||
|
||||
use oaknode::folder::FolderBehavior;
|
||||
use oaknode::id::NodeId;
|
||||
use oak_node::folder::FolderBehavior;
|
||||
use oak_node::id::NodeId;
|
||||
|
||||
use crate::oakui::graphops::{self, ProjectRef};
|
||||
|
||||
/// One child entry of a folder with its identity.
|
||||
fn child_entry(g: &oaknode::graph::Graph, child: NodeId) -> Option<ProjectEntry> {
|
||||
fn child_entry(g: &oak_node::graph::Graph, child: NodeId) -> Option<ProjectEntry> {
|
||||
let entry = g.get(child)?;
|
||||
let mut name = entry.core.label.clone();
|
||||
if name.is_empty() {
|
||||
@@ -45,7 +45,7 @@ fn child_entry(g: &oaknode::graph::Graph, child: NodeId) -> Option<ProjectEntry>
|
||||
}
|
||||
|
||||
/// The children of a folder node.
|
||||
fn folder_children(g: &oaknode::graph::Graph, folder: NodeId) -> Vec<ProjectEntry> {
|
||||
fn folder_children(g: &oak_node::graph::Graph, folder: NodeId) -> Vec<ProjectEntry> {
|
||||
let Some(f) = g
|
||||
.get(folder)
|
||||
.and_then(|e| e.behavior.as_any())
|
||||
@@ -19,10 +19,10 @@
|
||||
//! [`EngineGateway`](super::engine::EngineGateway) /
|
||||
//! [`AppEngine`](super::engine::AppEngine) seam the mock implements.
|
||||
//!
|
||||
//! The engine owns the domain project (`Arc<Mutex<oaknode::project::Project>>`)
|
||||
//! The engine owns the domain project (`Arc<Mutex<oak_node::project::Project>>`)
|
||||
//! and the current sequence's `NodeId`; every read is a direct graph walk
|
||||
//! and every edit is an oaktimeline/oakundo command pushed onto the
|
||||
//! process-wide undo stack ([`oakundo::global`]). The assembly-layer
|
||||
//! process-wide undo stack ([`oak_undo::global`]). The assembly-layer
|
||||
//! helpers live in [`super::graphops`] (project/timeline/storage),
|
||||
//! [`super::effectchain`] (effect chains) and [`super::renderops`]
|
||||
//! (montage resolution, ticket rendering, the export driver); the
|
||||
@@ -48,7 +48,7 @@
|
||||
//! audio device selection additionally applies live through oakaudio's
|
||||
//! manager.
|
||||
//! * **Storage** — the write-through library binds every opened project
|
||||
//! through [`oakstorage::writethrough`]; the manager window's library
|
||||
//! through [`oak_storage::writethrough`]; the manager window's library
|
||||
//! operations call the oakstorage database backend directly.
|
||||
//!
|
||||
//! # Threading note
|
||||
@@ -78,12 +78,12 @@ use gpui_widgets::audio_meter::AudioMeterDataSource;
|
||||
use gpui_widgets::project_explorer::{ProjectDataSource, ProjectEntry};
|
||||
use gpui_widgets::viewer::PlaybackClock;
|
||||
|
||||
use oaknode::id::NodeId;
|
||||
use oaknode::track::TrackType;
|
||||
use oakrender::manager::RenderManager;
|
||||
use oakrender::procpool::{bgra8_to_rgba8, ShmFrameRef};
|
||||
use oaktimeline::handle::CHandle;
|
||||
use oaktimeline::util::NodeRef;
|
||||
use oak_node::id::NodeId;
|
||||
use oak_node::track::TrackType;
|
||||
use oak_render::manager::RenderManager;
|
||||
use oak_render::procpool::{bgra8_to_rgba8, ShmFrameRef};
|
||||
use oak_timeline::handle::CHandle;
|
||||
use oak_timeline::util::NodeRef;
|
||||
|
||||
use super::engine::{
|
||||
AppEngine, EngineGateway, ExportSession, LibraryProject, Monitor, MulticamState, Project,
|
||||
@@ -1114,12 +1114,12 @@ impl RealEngine {
|
||||
// Packed F32; layout: 1ch → mono mask, else stereo.
|
||||
let layout: u64 = if buf.channel_count == 1 { 0x4 } else { 0x3 };
|
||||
let bytes: Vec<u8> = buf.data.iter().flat_map(|v| v.to_ne_bytes()).collect();
|
||||
if let Some(mut manager) = oakaudio::manager::instance() {
|
||||
if let Some(mut manager) = oak_audio::manager::instance() {
|
||||
let _ = manager.push_to_output(
|
||||
oakaudio::params::AudioParams {
|
||||
oak_audio::params::AudioParams {
|
||||
sample_rate: buf.sample_rate,
|
||||
channel_layout: layout,
|
||||
format: oakcore_rs::SampleFormat::F32,
|
||||
format: oak_core::SampleFormat::F32,
|
||||
},
|
||||
&bytes,
|
||||
&mut [],
|
||||
@@ -1724,12 +1724,12 @@ impl RealEngine {
|
||||
let distance = frame.saturating_sub(playhead).abs();
|
||||
let version = self.preview_generation;
|
||||
let preview_windows = self.preview_windows.clone();
|
||||
let done: oakrender::ticket::Completion = Box::new(move |result| {
|
||||
let done: oak_render::ticket::Completion = Box::new(move |result| {
|
||||
let mut windows =
|
||||
preview_windows.lock().unwrap_or_else(|e| e.into_inner());
|
||||
let window = windows.entry(monitor).or_default();
|
||||
match result {
|
||||
Ok(oakrender::ticket::TicketPayload::ShmFrame(slot)) => {
|
||||
Ok(oak_render::ticket::TicketPayload::ShmFrame(slot)) => {
|
||||
// The rendered frame is cached in its shm slot until
|
||||
// the playhead reaches it (cpu_frame) or it falls out
|
||||
// of the window.
|
||||
@@ -1983,7 +1983,7 @@ impl RealEngine {
|
||||
fn proxy_cache_path() -> String {
|
||||
let configured = config_get_string(CONFIG_KEY_DISK_CACHE_PATH);
|
||||
if configured.trim().is_empty() {
|
||||
oakcommon::filefunctions::default_disk_cache_path()
|
||||
oak_common::filefunctions::default_disk_cache_path()
|
||||
} else {
|
||||
configured
|
||||
}
|
||||
@@ -2032,7 +2032,7 @@ impl RealEngine {
|
||||
.graph
|
||||
.get_mut(*footage)
|
||||
.and_then(|e| e.behavior.as_any_mut())
|
||||
.and_then(|a| a.downcast_mut::<oaknode::footage::FootageBehavior>())
|
||||
.and_then(|a| a.downcast_mut::<oak_node::footage::FootageBehavior>())
|
||||
{
|
||||
if *ok {
|
||||
// The transcode wrote the final file: mark the
|
||||
@@ -2062,7 +2062,7 @@ impl RealEngine {
|
||||
/// recorded failure (state 3) preserved while no file is on disk.
|
||||
fn proxy_state_of(
|
||||
&self,
|
||||
f: &oaknode::footage::FootageBehavior,
|
||||
f: &oak_node::footage::FootageBehavior,
|
||||
node: NodeId,
|
||||
) -> super::engine::ProxyMediaState {
|
||||
use super::engine::ProxyMediaState;
|
||||
@@ -2072,9 +2072,9 @@ impl RealEngine {
|
||||
if f.proxy.is_empty() {
|
||||
return ProxyMediaState::Missing;
|
||||
}
|
||||
match oakcodec::proxymanager::ProxyManager::get_proxy_state(&f.proxy) {
|
||||
oakcodec::proxymanager::ProxyState::Ready => ProxyMediaState::Ready,
|
||||
oakcodec::proxymanager::ProxyState::Generating => ProxyMediaState::Generating,
|
||||
match oak_codec::proxymanager::ProxyManager::get_proxy_state(&f.proxy) {
|
||||
oak_codec::proxymanager::ProxyState::Ready => ProxyMediaState::Ready,
|
||||
oak_codec::proxymanager::ProxyState::Generating => ProxyMediaState::Generating,
|
||||
_ => {
|
||||
if f.proxy_state == 3 {
|
||||
ProxyMediaState::Failed
|
||||
@@ -2092,7 +2092,7 @@ impl RealEngine {
|
||||
/// every clip is re-placed so its source head lines up with the
|
||||
/// reference's at the anchor (one multi-undo).
|
||||
fn sync_clips_by_source_time_internal(&mut self, clips: &[ClipId]) {
|
||||
use oakaudio::synchronizer::{place_by_source_time, SourceClip};
|
||||
use oak_audio::synchronizer::{place_by_source_time, SourceClip};
|
||||
|
||||
let Some(project) = self.project.clone() else {
|
||||
return;
|
||||
@@ -2104,8 +2104,8 @@ impl RealEngine {
|
||||
list: NodeId,
|
||||
track_index: i32,
|
||||
source: SourceClip,
|
||||
source_head: oakcore_rs::Rational,
|
||||
block_in: oakcore_rs::Rational,
|
||||
source_head: oak_core::Rational,
|
||||
block_in: oak_core::Rational,
|
||||
}
|
||||
|
||||
let mut targets: Vec<SourceTarget> = Vec::new();
|
||||
@@ -2168,7 +2168,7 @@ impl RealEngine {
|
||||
|
||||
// (node, track, list, track index, placement) for every valid
|
||||
// placement (the reference itself lands on the anchor).
|
||||
let mut placements: Vec<(NodeId, NodeId, NodeId, i32, oakcore_rs::Rational)> =
|
||||
let mut placements: Vec<(NodeId, NodeId, NodeId, i32, oak_core::Rational)> =
|
||||
Vec::new();
|
||||
for target in &targets {
|
||||
let placement = place_by_source_time(&reference.source, &target.source, anchor_in);
|
||||
@@ -2186,10 +2186,10 @@ impl RealEngine {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut children: Vec<oakundo::undocommand::UndoCommand> = Vec::new();
|
||||
let mut children: Vec<oak_undo::undocommand::UndoCommand> = Vec::new();
|
||||
for (node, track, _, _, _) in &placements {
|
||||
children.push(
|
||||
oaktimeline::undogeneral::TrackReplaceBlockWithGapCommand::new(
|
||||
oak_timeline::undogeneral::TrackReplaceBlockWithGapCommand::new(
|
||||
graphops::node_ref(&project, *track),
|
||||
graphops::node_ref(&project, *node),
|
||||
false,
|
||||
@@ -2199,7 +2199,7 @@ impl RealEngine {
|
||||
}
|
||||
for (node, _, list, track_index, timeline_in) in &placements {
|
||||
children.push(
|
||||
oaktimeline::undopointer::TrackPlaceBlockCommand::new(
|
||||
oak_timeline::undopointer::TrackPlaceBlockCommand::new(
|
||||
graphops::node_ref(&project, *list),
|
||||
*track_index,
|
||||
graphops::node_ref(&project, *node),
|
||||
@@ -2218,8 +2218,8 @@ impl RealEngine {
|
||||
/// offset triggers a rate search whose winner also rescales the clip
|
||||
/// speed (one multi-undo).
|
||||
fn sync_clips_by_waveform_internal(&mut self, clips: &[ClipId], allow_speed: bool) {
|
||||
use oakaudio::synchronizer::place_by_waveform_offset;
|
||||
use oakaudio::waveformsync::{estimate_envelope_offset_valid, estimate_stretch_and_offset};
|
||||
use oak_audio::synchronizer::place_by_waveform_offset;
|
||||
use oak_audio::waveformsync::{estimate_envelope_offset_valid, estimate_stretch_and_offset};
|
||||
|
||||
let Some(cache) = self.waveform_cache() else {
|
||||
return;
|
||||
@@ -2233,7 +2233,7 @@ impl RealEngine {
|
||||
track: NodeId,
|
||||
list: NodeId,
|
||||
track_index: i32,
|
||||
block_in: oakcore_rs::Rational,
|
||||
block_in: oak_core::Rational,
|
||||
speed: f64,
|
||||
media_in_s: f64,
|
||||
media_len_s: f64,
|
||||
@@ -2318,7 +2318,7 @@ impl RealEngine {
|
||||
NodeId,
|
||||
NodeId,
|
||||
i32,
|
||||
oakcore_rs::Rational,
|
||||
oak_core::Rational,
|
||||
f64,
|
||||
f64,
|
||||
)> = vec![(
|
||||
@@ -2381,7 +2381,7 @@ impl RealEngine {
|
||||
}
|
||||
let placement =
|
||||
place_by_waveform_offset(reference.block_in, offset.offset_samples, sample_rate);
|
||||
if placement.valid && placement.timeline_in >= oakcore_rs::Rational::new(0, 1) {
|
||||
if placement.valid && placement.timeline_in >= oak_core::Rational::new(0, 1) {
|
||||
placements.push((
|
||||
target.node,
|
||||
target.track,
|
||||
@@ -2397,10 +2397,10 @@ impl RealEngine {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut children: Vec<oakundo::undocommand::UndoCommand> = Vec::new();
|
||||
let mut children: Vec<oak_undo::undocommand::UndoCommand> = Vec::new();
|
||||
for (node, track, _, _, _, speed, old_speed) in &placements {
|
||||
children.push(
|
||||
oaktimeline::undogeneral::TrackReplaceBlockWithGapCommand::new(
|
||||
oak_timeline::undogeneral::TrackReplaceBlockWithGapCommand::new(
|
||||
graphops::node_ref(&project, *track),
|
||||
graphops::node_ref(&project, *node),
|
||||
false,
|
||||
@@ -2415,14 +2415,14 @@ impl RealEngine {
|
||||
let old_speed = *old_speed;
|
||||
let node = *node;
|
||||
let (p1, p2) = (project.clone(), project.clone());
|
||||
children.push(oakundo::undocommand::UndoCommand::from_closures(
|
||||
children.push(oak_undo::undocommand::UndoCommand::from_closures(
|
||||
move || {
|
||||
let mut g = graphops::lock(&p1);
|
||||
if let Some(c) = g
|
||||
.graph
|
||||
.get_mut(node)
|
||||
.and_then(|e| e.behavior.as_any_mut())
|
||||
.and_then(|a| a.downcast_mut::<oaknode::block::ClipBlockBehavior>())
|
||||
.and_then(|a| a.downcast_mut::<oak_node::block::ClipBlockBehavior>())
|
||||
{
|
||||
c.core.speed = new_speed;
|
||||
}
|
||||
@@ -2433,7 +2433,7 @@ impl RealEngine {
|
||||
.graph
|
||||
.get_mut(node)
|
||||
.and_then(|e| e.behavior.as_any_mut())
|
||||
.and_then(|a| a.downcast_mut::<oaknode::block::ClipBlockBehavior>())
|
||||
.and_then(|a| a.downcast_mut::<oak_node::block::ClipBlockBehavior>())
|
||||
{
|
||||
c.core.speed = old_speed;
|
||||
}
|
||||
@@ -2443,7 +2443,7 @@ impl RealEngine {
|
||||
}
|
||||
for (node, _, list, track_index, timeline_in, _, _) in &placements {
|
||||
children.push(
|
||||
oaktimeline::undopointer::TrackPlaceBlockCommand::new(
|
||||
oak_timeline::undopointer::TrackPlaceBlockCommand::new(
|
||||
graphops::node_ref(&project, *list),
|
||||
*track_index,
|
||||
graphops::node_ref(&project, *node),
|
||||
@@ -2462,7 +2462,7 @@ impl RealEngine {
|
||||
/// library. Projects without a sequence get a blank default.
|
||||
fn adopt_project(&mut self, project: ProjectRef, cx: &mut Context<Self>) {
|
||||
self.drop_project();
|
||||
oakundo::global::clear().ok();
|
||||
oak_undo::global::clear().ok();
|
||||
|
||||
// Cached display info.
|
||||
let (name, path, first_sequence) = {
|
||||
@@ -2509,7 +2509,7 @@ impl RealEngine {
|
||||
fn drop_project(&mut self) {
|
||||
*self.renderer.lock().unwrap() = RendererSlot::Untried;
|
||||
*self.source_renderer.lock().unwrap() = RendererSlot::Untried;
|
||||
oakundo::global::clear().ok();
|
||||
oak_undo::global::clear().ok();
|
||||
if let Some(mut markers) = self.markers.take() {
|
||||
graphops::release_handle(&mut markers.0);
|
||||
}
|
||||
@@ -2648,7 +2648,7 @@ impl RealEngine {
|
||||
|
||||
/// Snapshots one track (with its clips) from the graph.
|
||||
fn snapshot_track(
|
||||
graph: &oaknode::graph::Graph,
|
||||
graph: &oak_node::graph::Graph,
|
||||
track: NodeId,
|
||||
kind: TrackType,
|
||||
track_index: usize,
|
||||
@@ -2662,7 +2662,7 @@ impl RealEngine {
|
||||
// Height in internal units → pixels.
|
||||
let height = graphops::track_behavior(graph, track)
|
||||
.map(|t| {
|
||||
px(oaknode::track::internal_height_to_pixel_height(t.height).max(24) as f32)
|
||||
px(oak_node::track::internal_height_to_pixel_height(t.height).max(24) as f32)
|
||||
})
|
||||
.unwrap_or(px(64.0));
|
||||
let clips = graphops::clip_ids(graph, track)
|
||||
@@ -2670,7 +2670,7 @@ impl RealEngine {
|
||||
.enumerate()
|
||||
.filter_map(|(clip_index, &block)| {
|
||||
let (in_r, out_r, media_r) = graphops::clip_range(graph, block)?;
|
||||
let to_ts = |r: oakcore_rs::Rational| tb.map(|tb| graphops::rational_to_ts(r, tb)).unwrap_or(0);
|
||||
let to_ts = |r: oak_core::Rational| tb.map(|tb| graphops::rational_to_ts(r, tb)).unwrap_or(0);
|
||||
Some(RealClip {
|
||||
id: ClipId(block.identity()),
|
||||
range: FrameRange::new(Frame(to_ts(in_r)), Frame(to_ts(out_r))),
|
||||
@@ -2844,7 +2844,7 @@ impl RealEngine {
|
||||
let type_id = graphops::node_type_id(&guard.graph, node);
|
||||
// `name_of` covers both static (built-in) and dynamic (OpenFX
|
||||
// plugin) factory entries.
|
||||
let title = oaknode::factory::Factory::global()
|
||||
let title = oak_node::factory::Factory::global()
|
||||
.name_of(&type_id)
|
||||
.filter(|n| !n.is_empty())
|
||||
.unwrap_or(type_id);
|
||||
@@ -2853,7 +2853,7 @@ impl RealEngine {
|
||||
// simplified 徽标/计数 of stage 6b). Built-in effects show none.
|
||||
let badge = plugin_handle.and_then(|handle| {
|
||||
let count =
|
||||
oakplugin::suites::message::persistent_message_count(handle as usize);
|
||||
oak_plugin::suites::message::persistent_message_count(handle as usize);
|
||||
(count > 0).then_some(count)
|
||||
});
|
||||
let subtitle = plugin_handle.map(|_| {
|
||||
@@ -2897,7 +2897,7 @@ impl RealEngine {
|
||||
/// waveform sync extracts its envelope over (`media_in + length`, speed
|
||||
/// and reverse ignored there; parity with
|
||||
/// `oakengine_clip_get_media_range_rational`).
|
||||
fn clip_media_length_seconds(g: &oaknode::graph::Graph, node: NodeId) -> f64 {
|
||||
fn clip_media_length_seconds(g: &oak_node::graph::Graph, node: NodeId) -> f64 {
|
||||
match graphops::clip_range(g, node) {
|
||||
Some((in_r, out_r, _)) => (out_r - in_r).to_f64(),
|
||||
None => 0.0,
|
||||
@@ -3143,7 +3143,7 @@ impl AudioMeterDataSource for RealEngine {
|
||||
// (oakaudio's manager, clamped to the meter's 0..1 range). Silent
|
||||
// when nothing has been pushed to the output (no playback audio
|
||||
// path yet) or without an AudioManager instance.
|
||||
let Some(manager) = oakaudio::manager::instance() else {
|
||||
let Some(manager) = oak_audio::manager::instance() else {
|
||||
return vec![0.0, 0.0];
|
||||
};
|
||||
let mut peaks = [0.0f32; 8];
|
||||
@@ -3287,7 +3287,7 @@ impl AppEngine for RealEngine {
|
||||
let (Some(project), Some(seq)) = (self.project.clone(), self.sequence) else {
|
||||
return;
|
||||
};
|
||||
let internal = oaknode::track::pixel_height_to_internal_height(f32::from(height) as i32);
|
||||
let internal = oak_node::track::pixel_height_to_internal_height(f32::from(height) as i32);
|
||||
let guard = graphops::lock(&project);
|
||||
for kind in [TrackType::Video, TrackType::Audio, TrackType::Subtitle] {
|
||||
for track in graphops::track_ids(&guard.graph, seq, kind) {
|
||||
@@ -3363,7 +3363,7 @@ impl AppEngine for RealEngine {
|
||||
&mut self,
|
||||
effect: EffectId,
|
||||
input_id: &str,
|
||||
value: oaknode::value::NodeValue,
|
||||
value: oak_node::value::NodeValue,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Result<(), String> {
|
||||
let Some(project) = self.project.clone() else {
|
||||
@@ -3394,7 +3394,7 @@ impl AppEngine for RealEngine {
|
||||
return Err("not a plugin effect".into());
|
||||
};
|
||||
drop(guard);
|
||||
if !oakplugin::node_factory::push_button_clicked(instance, input_id) {
|
||||
if !oak_plugin::node_factory::push_button_clicked(instance, input_id) {
|
||||
return Err(format!("push button \"{input_id}\" not found"));
|
||||
}
|
||||
// A button press can change the plugin's other parameters; refresh
|
||||
@@ -3663,7 +3663,7 @@ impl AppEngine for RealEngine {
|
||||
TimelineEvent::TrackHeightChanged { track, height } => {
|
||||
if let (Some(t), Some(project)) = (self.tracks.get(*track), self.project.clone()) {
|
||||
let internal =
|
||||
oaknode::track::pixel_height_to_internal_height(f32::from(*height) as i32);
|
||||
oak_node::track::pixel_height_to_internal_height(f32::from(*height) as i32);
|
||||
graphops::set_track_height(&project, t.track, internal);
|
||||
self.rebuild_timeline();
|
||||
}
|
||||
@@ -3798,7 +3798,7 @@ impl AppEngine for RealEngine {
|
||||
graphops::workarea_set(
|
||||
&wa.0,
|
||||
true,
|
||||
oakcore_rs::TimeRange::new(
|
||||
oak_core::TimeRange::new(
|
||||
graphops::ts_to_rational(start.0, tb),
|
||||
graphops::ts_to_rational(end.0, tb),
|
||||
),
|
||||
@@ -3820,11 +3820,11 @@ impl AppEngine for RealEngine {
|
||||
let result = graphops::workarea_set_undoable(
|
||||
&wa.0,
|
||||
true,
|
||||
oakcore_rs::TimeRange::new(
|
||||
oak_core::TimeRange::new(
|
||||
graphops::ts_to_rational(start.0, tb),
|
||||
graphops::ts_to_rational(end.0, tb),
|
||||
),
|
||||
oakcore_rs::TimeRange::new(
|
||||
oak_core::TimeRange::new(
|
||||
graphops::ts_to_rational(old_start.0, tb),
|
||||
graphops::ts_to_rational(old_end.0, tb),
|
||||
),
|
||||
@@ -3840,11 +3840,11 @@ impl AppEngine for RealEngine {
|
||||
let result = graphops::workarea_set_undoable(
|
||||
&wa.0,
|
||||
false,
|
||||
oakcore_rs::TimeRange::new(
|
||||
oak_core::TimeRange::new(
|
||||
graphops::ts_to_rational(old_start.0, tb),
|
||||
graphops::ts_to_rational(old_end.0, tb),
|
||||
),
|
||||
oakcore_rs::TimeRange::new(
|
||||
oak_core::TimeRange::new(
|
||||
graphops::ts_to_rational(old_start.0, tb),
|
||||
graphops::ts_to_rational(old_end.0, tb),
|
||||
),
|
||||
@@ -3915,23 +3915,23 @@ impl AppEngine for RealEngine {
|
||||
}
|
||||
|
||||
fn can_undo(&self) -> bool {
|
||||
self.project.is_some() && oakundo::global::undoable()
|
||||
self.project.is_some() && oak_undo::global::undoable()
|
||||
}
|
||||
|
||||
fn can_redo(&self) -> bool {
|
||||
self.project.is_some() && oakundo::global::redoable()
|
||||
self.project.is_some() && oak_undo::global::redoable()
|
||||
}
|
||||
|
||||
fn undo(&mut self, cx: &mut Context<Self>) {
|
||||
if self.project.is_some() {
|
||||
oakundo::global::undo().ok();
|
||||
oak_undo::global::undo().ok();
|
||||
self.apply_stack_change(cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn redo(&mut self, cx: &mut Context<Self>) {
|
||||
if self.project.is_some() {
|
||||
oakundo::global::redo().ok();
|
||||
oak_undo::global::redo().ok();
|
||||
self.apply_stack_change(cx);
|
||||
}
|
||||
}
|
||||
@@ -3943,18 +3943,18 @@ impl AppEngine for RealEngine {
|
||||
// The C++ `HistoryModel` lists every row of the engine stack (done
|
||||
// first, then the redoable tail); labels are read through the same
|
||||
// two-stage contract the C++ `oakengine_undo_command_text` uses.
|
||||
let count = oakundo::global::count().unwrap_or(0);
|
||||
let count = oak_undo::global::count().unwrap_or(0);
|
||||
(0..count)
|
||||
.map(|row| super::HistoryEntry {
|
||||
name: oakundo::global::command_name(row).unwrap_or_default(),
|
||||
done: oakundo::global::command_done(row).unwrap_or(false),
|
||||
name: oak_undo::global::command_name(row).unwrap_or_default(),
|
||||
done: oak_undo::global::command_done(row).unwrap_or(false),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn history_index(&self) -> i64 {
|
||||
if self.project.is_some() {
|
||||
oakundo::global::index().unwrap_or(0)
|
||||
oak_undo::global::index().unwrap_or(0)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
@@ -3962,7 +3962,7 @@ impl AppEngine for RealEngine {
|
||||
|
||||
fn jump_history(&mut self, index: i64, cx: &mut Context<Self>) {
|
||||
if self.project.is_some() {
|
||||
oakundo::global::jump(index).ok();
|
||||
oak_undo::global::jump(index).ok();
|
||||
self.apply_stack_change(cx);
|
||||
}
|
||||
}
|
||||
@@ -4055,7 +4055,7 @@ impl AppEngine for RealEngine {
|
||||
.graph
|
||||
.get_mut(footage)
|
||||
.and_then(|e| e.behavior.as_any_mut())
|
||||
.and_then(|a| a.downcast_mut::<oaknode::footage::FootageBehavior>())
|
||||
.and_then(|a| a.downcast_mut::<oak_node::footage::FootageBehavior>())
|
||||
else {
|
||||
return Err("entry is not footage".into());
|
||||
};
|
||||
@@ -4275,7 +4275,7 @@ impl AppEngine for RealEngine {
|
||||
}
|
||||
|
||||
fn set_use_proxy_media(&mut self, enabled: bool, cx: &mut Context<Self>) {
|
||||
oakcommon::configstore::ConfigStore::instance().set(
|
||||
oak_common::configstore::ConfigStore::instance().set(
|
||||
None,
|
||||
CONFIG_KEY_USE_PROXY,
|
||||
if enabled { "true" } else { "false" },
|
||||
@@ -4289,7 +4289,7 @@ impl AppEngine for RealEngine {
|
||||
/// Resolution ▸` menu): the preview geometry changes, so every cached
|
||||
/// and in-flight preview frame is stale.
|
||||
fn set_playback_divider(&mut self, divider: i64, cx: &mut Context<Self>) {
|
||||
oakcommon::configstore::ConfigStore::instance().set(
|
||||
oak_common::configstore::ConfigStore::instance().set(
|
||||
None,
|
||||
"PlaybackDivider",
|
||||
÷r.clamp(1, 8).to_string(),
|
||||
@@ -4354,7 +4354,7 @@ impl AppEngine for RealEngine {
|
||||
(f.filename.clone(), stream, f.effective_proxy_params())
|
||||
};
|
||||
|
||||
let proxy_path = oakcodec::proxymanager::ProxyManager::get_proxy_filename(
|
||||
let proxy_path = oak_codec::proxymanager::ProxyManager::get_proxy_filename(
|
||||
&Self::proxy_cache_path(),
|
||||
&filename,
|
||||
stream_index,
|
||||
@@ -4369,8 +4369,8 @@ impl AppEngine for RealEngine {
|
||||
} else {
|
||||
(0, 0)
|
||||
};
|
||||
let request = oakcodec::task::TaskRequest {
|
||||
kind: oakcodec::task::TaskKind::Proxy,
|
||||
let request = oak_codec::task::TaskRequest {
|
||||
kind: oak_codec::task::TaskKind::Proxy,
|
||||
input_filename: &filename,
|
||||
output_filename: &proxy_path,
|
||||
stream_index,
|
||||
@@ -4380,7 +4380,7 @@ impl AppEngine for RealEngine {
|
||||
proxy_width,
|
||||
proxy_height,
|
||||
};
|
||||
let task_params = oaktask::proxy::ProxyParams {
|
||||
let task_params = oak_task::proxy::ProxyParams {
|
||||
width: params.width,
|
||||
height: params.height,
|
||||
divider: params.divider,
|
||||
@@ -4407,21 +4407,21 @@ impl AppEngine for RealEngine {
|
||||
|
||||
let label = format!("Generating Proxy {}", filename);
|
||||
let (tx, rx) = mpsc::channel::<super::engine::ExportEvent>();
|
||||
let mut driver = oaktask::task::Task::new(&label, None);
|
||||
let mut driver = oak_task::task::Task::new(&label, None);
|
||||
{
|
||||
let tx = tx.clone();
|
||||
driver.set_event_listener(Box::new(move |event| {
|
||||
let event = match event {
|
||||
oaktask::task::TaskEvent::Started => super::engine::ExportEvent::Started,
|
||||
oaktask::task::TaskEvent::Progress(value) => {
|
||||
oak_task::task::TaskEvent::Started => super::engine::ExportEvent::Started,
|
||||
oak_task::task::TaskEvent::Progress(value) => {
|
||||
super::engine::ExportEvent::Progress(value)
|
||||
}
|
||||
oaktask::task::TaskEvent::Finished => return,
|
||||
oak_task::task::TaskEvent::Finished => return,
|
||||
};
|
||||
let _ = tx.send(event);
|
||||
}));
|
||||
}
|
||||
driver.set_behavior(Box::new(oaktask::proxy::ProxyTask::new(
|
||||
driver.set_behavior(Box::new(oak_task::proxy::ProxyTask::new(
|
||||
&request,
|
||||
task_params,
|
||||
)));
|
||||
@@ -4447,7 +4447,7 @@ impl AppEngine for RealEngine {
|
||||
.graph
|
||||
.get_mut(footage)
|
||||
.and_then(|e| e.behavior.as_any_mut())
|
||||
.and_then(|a| a.downcast_mut::<oaknode::footage::FootageBehavior>())
|
||||
.and_then(|a| a.downcast_mut::<oak_node::footage::FootageBehavior>())
|
||||
{
|
||||
f.set_proxy(&proxy_path, 1, stream_index, params.version, true);
|
||||
}
|
||||
@@ -4483,7 +4483,7 @@ impl AppEngine for RealEngine {
|
||||
}
|
||||
};
|
||||
let _ = std::fs::remove_file(&proxy_path);
|
||||
if let Ok(working) = oakcodec::proxymanager::ProxyManager::get_working_filename(&proxy_path)
|
||||
if let Ok(working) = oak_codec::proxymanager::ProxyManager::get_working_filename(&proxy_path)
|
||||
{
|
||||
let _ = std::fs::remove_file(&working);
|
||||
}
|
||||
@@ -4493,7 +4493,7 @@ impl AppEngine for RealEngine {
|
||||
.graph
|
||||
.get_mut(footage)
|
||||
.and_then(|e| e.behavior.as_any_mut())
|
||||
.and_then(|a| a.downcast_mut::<oaknode::footage::FootageBehavior>())
|
||||
.and_then(|a| a.downcast_mut::<oak_node::footage::FootageBehavior>())
|
||||
{
|
||||
f.clear_proxy();
|
||||
}
|
||||
@@ -4511,7 +4511,7 @@ impl AppEngine for RealEngine {
|
||||
.graph
|
||||
.get_mut(footage)
|
||||
.and_then(|e| e.behavior.as_any_mut())
|
||||
.and_then(|a| a.downcast_mut::<oaknode::footage::FootageBehavior>())
|
||||
.and_then(|a| a.downcast_mut::<oak_node::footage::FootageBehavior>())
|
||||
{
|
||||
f.proxy_enabled = enabled;
|
||||
}
|
||||
@@ -4557,7 +4557,7 @@ impl AppEngine for RealEngine {
|
||||
.graph
|
||||
.get_mut(footage)
|
||||
.and_then(|e| e.behavior.as_any_mut())
|
||||
.and_then(|a| a.downcast_mut::<oaknode::footage::FootageBehavior>())
|
||||
.and_then(|a| a.downcast_mut::<oak_node::footage::FootageBehavior>())
|
||||
{
|
||||
f.set_custom_proxy_params(params.to_codec());
|
||||
}
|
||||
@@ -4575,7 +4575,7 @@ impl AppEngine for RealEngine {
|
||||
.graph
|
||||
.get_mut(footage)
|
||||
.and_then(|e| e.behavior.as_any_mut())
|
||||
.and_then(|a| a.downcast_mut::<oaknode::footage::FootageBehavior>())
|
||||
.and_then(|a| a.downcast_mut::<oak_node::footage::FootageBehavior>())
|
||||
{
|
||||
f.clear_custom_proxy_params();
|
||||
}
|
||||
@@ -4741,7 +4741,7 @@ impl AppEngine for RealEngine {
|
||||
continue;
|
||||
}
|
||||
let clip_ref = NodeRef::new(project.clone(), block);
|
||||
if oaktimeline::multicam::clip_find_multicam(&clip_ref).is_some() {
|
||||
if oak_timeline::multicam::clip_find_multicam(&clip_ref).is_some() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -4790,21 +4790,21 @@ impl AppEngine for RealEngine {
|
||||
let children: Vec<_> = by_seq
|
||||
.into_iter()
|
||||
.map(|(seq, clips)| {
|
||||
oaktimeline::multicam::multicam_enable(
|
||||
oak_timeline::multicam::multicam_enable(
|
||||
clips,
|
||||
NodeRef::new(project.clone(), seq),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let label = oaktimeline::multicam::enable_label(eligible.len());
|
||||
let label = oak_timeline::multicam::enable_label(eligible.len());
|
||||
graphops::push_multi_command(children, &label)
|
||||
} else {
|
||||
let clip_refs: Vec<NodeRef> = all_clips
|
||||
.iter()
|
||||
.map(|block| NodeRef::new(project.clone(), *block))
|
||||
.collect();
|
||||
let label = oaktimeline::multicam::disable_label(all_clips.len());
|
||||
graphops::push_command(oaktimeline::multicam::multicam_disable(clip_refs), &label)
|
||||
let label = oak_timeline::multicam::disable_label(all_clips.len());
|
||||
graphops::push_command(oak_timeline::multicam::multicam_disable(clip_refs), &label)
|
||||
};
|
||||
self.apply_edit(result, "multicam enable/disable", cx);
|
||||
}
|
||||
@@ -4826,14 +4826,14 @@ impl AppEngine for RealEngine {
|
||||
return;
|
||||
};
|
||||
let playhead = graphops::sequence_playhead(&graphops::lock(&project).graph, seq);
|
||||
let cmd = oaktimeline::multicam::multicam_switch(
|
||||
let cmd = oak_timeline::multicam::multicam_switch(
|
||||
NodeRef::new(project.clone(), clip),
|
||||
source,
|
||||
split_clip,
|
||||
playhead,
|
||||
);
|
||||
let result =
|
||||
graphops::push_command(cmd, oaktimeline::multicam::SWITCH_LABEL);
|
||||
graphops::push_command(cmd, oak_timeline::multicam::SWITCH_LABEL);
|
||||
self.apply_edit(result, "multicam switch", cx);
|
||||
}
|
||||
|
||||
@@ -4897,11 +4897,11 @@ impl RealEngine {
|
||||
fn open_interchange(&mut self, path: &PathBuf, cx: &mut Context<Self>) -> Result<(), String> {
|
||||
let title = format!("Loading '{}'", path.display());
|
||||
let result: Arc<Mutex<Option<ProjectRef>>> = Arc::new(Mutex::new(None));
|
||||
let mut driver = oaktask::task::Task::new(&title, None);
|
||||
let mut driver = oak_task::task::Task::new(&title, None);
|
||||
driver.set_behavior(Box::new(OtioLoadBehavior {
|
||||
inner: oaktask::project::loadotio::LoadOTIOTask::new(
|
||||
oaktask::project::load::ProjectLoadBaseTask::new(
|
||||
oaktask::task::Task::new(&title, None),
|
||||
inner: oak_task::project::loadotio::LoadOTIOTask::new(
|
||||
oak_task::project::load::ProjectLoadBaseTask::new(
|
||||
oak_task::task::Task::new(&title, None),
|
||||
path.to_string_lossy().into_owned(),
|
||||
),
|
||||
),
|
||||
@@ -4958,9 +4958,9 @@ impl RealEngine {
|
||||
return Err("no project open".into());
|
||||
};
|
||||
let filename = path.to_string_lossy().into_owned();
|
||||
let mut driver = oaktask::task::Task::new("Saving project...", None);
|
||||
driver.set_behavior(Box::new(oaktask::project::saveotio::SaveOTIOTask {
|
||||
base: oaktask::task::Task::new("Saving project...", None),
|
||||
let mut driver = oak_task::task::Task::new("Saving project...", None);
|
||||
driver.set_behavior(Box::new(oak_task::project::saveotio::SaveOTIOTask {
|
||||
base: oak_task::task::Task::new("Saving project...", None),
|
||||
project,
|
||||
filename,
|
||||
}));
|
||||
@@ -4998,13 +4998,13 @@ impl RealEngine {
|
||||
/// pattern).
|
||||
struct OtioLoadBehavior {
|
||||
/// The module load task.
|
||||
inner: oaktask::project::loadotio::LoadOTIOTask,
|
||||
inner: oak_task::project::loadotio::LoadOTIOTask,
|
||||
/// The result slot (the loaded project).
|
||||
result: Arc<Mutex<Option<ProjectRef>>>,
|
||||
}
|
||||
|
||||
impl oaktask::task::TaskBehavior for OtioLoadBehavior {
|
||||
fn run(&mut self, task: &mut oaktask::task::Task) -> oaktask::error::Result<()> {
|
||||
impl oak_task::task::TaskBehavior for OtioLoadBehavior {
|
||||
fn run(&mut self, task: &mut oak_task::task::Task) -> oak_task::error::Result<()> {
|
||||
self.inner.run(task)?;
|
||||
if let Ok(project) = self.inner.base.take_project() {
|
||||
*self.result.lock().unwrap_or_else(|e| e.into_inner()) = Some(project);
|
||||
@@ -5019,14 +5019,14 @@ impl oaktask::task::TaskBehavior for OtioLoadBehavior {
|
||||
/// Pure helper so the export dialog can be unit tested.
|
||||
pub fn encoding_formats() -> Vec<(i32, String, String)> {
|
||||
let mut out = Vec::new();
|
||||
for i in 0..(oakcodec::exportformat::Format::Count as i32) {
|
||||
let Some(format) = oakcodec::exportformat::Format::from_i32(i) else {
|
||||
for i in 0..(oak_codec::exportformat::Format::Count as i32) {
|
||||
let Some(format) = oak_codec::exportformat::Format::from_i32(i) else {
|
||||
continue;
|
||||
};
|
||||
out.push((
|
||||
i,
|
||||
oakcodec::exportformat::Format::get_name(format),
|
||||
oakcodec::exportformat::Format::get_extension(format),
|
||||
oak_codec::exportformat::Format::get_name(format),
|
||||
oak_codec::exportformat::Format::get_extension(format),
|
||||
));
|
||||
}
|
||||
out
|
||||
@@ -5081,8 +5081,8 @@ pub const DEFAULT_SNAPSHOT_INTERVAL_SEC: i64 = 600;
|
||||
pub const DEFAULT_TRANSITION_SEC: &str = "0.5";
|
||||
|
||||
/// The process-wide config store.
|
||||
fn config_store() -> &'static oakcommon::configstore::ConfigStore {
|
||||
oakcommon::configstore::ConfigStore::instance()
|
||||
fn config_store() -> &'static oak_common::configstore::ConfigStore {
|
||||
oak_common::configstore::ConfigStore::instance()
|
||||
}
|
||||
|
||||
/// Loads the persisted configuration from disk (once at startup, before
|
||||
@@ -5161,12 +5161,12 @@ pub fn set_theme_dark(dark: bool) {
|
||||
/// The host's audio output device names in enumeration order (the index is
|
||||
/// what the manager's `set_output_device` takes).
|
||||
pub fn audio_output_devices() -> Vec<String> {
|
||||
oakaudio::manager::output_device_names()
|
||||
oak_audio::manager::output_device_names()
|
||||
}
|
||||
|
||||
/// The host's audio input device names (see [`audio_output_devices`]).
|
||||
pub fn audio_input_devices() -> Vec<String> {
|
||||
oakaudio::manager::input_device_names()
|
||||
oak_audio::manager::input_device_names()
|
||||
}
|
||||
|
||||
/// Selects the output device by NAME (empty = system default), persists the
|
||||
@@ -5177,9 +5177,9 @@ pub fn set_audio_output_device(name: &str) {
|
||||
let index = if name.is_empty() {
|
||||
-1
|
||||
} else {
|
||||
oakaudio::manager::device_index_by_name(name, true).unwrap_or(-1)
|
||||
oak_audio::manager::device_index_by_name(name, true).unwrap_or(-1)
|
||||
};
|
||||
if let Some(mut manager) = oakaudio::manager::instance() {
|
||||
if let Some(mut manager) = oak_audio::manager::instance() {
|
||||
manager.set_output_device(index).ok();
|
||||
}
|
||||
}
|
||||
@@ -5191,9 +5191,9 @@ pub fn set_audio_input_device(name: &str) {
|
||||
let index = if name.is_empty() {
|
||||
-1
|
||||
} else {
|
||||
oakaudio::manager::device_index_by_name(name, false).unwrap_or(-1)
|
||||
oak_audio::manager::device_index_by_name(name, false).unwrap_or(-1)
|
||||
};
|
||||
if let Some(mut manager) = oakaudio::manager::instance() {
|
||||
if let Some(mut manager) = oak_audio::manager::instance() {
|
||||
manager.set_input_device(index).ok();
|
||||
}
|
||||
}
|
||||
@@ -5223,7 +5223,7 @@ pub fn audio_input_device() -> String {
|
||||
/// choices. Called once at startup; without an instance `push_to_output`
|
||||
/// fails silently and playback stays video-only.
|
||||
pub fn audio_init_from_config() {
|
||||
oakaudio::manager::ManagerInner::create_instance().ok();
|
||||
oak_audio::manager::ManagerInner::create_instance().ok();
|
||||
let output = config_get_string(CONFIG_KEY_AUDIO_OUTPUT);
|
||||
if !output.is_empty() {
|
||||
set_audio_output_device(&output);
|
||||
@@ -5469,7 +5469,7 @@ mod tests {
|
||||
// add_track returns the NEW track's index: with the default 2V+2A
|
||||
// layout those are the third video and third audio track.
|
||||
assert_eq!((v, a), (2, 2), "in-memory tracks");
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
|
||||
// Save as uncompressed `.ovexml` (the module serializer reads plain
|
||||
// XML).
|
||||
@@ -5590,7 +5590,7 @@ mod tests {
|
||||
"oakapp_e2e_media_{}.mp4",
|
||||
std::process::id()
|
||||
));
|
||||
oakcodec::testmedia::write_test_clip(&media, 64, 64, 10, 10)
|
||||
oak_codec::testmedia::write_test_clip(&media, 64, 64, 10, 10)
|
||||
.expect("generate e2e test media");
|
||||
let footage = graphops::import_footage(&project, &media).expect("import_footage");
|
||||
graphops::add_track(&project, seq, TrackType::Video).expect("add a video track");
|
||||
@@ -5622,7 +5622,7 @@ mod tests {
|
||||
// Invalid geometry is rejected.
|
||||
assert!(crate::oakui::renderops::render_sequence_frame(&project, seq, 0, tb, 0, 270).is_err());
|
||||
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
let _ = std::fs::remove_file(&media);
|
||||
}
|
||||
|
||||
@@ -5637,8 +5637,8 @@ mod tests {
|
||||
fn process_backend_preview_path_is_zero_copy() {
|
||||
let _media = media_lock();
|
||||
let _worker = WorkerBinGuard::set();
|
||||
use oakrender::manager::{RenderBackendChoice, RenderManager};
|
||||
use oakrender::procpool::{
|
||||
use oak_render::manager::{RenderBackendChoice, RenderManager};
|
||||
use oak_render::procpool::{
|
||||
main_heap_frame_copies, reset_main_heap_frame_copies, DispatcherConfig,
|
||||
};
|
||||
RenderManager::shutdown();
|
||||
@@ -5691,7 +5691,7 @@ mod tests {
|
||||
release_rendered_frame(&frame);
|
||||
|
||||
RenderManager::shutdown();
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
}
|
||||
|
||||
/// M12 P3 acceptance: importing a media file makes it appear in the
|
||||
@@ -5706,7 +5706,7 @@ mod tests {
|
||||
"oakapp_browser_{}.mp4",
|
||||
std::process::id()
|
||||
));
|
||||
oakcodec::testmedia::write_test_clip(&media, 64, 64, 10, 10)
|
||||
oak_codec::testmedia::write_test_clip(&media, 64, 64, 10, 10)
|
||||
.expect("generate test media");
|
||||
graphops::import_footage(&project, &media).expect("import must succeed");
|
||||
|
||||
@@ -5726,7 +5726,7 @@ mod tests {
|
||||
let node = crate::oakui::projectbrowser::find_by_identity(&project, entry.id);
|
||||
assert!(node.is_some(), "selection resolves to a node");
|
||||
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
let _ = std::fs::remove_file(&media);
|
||||
}
|
||||
|
||||
@@ -5748,7 +5748,7 @@ mod tests {
|
||||
"oakapp_engine_import_{}.mp4",
|
||||
std::process::id()
|
||||
));
|
||||
oakcodec::testmedia::write_test_clip(&media, 64, 64, 10, 10)
|
||||
oak_codec::testmedia::write_test_clip(&media, 64, 64, 10, 10)
|
||||
.expect("generate e2e test media");
|
||||
let imported = cx
|
||||
.update(|app| engine.update(app, |engine, cx| engine.import_footage(media.clone(), cx)));
|
||||
@@ -5954,7 +5954,7 @@ mod tests {
|
||||
let entries = cx.read(|app| engine.read(app).history_entries());
|
||||
assert!(entries.iter().all(|e| e.done), "the redo restored every row");
|
||||
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
}
|
||||
|
||||
/// The multicam switch through the UI path (`multicam_switch_to`): it
|
||||
@@ -5966,7 +5966,7 @@ mod tests {
|
||||
async fn real_engine_multicam_switch_round_trips_through_undo(
|
||||
cx: &mut gpui::TestAppContext,
|
||||
) {
|
||||
use oaknode::block::clip_input::TEXTURE_INPUT;
|
||||
use oak_node::block::clip_input::TEXTURE_INPUT;
|
||||
let _media = media_lock();
|
||||
let engine = cx.update(|cx| cx.new(|cx| RealEngine::create(cx)));
|
||||
|
||||
@@ -5977,7 +5977,7 @@ mod tests {
|
||||
let seq = graphops::create_sequence(&project, "Multicam Test");
|
||||
graphops::add_track(&project, seq, TrackType::Video).unwrap();
|
||||
graphops::add_track(&project, seq, TrackType::Video).unwrap();
|
||||
let clip = oaktimeline::util::block_clip_create(&project);
|
||||
let clip = oak_timeline::util::block_clip_create(&project);
|
||||
{
|
||||
let mut g = graphops::lock(&project);
|
||||
let c = g
|
||||
@@ -5987,20 +5987,20 @@ mod tests {
|
||||
.behavior
|
||||
.as_any_mut()
|
||||
.unwrap()
|
||||
.downcast_mut::<oaknode::block::ClipBlockBehavior>()
|
||||
.downcast_mut::<oak_node::block::ClipBlockBehavior>()
|
||||
.unwrap();
|
||||
c.core.range = oakcore_rs::TimeRange::new(
|
||||
oakcore_rs::Rational::new(0, 1),
|
||||
oakcore_rs::Rational::new(100, 1),
|
||||
c.core.range = oak_core::TimeRange::new(
|
||||
oak_core::Rational::new(0, 1),
|
||||
oak_core::Rational::new(100, 1),
|
||||
);
|
||||
c.core.media_in = oakcore_rs::Rational::new(0, 1);
|
||||
c.core.media_in = oak_core::Rational::new(0, 1);
|
||||
}
|
||||
let track0 = {
|
||||
let g = graphops::lock(&project);
|
||||
graphops::track_ids(&g.graph, seq, TrackType::Video)[0]
|
||||
};
|
||||
oaktimeline::util::track_append_block(
|
||||
&oaktimeline::util::NodeRef::new(project.clone(), track0),
|
||||
oak_timeline::util::track_append_block(
|
||||
&oak_timeline::util::NodeRef::new(project.clone(), track0),
|
||||
&clip,
|
||||
);
|
||||
{
|
||||
@@ -6074,7 +6074,7 @@ mod tests {
|
||||
"disabling multicam clears the detection"
|
||||
);
|
||||
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
}
|
||||
|
||||
/// M12 P2 acceptance: a real project with a sequence + footage clip
|
||||
@@ -6093,7 +6093,7 @@ mod tests {
|
||||
"oakapp_nodegraph_{}.mp4",
|
||||
std::process::id()
|
||||
));
|
||||
oakcodec::testmedia::write_test_clip(&media, 64, 64, 10, 10)
|
||||
oak_codec::testmedia::write_test_clip(&media, 64, 64, 10, 10)
|
||||
.expect("generate test media");
|
||||
let footage = graphops::import_footage(&project, &media).expect("import must succeed");
|
||||
graphops::place_footage_clip(&project, seq, footage, TrackType::Video, 0, 0, 10, 0)
|
||||
@@ -6139,7 +6139,7 @@ mod tests {
|
||||
"the footage→clip media edge is real (got {real_edges} real edges)"
|
||||
);
|
||||
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
let _ = std::fs::remove_file(&media);
|
||||
}
|
||||
|
||||
@@ -6162,7 +6162,7 @@ mod tests {
|
||||
// (the effect-chain insert on an empty chain rewires nothing
|
||||
// and connects the effect to the clip's `tex_in`), then the
|
||||
// footage feeds the effect's media input.
|
||||
let clip = oaktimeline::util::block_clip_create(&project);
|
||||
let clip = oak_timeline::util::block_clip_create(&project);
|
||||
let ty = crate::oakui::effectchain::addable_effects()
|
||||
.into_iter()
|
||||
.next()
|
||||
@@ -6174,7 +6174,7 @@ mod tests {
|
||||
"oakapp_sel_link_{}.mp4",
|
||||
std::process::id()
|
||||
));
|
||||
oakcodec::testmedia::write_test_clip(&media, 32, 32, 10, 10)
|
||||
oak_codec::testmedia::write_test_clip(&media, 32, 32, 10, 10)
|
||||
.expect("generate test media");
|
||||
let footage = graphops::import_footage(&project, &media).expect("import the media");
|
||||
let effect_input = graphops::lock(&project)
|
||||
@@ -6194,8 +6194,8 @@ mod tests {
|
||||
let g = graphops::lock(&project);
|
||||
graphops::track_ids(&g.graph, seq, TrackType::Video)[0]
|
||||
};
|
||||
oaktimeline::util::track_append_block(
|
||||
&oaktimeline::util::NodeRef::new(project.clone(), track0),
|
||||
oak_timeline::util::track_append_block(
|
||||
&oak_timeline::util::NodeRef::new(project.clone(), track0),
|
||||
&clip,
|
||||
);
|
||||
let _ = std::fs::remove_file(&media);
|
||||
@@ -6272,7 +6272,7 @@ mod tests {
|
||||
"a card click re-selects the effect's node"
|
||||
);
|
||||
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
}
|
||||
|
||||
/// Regression: the source monitor's full-res job renders the selected
|
||||
@@ -6293,7 +6293,7 @@ mod tests {
|
||||
"oakapp_fullres_src_{}.mp4",
|
||||
std::process::id()
|
||||
));
|
||||
oakcodec::testmedia::write_test_clip(&media, 64, 64, 10, 10)
|
||||
oak_codec::testmedia::write_test_clip(&media, 64, 64, 10, 10)
|
||||
.expect("generate test media");
|
||||
let footage = graphops::import_footage(&project, &media).expect("import must succeed");
|
||||
let tb = graphops::sequence_time_base(&graphops::lock(&project).graph, seq).unwrap();
|
||||
@@ -6322,7 +6322,7 @@ mod tests {
|
||||
.expect("the worker delivers the frame after the project drop");
|
||||
let bytes = event.image.as_bytes(0).expect("one frame");
|
||||
assert_eq!(bytes.len(), 64 * 64 * 4, "full-res geometry");
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
let _ = std::fs::remove_file(&media);
|
||||
}
|
||||
|
||||
@@ -6467,7 +6467,7 @@ mod tests {
|
||||
"oakapp_fullres_{}.mp4",
|
||||
std::process::id()
|
||||
));
|
||||
oakcodec::testmedia::write_test_clip(&media, 64, 64, 10, 10)
|
||||
oak_codec::testmedia::write_test_clip(&media, 64, 64, 10, 10)
|
||||
.expect("generate test media");
|
||||
let footage = graphops::import_footage(&project, &media).expect("import must succeed");
|
||||
graphops::place_footage_clip(&project, seq, footage, TrackType::Video, 0, 0, 10, 0)
|
||||
@@ -6503,7 +6503,7 @@ mod tests {
|
||||
.count();
|
||||
assert!(nonzero > 0, "the footage clip renders non-black pixels");
|
||||
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
let _ = std::fs::remove_file(&media);
|
||||
}
|
||||
|
||||
@@ -6834,7 +6834,7 @@ mod tests {
|
||||
"oakapp_playback_window_{}.mp4",
|
||||
std::process::id()
|
||||
));
|
||||
oakcodec::testmedia::write_test_clip(&media, 64, 64, 250, 25)
|
||||
oak_codec::testmedia::write_test_clip(&media, 64, 64, 250, 25)
|
||||
.expect("generate playback test media");
|
||||
cx.update(|app| {
|
||||
engine.update(app, |engine, cx| {
|
||||
@@ -22,20 +22,20 @@
|
||||
//! `oakengine_task_start_sync` + the task subscription) are app-side now:
|
||||
//! the montage builders read the oaknode graph directly and the renders
|
||||
//! go through the oakrender ticket arena, exactly like the CLI's
|
||||
//! `engine.rs` (M14 R2). The export drives `oaktask::export::ExportTask`
|
||||
//! `engine.rs` (M14 R2). The export drives `oak_task::export::ExportTask`
|
||||
//! synchronously on a background thread with the module task's event
|
||||
//! listener and cancel atom wired to the app's [`ExportSession`].
|
||||
|
||||
use std::sync::mpsc;
|
||||
|
||||
use gpui::RenderImage;
|
||||
use oakcore_rs::{Rational, TimeRange};
|
||||
use oaknode::id::NodeId;
|
||||
use oaknode::track::TrackType;
|
||||
use oakrender::manager::RenderManager;
|
||||
use oakrender::procpool::ShmFrameRef;
|
||||
use oakrender::texture::Texture;
|
||||
use oakrender::ticket::{AudioTicketParams, MontageClip, TicketPayload, VideoTicketParams};
|
||||
use oak_core::{Rational, TimeRange};
|
||||
use oak_node::id::NodeId;
|
||||
use oak_node::track::TrackType;
|
||||
use oak_render::manager::RenderManager;
|
||||
use oak_render::procpool::ShmFrameRef;
|
||||
use oak_render::texture::Texture;
|
||||
use oak_render::ticket::{AudioTicketParams, MontageClip, TicketPayload, VideoTicketParams};
|
||||
|
||||
use super::engine::{ExportEvent, ExportSession};
|
||||
use super::frames::{bgra_bytes_to_render_image, f32_rgba_to_bgra_image};
|
||||
@@ -44,14 +44,14 @@ use super::graphops::{
|
||||
};
|
||||
use super::scopes::{analyze_bgra8, analyze_f32_rgba, ScopeData};
|
||||
|
||||
/// The pixel format the viewers render in (`oakcore_rs::PixelFormat::F32`,
|
||||
/// The pixel format the viewers render in (`oak_core::PixelFormat::F32`,
|
||||
/// the pipeline's internal format; the app downconverts to BGRA itself).
|
||||
pub const PIXEL_FORMAT_F32: i32 = 4;
|
||||
|
||||
/// The BGRA8 slot wire format the process backend renders into (M15 S2):
|
||||
/// the worker converts its F32 pipeline output to BGRA8 at the end of the
|
||||
/// render, so the main process reads display-ready bytes from the slot.
|
||||
pub const SLOT_FORMAT_BGRA8: i32 = oakrender::ipc::SLOT_FORMAT_BGRA8;
|
||||
pub const SLOT_FORMAT_BGRA8: i32 = oak_render::ipc::SLOT_FORMAT_BGRA8;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Render manager
|
||||
@@ -79,7 +79,7 @@ pub fn ensure_render_manager() -> bool {
|
||||
/// `UseProxyMedia` config switch (C++ `Tools > Use Proxy Media`; the
|
||||
/// export path never consults it — exports always decode the original).
|
||||
pub fn use_proxy_media() -> bool {
|
||||
oakcommon::configstore::ConfigStore::instance().get_bool(None, "UseProxyMedia", 1) != 0
|
||||
oak_common::configstore::ConfigStore::instance().get_bool(None, "UseProxyMedia", 1) != 0
|
||||
}
|
||||
|
||||
/// The preview media of a footage node with the three-level proxy switch
|
||||
@@ -91,7 +91,7 @@ pub fn use_proxy_media() -> bool {
|
||||
/// (the first source audio stream's rank + 1). A missing proxy file falls
|
||||
/// back to the original.
|
||||
pub fn preview_footage_media(
|
||||
f: &oaknode::footage::FootageBehavior,
|
||||
f: &oak_node::footage::FootageBehavior,
|
||||
is_video: bool,
|
||||
) -> (String, i32) {
|
||||
// The original media decodes from the footage's actual first stream of
|
||||
@@ -107,8 +107,8 @@ pub fn preview_footage_media(
|
||||
if !use_proxy_media() || !f.proxy_enabled || f.proxy.is_empty() {
|
||||
return original;
|
||||
}
|
||||
if oakcodec::proxymanager::ProxyManager::get_proxy_state(&f.proxy)
|
||||
!= oakcodec::proxymanager::ProxyState::Ready
|
||||
if oak_codec::proxymanager::ProxyManager::get_proxy_state(&f.proxy)
|
||||
!= oak_codec::proxymanager::ProxyState::Ready
|
||||
{
|
||||
return original;
|
||||
}
|
||||
@@ -120,7 +120,7 @@ pub fn preview_footage_media(
|
||||
Some(stream) if f.proxy_video_stream_index == stream.index => (f.proxy.clone(), 0),
|
||||
_ => original,
|
||||
}
|
||||
} else if oakcodec::proxymanager::ProxyManager::proxy_filename_has_audio(&f.proxy) {
|
||||
} else if oak_codec::proxymanager::ProxyManager::proxy_filename_has_audio(&f.proxy) {
|
||||
(f.proxy.clone(), 1)
|
||||
} else {
|
||||
original
|
||||
@@ -130,7 +130,7 @@ pub fn preview_footage_media(
|
||||
/// The preview media feeding a clip: the clip's footage with the proxy
|
||||
/// switch applied ([`preview_footage_media`]).
|
||||
fn clip_preview_media(
|
||||
g: &oaknode::graph::Graph,
|
||||
g: &oak_node::graph::Graph,
|
||||
block_id: NodeId,
|
||||
is_video: bool,
|
||||
) -> Option<(String, i32)> {
|
||||
@@ -743,14 +743,14 @@ pub fn encoding_params(
|
||||
path: &std::path::Path,
|
||||
workarea: Option<(i64, i64)>,
|
||||
length_frames: i64,
|
||||
) -> Result<oaktask::export::EncodingParams, String> {
|
||||
let container = oakcodec::exportformat::Format::from_i32(format)
|
||||
) -> Result<oak_task::export::EncodingParams, String> {
|
||||
let container = oak_codec::exportformat::Format::from_i32(format)
|
||||
.ok_or_else(|| format!("unknown export format {format}"))?;
|
||||
let video_codec = oakcodec::exportformat::Format::get_video_codecs(container)
|
||||
let video_codec = oak_codec::exportformat::Format::get_video_codecs(container)
|
||||
.first()
|
||||
.copied()
|
||||
.ok_or_else(|| format!("format {format} has no video codec"))?;
|
||||
let audio_codec = oakcodec::exportformat::Format::get_audio_codecs(container)
|
||||
let audio_codec = oak_codec::exportformat::Format::get_audio_codecs(container)
|
||||
.first()
|
||||
.copied()
|
||||
.ok_or_else(|| format!("format {format} has no audio codec"))?;
|
||||
@@ -780,7 +780,7 @@ pub fn encoding_params(
|
||||
Rational::new(length_frames.max(0) * i64::from(rate_den), i64::from(rate_num)),
|
||||
),
|
||||
};
|
||||
Ok(oaktask::export::EncodingParams {
|
||||
Ok(oak_task::export::EncodingParams {
|
||||
filename: path.to_string_lossy().into_owned(),
|
||||
format,
|
||||
video_enabled: true,
|
||||
@@ -812,24 +812,24 @@ pub fn encoding_params(
|
||||
pub fn spawn_export(
|
||||
p: &ProjectRef,
|
||||
seq: NodeId,
|
||||
params: oaktask::export::EncodingParams,
|
||||
params: oak_task::export::EncodingParams,
|
||||
) -> ExportSession {
|
||||
let (tx, rx) = mpsc::channel::<ExportEvent>();
|
||||
let mut driver = oaktask::task::Task::new("Exporting...", None);
|
||||
let mut driver = oak_task::task::Task::new("Exporting...", None);
|
||||
let cancel_atom = driver.get_cancel_atom();
|
||||
{
|
||||
let tx = tx.clone();
|
||||
driver.set_event_listener(Box::new(move |event| {
|
||||
let event = match event {
|
||||
oaktask::task::TaskEvent::Started => ExportEvent::Started,
|
||||
oaktask::task::TaskEvent::Progress(value) => ExportEvent::Progress(value),
|
||||
oak_task::task::TaskEvent::Started => ExportEvent::Started,
|
||||
oak_task::task::TaskEvent::Progress(value) => ExportEvent::Progress(value),
|
||||
// Finished is reported by the worker below (with the error).
|
||||
oaktask::task::TaskEvent::Finished => return,
|
||||
oak_task::task::TaskEvent::Finished => return,
|
||||
};
|
||||
let _ = tx.send(event);
|
||||
}));
|
||||
}
|
||||
driver.set_behavior(Box::new(oaktask::export::ExportTask::new(
|
||||
driver.set_behavior(Box::new(oak_task::export::ExportTask::new(
|
||||
(p.clone(), seq),
|
||||
params,
|
||||
)));
|
||||
@@ -879,7 +879,7 @@ mod tests {
|
||||
fn video_montage_covers_the_clip_range() {
|
||||
let _media = media_lock();
|
||||
let media = std::env::temp_dir().join(format!("oakapp_montage_{}.mp4", std::process::id()));
|
||||
oakcodec::testmedia::write_test_clip(&media, 64, 64, 10, 10).expect("generate test media");
|
||||
oak_codec::testmedia::write_test_clip(&media, 64, 64, 10, 10).expect("generate test media");
|
||||
|
||||
let (project, seq, _) = project_with_clip(&media);
|
||||
let tb = graphops::sequence_time_base(&lock(&project).graph, seq).unwrap();
|
||||
@@ -897,7 +897,7 @@ mod tests {
|
||||
video_montage(&project, seq, at(-1)).is_empty(),
|
||||
"a negative time covers nothing"
|
||||
);
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
let _ = std::fs::remove_file(&media);
|
||||
}
|
||||
|
||||
@@ -907,22 +907,22 @@ mod tests {
|
||||
/// the proxy switch is off or the proxy is not ready.
|
||||
#[test]
|
||||
fn preview_media_uses_the_probed_stream_indices() {
|
||||
let stream = |index: i32, is_video: bool| oaknode::footage::StreamInfo {
|
||||
let stream = |index: i32, is_video: bool| oak_node::footage::StreamInfo {
|
||||
index,
|
||||
is_video,
|
||||
video: None,
|
||||
audio: None,
|
||||
duration: oakcore_rs::Rational::new(0, 1),
|
||||
duration: oak_core::Rational::new(0, 1),
|
||||
};
|
||||
// An audio-first container: audio at 0, video at 1… plus a second
|
||||
// audio track at 2. The video must come from stream 1 and the audio
|
||||
// from stream 0, not the legacy 0/1 assumption.
|
||||
let mut f = oaknode::footage::FootageBehavior::new("/tmp/audio-first.mov");
|
||||
let mut f = oak_node::footage::FootageBehavior::new("/tmp/audio-first.mov");
|
||||
f.streams = vec![stream(0, false), stream(1, true), stream(2, false)];
|
||||
assert_eq!(preview_footage_media(&f, true).1, 1);
|
||||
assert_eq!(preview_footage_media(&f, false).1, 0);
|
||||
// Unprobed footage (no stream metadata) keeps the 0/1 fallbacks.
|
||||
let unprobed = oaknode::footage::FootageBehavior::new("/tmp/legacy.mov");
|
||||
let unprobed = oak_node::footage::FootageBehavior::new("/tmp/legacy.mov");
|
||||
assert_eq!(preview_footage_media(&unprobed, true).1, 0);
|
||||
assert_eq!(preview_footage_media(&unprobed, false).1, 1);
|
||||
}
|
||||
@@ -936,7 +936,7 @@ mod tests {
|
||||
let _media = media_lock();
|
||||
let media =
|
||||
std::env::temp_dir().join(format!("oakapp_montage_ang_{}.mp4", std::process::id()));
|
||||
oakcodec::testmedia::write_test_clip(&media, 64, 64, 10, 10).expect("generate test media");
|
||||
oak_codec::testmedia::write_test_clip(&media, 64, 64, 10, 10).expect("generate test media");
|
||||
|
||||
let (project, seq, footage) = project_with_clip(&media);
|
||||
graphops::add_track(&project, seq, TrackType::Video).expect("add a second video track");
|
||||
@@ -974,7 +974,7 @@ mod tests {
|
||||
"the other track is unaffected"
|
||||
);
|
||||
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
let _ = std::fs::remove_file(&media);
|
||||
}
|
||||
|
||||
@@ -982,7 +982,7 @@ mod tests {
|
||||
fn audio_montage_overlaps_the_range() {
|
||||
let _media = media_lock();
|
||||
let media = std::env::temp_dir().join(format!("oakapp_montage_a_{}.mp4", std::process::id()));
|
||||
oakcodec::testmedia::write_test_clip(&media, 64, 64, 10, 10).expect("generate test media");
|
||||
oak_codec::testmedia::write_test_clip(&media, 64, 64, 10, 10).expect("generate test media");
|
||||
|
||||
let (project, seq, footage) = project_with_clip(&media);
|
||||
graphops::add_track(&project, seq, TrackType::Audio).expect("add an audio track");
|
||||
@@ -996,7 +996,7 @@ mod tests {
|
||||
assert_eq!(overlapping[0].stream_index, 1, "the audio stream is selected");
|
||||
let disjoint = audio_montage(&project, seq, TimeRange::new(at(10), at(20)));
|
||||
assert!(disjoint.is_empty(), "a range past the clip overlaps nothing");
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
let _ = std::fs::remove_file(&media);
|
||||
}
|
||||
|
||||
@@ -1007,7 +1007,7 @@ mod tests {
|
||||
fn montages_skip_muted_tracks() {
|
||||
let _media = media_lock();
|
||||
let media = std::env::temp_dir().join(format!("oakapp_montage_m_{}.mp4", std::process::id()));
|
||||
oakcodec::testmedia::write_test_clip(&media, 64, 64, 10, 10).expect("generate test media");
|
||||
oak_codec::testmedia::write_test_clip(&media, 64, 64, 10, 10).expect("generate test media");
|
||||
|
||||
let (project, seq, footage) = project_with_clip(&media);
|
||||
graphops::add_track(&project, seq, TrackType::Audio).expect("add an audio track");
|
||||
@@ -1037,11 +1037,11 @@ mod tests {
|
||||
"a muted audio track contributes nothing"
|
||||
);
|
||||
|
||||
oakundo::global::undo().unwrap();
|
||||
oakundo::global::undo().unwrap();
|
||||
oak_undo::global::undo().unwrap();
|
||||
oak_undo::global::undo().unwrap();
|
||||
assert_eq!(video_montage(&project, seq, at(0)).len(), 1, "undo restores the video clip");
|
||||
assert_eq!(audio_montage(&project, seq, range).len(), 1, "undo restores the audio clip");
|
||||
oakundo::global::clear().unwrap();
|
||||
oak_undo::global::clear().unwrap();
|
||||
let _ = std::fs::remove_file(&media);
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
//! M12 P4: timeline audio waveforms.
|
||||
//!
|
||||
//! The [`WaveformCache`] holds per-clip min/max peak data extracted
|
||||
//! through `oakaudio::waveform::extract` (the real FFmpeg-backed
|
||||
//! through `oak_audio::waveform::extract` (the real FFmpeg-backed
|
||||
//! extraction; M14 R3: a direct module call, no facade); the engine
|
||||
//! refreshes it when the timeline rebuilds. The [`OakClipDecorator`] draws
|
||||
//! the peaks into the timeline's clip body.
|
||||
@@ -106,7 +106,7 @@ impl WaveformCache {
|
||||
let cname = std::ffi::CString::new(filename).ok()?;
|
||||
const SAMPLES_PER_POINT: i32 = 256;
|
||||
// Audio-stream index 0 (probe numbering).
|
||||
let outcome = oakaudio::waveform::extract(&cname, 0, SAMPLES_PER_POINT).ok()?;
|
||||
let outcome = oak_audio::waveform::extract(&cname, 0, SAMPLES_PER_POINT).ok()?;
|
||||
if outcome.channels <= 0 || outcome.points.is_empty() {
|
||||
return None;
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Windowed peak envelopes over the cached clip waveforms, the input of
|
||||
//! `oakaudio::waveformsync`'s offset / stretch correlation (the app-side
|
||||
//! `oak_audio::waveformsync`'s offset / stretch correlation (the app-side
|
||||
//! mirror of the C++ `extract_waveform_cache_envelope` in
|
||||
//! `timelinewidgetwaveformsync.cpp`).
|
||||
//!
|
||||
@@ -36,7 +36,7 @@
|
||||
//!
|
||||
//! Switch requests during playback are queued (the C++ `play_queue_`
|
||||
//! semantics) and applied when the program playhead reaches the target
|
||||
//! time. Every switch goes through `oaktimeline::multicam::multicam_switch`
|
||||
//! time. Every switch goes through `oak_timeline::multicam::multicam_switch`
|
||||
//! on the global undo stack.
|
||||
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
@@ -50,7 +50,7 @@ use gpui::{
|
||||
};
|
||||
use gpui_widgets::viewer::PlaybackClock;
|
||||
|
||||
use oaknode::nodes::multicamnode::MultiCamNode;
|
||||
use oak_node::nodes::multicamnode::MultiCamNode;
|
||||
|
||||
use crate::oakui::timecode::format_timecode;
|
||||
use crate::oakui::{AppEngine, MulticamState};
|
||||
@@ -61,7 +61,7 @@ use gpui_widgets::slider::{Slider, SliderEvent, SliderModel};
|
||||
use gpui_widgets::spinbox::{SpinBox, SpinBoxEvent};
|
||||
use gpui_widgets::value::{SliderValue, ValueKind};
|
||||
|
||||
use oaknode::value::{NodeValue, ValueType};
|
||||
use oak_node::value::{NodeValue, ValueType};
|
||||
|
||||
use crate::oakui::{AppEngine, EffectParam};
|
||||
|
||||
@@ -211,7 +211,7 @@ impl<E: AppEngine> OfxParamsView<E> {
|
||||
// an identical curve (sync_values runs per render).
|
||||
let curves = match ¶m.value {
|
||||
NodeValue::Text(json) => {
|
||||
oakplugin::param_curve::curves_from_json(json).unwrap_or_default()
|
||||
oak_plugin::param_curve::curves_from_json(json).unwrap_or_default()
|
||||
}
|
||||
_ => Vec::new(),
|
||||
};
|
||||
@@ -244,10 +244,10 @@ impl<E: AppEngine> OfxParamsView<E> {
|
||||
/// (LUT-style params), else the data extent with a 10% pad.
|
||||
fn curve_domain(
|
||||
param: &EffectParam,
|
||||
curves: &[oakplugin::param_curve::Curve],
|
||||
curves: &[oak_plugin::param_curve::Curve],
|
||||
) -> (f64, f64, f64, f64) {
|
||||
let (mut lo, mut hi) = (0.0, 1.0);
|
||||
if let Some((_, oaknode::value::NodeValue::Vec2(v))) = param
|
||||
if let Some((_, oak_node::value::NodeValue::Vec2(v))) = param
|
||||
.properties
|
||||
.iter()
|
||||
.find(|(k, _)| k == "parametric_range")
|
||||
@@ -288,8 +288,8 @@ fn curve_domain(
|
||||
/// handle offsets; a point without an explicit slope edits gets linear
|
||||
/// handles).
|
||||
fn curve_point_to_editor(
|
||||
p: &oakplugin::param_curve::ControlPoint,
|
||||
points: &[oakplugin::param_curve::ControlPoint],
|
||||
p: &oak_plugin::param_curve::ControlPoint,
|
||||
points: &[oak_plugin::param_curve::ControlPoint],
|
||||
domain: (f64, f64, f64, f64),
|
||||
) -> gpui_widgets::curve_editor::CurvePoint {
|
||||
use gpui_widgets::curve_editor::{CurvePoint, CurveVec2};
|
||||
@@ -322,11 +322,11 @@ fn curve_point_to_editor(
|
||||
fn curve_from_editor(
|
||||
points: &[gpui_widgets::curve_editor::CurvePoint],
|
||||
domain: (f64, f64, f64, f64),
|
||||
) -> oakplugin::param_curve::Curve {
|
||||
) -> oak_plugin::param_curve::Curve {
|
||||
let (lo, hi, vmin, vmax) = domain;
|
||||
let (sx, sy) = (hi - lo, vmax - vmin);
|
||||
let n = points.len();
|
||||
let mut out = oakplugin::param_curve::Curve::empty();
|
||||
let mut out = oak_plugin::param_curve::Curve::empty();
|
||||
for (i, p) in points.iter().enumerate() {
|
||||
let key = lo + p.x * sx;
|
||||
let value = vmin + p.y * sy;
|
||||
@@ -373,7 +373,7 @@ fn curve_from_editor(
|
||||
_ => 0.0,
|
||||
}
|
||||
});
|
||||
out.points.push(oakplugin::param_curve::ControlPoint {
|
||||
out.points.push(oak_plugin::param_curve::ControlPoint {
|
||||
key,
|
||||
value,
|
||||
slope: m_norm * sy / sx,
|
||||
@@ -600,7 +600,7 @@ fn build_control<E: AppEngine>(
|
||||
// One curve editor per dimension; points are seeded from the
|
||||
// JSON mirror of the curves (the input's Text value).
|
||||
let curves = match ¶m.value {
|
||||
NodeValue::Text(json) => oakplugin::param_curve::curves_from_json(json)
|
||||
NodeValue::Text(json) => oak_plugin::param_curve::curves_from_json(json)
|
||||
.unwrap_or_default(),
|
||||
_ => Vec::new(),
|
||||
};
|
||||
@@ -779,14 +779,14 @@ fn wire_controls<E: AppEngine>(view: &OfxParamsView<E>, cx: &mut Context<OfxPara
|
||||
E::PointMoved { .. } | E::HandleMoved { .. } | E::PointAdded { .. } => {}
|
||||
_ => return,
|
||||
}
|
||||
let curves: Vec<oakplugin::param_curve::Curve> = editors_all
|
||||
let curves: Vec<oak_plugin::param_curve::Curve> = editors_all
|
||||
.iter()
|
||||
.map(|e| {
|
||||
let points = e.read(cx).points().to_vec();
|
||||
curve_from_editor(&points, domain)
|
||||
})
|
||||
.collect();
|
||||
let json = oakplugin::param_curve::curves_to_json(&curves);
|
||||
let json = oak_plugin::param_curve::curves_to_json(&curves);
|
||||
engine.update(cx, |engine, cx| {
|
||||
let _ = engine.set_effect_param(
|
||||
effect,
|
||||
|
Before Width: | Height: | Size: 2.4 MiB After Width: | Height: | Size: 2.4 MiB |
@@ -21,7 +21,7 @@
|
||||
//! decode + an audio decode in one process crashes at exit, so the
|
||||
//! waveform test stays isolated from the in-lib media tests.
|
||||
//!
|
||||
//! M14 R3: the extraction is a direct `oakaudio::waveform::extract` call
|
||||
//! M14 R3: the extraction is a direct `oak_audio::waveform::extract` call
|
||||
//! (no facade).
|
||||
|
||||
use oakapp::oakui::waveform::{MinMax, WaveformCache};
|
||||
@@ -29,7 +29,7 @@ use oakapp::oakui::waveform::{MinMax, WaveformCache};
|
||||
#[test]
|
||||
fn waveform_extract_and_cache_hit() {
|
||||
let media = std::env::temp_dir().join(format!("oakapp_waveform_{}.mp4", std::process::id()));
|
||||
oakcodec::testmedia::write_test_clip(&media, 64, 64, 10, 10).expect("generate test media");
|
||||
oak_codec::testmedia::write_test_clip(&media, 64, 64, 10, 10).expect("generate test media");
|
||||
let filename = media.to_string_lossy().into_owned();
|
||||
let cache = WaveformCache::new(25.0);
|
||||
cache.refresh(7, &filename, 250);
|
||||
@@ -54,7 +54,7 @@ fn waveform_extract_and_cache_hit() {
|
||||
fn minmax_layout_is_two_f32s() {
|
||||
assert_eq!(std::mem::size_of::<MinMax>(), 8);
|
||||
assert_eq!(
|
||||
std::mem::size_of::<oakaudio::waveform::SamplePerChannel>(),
|
||||
std::mem::size_of::<oak_audio::waveform::SamplePerChannel>(),
|
||||
8
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "oakaudio"
|
||||
version = "0.1.0"
|
||||
name = "oak-audio"
|
||||
version.workspace = true
|
||||
edition = "2021"
|
||||
description = "Oak Video Editor audio I/O, processing, synchronization and waveform engine (Rust)"
|
||||
license = "GPL-3.0-or-later"
|
||||
@@ -9,13 +9,13 @@ license = "GPL-3.0-or-later"
|
||||
crate-type = ["staticlib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
oakffmpeg-link = { path = "../oakffmpeg-link" }
|
||||
oakcore-rs = { path = "../oakcore" }
|
||||
oakcommon = { path = "../oakcommon" }
|
||||
oakcodec = { path = "../oakcodec" }
|
||||
oak-ffmpeg-link = { path = "../oak-ffmpeg-link" }
|
||||
oak-core = { path = "../oak-core" }
|
||||
oak-common = { path = "../oak-common" }
|
||||
oak-codec = { path = "../oak-codec" }
|
||||
# Real resample/channel-convert/time-stretch filter graph. The C++
|
||||
# ffmpeg_bridge library existed only to absorb FFmpeg API churn; the Rust
|
||||
# crate calls ffmpeg-next directly (same as oakcodec). The oakffmpeg-link
|
||||
# crate calls ffmpeg-next directly (same as oakcodec). The oak-ffmpeg-link
|
||||
# dependency above emits the transitive link flags of the static FFmpeg.
|
||||
ffmpeg-next = "9"
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
use std::error::Error;
|
||||
use std::ffi::CString;
|
||||
use std::str::FromStr;
|
||||
use oakcommon::configstore::*;
|
||||
use oak_common::configstore::*;
|
||||
/// PortAudio output buffer size in frames; 0 = let PortAudio choose.
|
||||
///
|
||||
/// `// CPP-PARITY: src/audio/src/configbridge.cpp:30`
|
||||
@@ -46,4 +46,4 @@ pub mod waveformsync;
|
||||
// link flags (the static FFmpeg's transitive dependencies) reach the
|
||||
// final link — rustc prunes the flags of an unreferenced rlib.
|
||||
#[used]
|
||||
static FORCE_FFMPEG_LINK: fn() = oakffmpeg_link::force_link;
|
||||
static FORCE_FFMPEG_LINK: fn() = oak_ffmpeg_link::force_link;
|
||||
@@ -29,8 +29,8 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex, MutexGuard, OnceLock};
|
||||
use cpal::{Device, DeviceId};
|
||||
use cpal::traits::{DeviceTrait, HostTrait};
|
||||
use oakcodec::encoder::Encoder;
|
||||
use oakcodec::encodingparams::EncodingParams;
|
||||
use oak_codec::encoder::Encoder;
|
||||
use oak_codec::encodingparams::EncodingParams;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::params::AudioParams;
|
||||
use crate::previewdevice::PreviewAudioDevice;
|
||||
@@ -290,7 +290,7 @@ impl ManagerInner {
|
||||
if self.input_device == PA_NO_DEVICE {
|
||||
return Err(Box::new(Error::Failed("no input device".to_string())));
|
||||
}
|
||||
let encoder = oakcodec::encoder::create_from_params(params)
|
||||
let encoder = oak_codec::encoder::create_from_params(params)
|
||||
.ok_or_else(|| Error::Failed("failed to create encoder for recording".to_string()))?;
|
||||
encoder
|
||||
.configure(params)
|
||||
@@ -22,12 +22,12 @@
|
||||
//! These integer values cross the C ABI as `int`, so they MUST match the
|
||||
//! authoritative C++ enums bit-for-bit.
|
||||
|
||||
use oakcore_rs::Rational;
|
||||
use oak_core::Rational;
|
||||
|
||||
/// Audio sample format: re-exported from oakcore-rs (planar-first,
|
||||
/// values identical to `olive::core::SampleFormat::Format`).
|
||||
/// `// CPP-PARITY: core/include/olive/core/render/sampleformat.h:33`
|
||||
pub use oakcore_rs::SampleFormat;
|
||||
pub use oak_core::SampleFormat;
|
||||
|
||||
/// Audio stream parameters, mirroring `olive::core::AudioParams`
|
||||
/// (core/include/olive/core/render/audioparams.h). A plain value type;
|
||||
@@ -18,7 +18,7 @@
|
||||
//! C++; becomes a plain function module. Times are `core::Rational` (from
|
||||
//! oakcore-rs).
|
||||
|
||||
use oakcore_rs::Rational;
|
||||
use oak_core::Rational;
|
||||
|
||||
use crate::params::rational_from_double;
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::ffi::CStr;
|
||||
|
||||
use oakcodec::decoder::{receive_list_of_all_decoders, CodecStream, Decoder as _, RetrieveAudioStatus};
|
||||
use oakcodec::ffmpeg::FFmpegDecoder;
|
||||
use oakcodec::footagedescription::FootageDescription;
|
||||
use oakcore_rs::{Rational, TimeRange};
|
||||
use oak_codec::decoder::{receive_list_of_all_decoders, CodecStream, Decoder as _, RetrieveAudioStatus};
|
||||
use oak_codec::ffmpeg::FFmpegDecoder;
|
||||
use oak_codec::footagedescription::FootageDescription;
|
||||
use oak_core::{Rational, TimeRange};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
|
||||
@@ -22,9 +22,9 @@ mod common;
|
||||
use std::ffi::CString;
|
||||
|
||||
use common::write_wav_header_only;
|
||||
use oakcore_rs::Rational;
|
||||
use oakaudio::params::{frames_to_rational, rational_to_samples, AudioParams, SampleFormat};
|
||||
use oakaudio::waveform::{extract, AudioVisualWaveform};
|
||||
use oak_core::Rational;
|
||||
use oak_audio::params::{frames_to_rational, rational_to_samples, AudioParams, SampleFormat};
|
||||
use oak_audio::waveform::{extract, AudioVisualWaveform};
|
||||
|
||||
/// SampleFormat planar-first ordering matches the authoritative C++ enum:
|
||||
/// f32_p == 4 == OAKAUDIO_PROCESSOR_OUTPUT_FORMAT. This guards the
|
||||
@@ -32,7 +32,7 @@ use oakaudio::waveform::{extract, AudioVisualWaveform};
|
||||
#[test]
|
||||
fn sample_format_planar_first_ordering() {
|
||||
assert_eq!(SampleFormat::F32Planar as i32, 4);
|
||||
assert_eq!(oakaudio::processor::OUTPUT_FORMAT as i32, 4);
|
||||
assert_eq!(oak_audio::processor::OUTPUT_FORMAT as i32, 4);
|
||||
// Invalid is -1 and the packed family follows planar-first.
|
||||
assert_eq!(SampleFormat::Invalid as i32, -1);
|
||||
assert_eq!(SampleFormat::U8Planar as i32, 0);
|
||||
@@ -57,7 +57,7 @@ fn sample_time_conversion_roundtrip() {
|
||||
/// rational conversion edge cases (NaN / out-of-range / tiny -> null).
|
||||
#[test]
|
||||
fn params_value_types() {
|
||||
use oakaudio::params::rational_from_double;
|
||||
use oak_audio::params::rational_from_double;
|
||||
|
||||
let p = AudioParams {
|
||||
sample_rate: 48000,
|
||||
@@ -136,14 +136,14 @@ fn waveform_mipmap_scale_parity() {
|
||||
fn levelmeter_db_golden() {
|
||||
let tone = common::planar_from(&[0.5f32; 64], 1);
|
||||
let refs: Vec<&[f32]> = tone.iter().map(|v| v.as_slice()).collect();
|
||||
let stats = oakaudio::levelmeter::analyze_sample_buffer(&refs);
|
||||
let stats = oak_audio::levelmeter::analyze_sample_buffer(&refs);
|
||||
let expected_db = 20.0 * 0.5f64.log10();
|
||||
assert!((stats.channels[0].peak_db - expected_db).abs() < 1e-9);
|
||||
assert!((stats.channels[0].rms_db - expected_db).abs() < 1e-9);
|
||||
|
||||
let silence = common::silence_planar(1, 64);
|
||||
let refs: Vec<&[f32]> = silence.iter().map(|v| v.as_slice()).collect();
|
||||
let stats = oakaudio::levelmeter::analyze_sample_buffer(&refs);
|
||||
let stats = oak_audio::levelmeter::analyze_sample_buffer(&refs);
|
||||
assert_eq!(stats.channels[0].peak_db, -200.0);
|
||||
assert_eq!(stats.channels[0].rms_db, -200.0);
|
||||
assert_eq!(stats.channels[0].vu_db, -200.0);
|
||||
@@ -157,7 +157,7 @@ fn waveform_sync_offset_golden() {
|
||||
let reference: Vec<f64> = (0..10).map(|i| i as f64 * 0.1 + 0.1).collect();
|
||||
let mut candidate = vec![0.0f64; 10];
|
||||
candidate[2..].copy_from_slice(&reference[..8]);
|
||||
let out = oakaudio::waveformsync::estimate_envelope_offset(&reference, &candidate, 100, 10);
|
||||
let out = oak_audio::waveformsync::estimate_envelope_offset(&reference, &candidate, 100, 10);
|
||||
assert!(out.valid);
|
||||
assert_eq!(out.offset_samples, 200);
|
||||
assert!((out.confidence - 1.0).abs() < 1e-9);
|
||||
+1
-1
@@ -19,7 +19,7 @@
|
||||
|
||||
mod common;
|
||||
|
||||
use oakaudio::levelmeter::{analyze_sample_buffer, Stats};
|
||||
use oak_audio::levelmeter::{analyze_sample_buffer, Stats};
|
||||
|
||||
fn analyze(planes: &[Vec<f32>]) -> Stats {
|
||||
let refs: Vec<&[f32]> = planes.iter().map(Vec::as_slice).collect();
|
||||
@@ -21,10 +21,10 @@
|
||||
mod common;
|
||||
|
||||
use common::lock_manager;
|
||||
use oakcodec::encodingparams::EncodingParams;
|
||||
use oakaudio::error::{Error, OAKAUDIO_E_INVALID};
|
||||
use oakaudio::manager::instance;
|
||||
use oakaudio::params::{AudioParams, SampleFormat};
|
||||
use oak_codec::encodingparams::EncodingParams;
|
||||
use oak_audio::error::{Error, OAKAUDIO_E_INVALID};
|
||||
use oak_audio::manager::instance;
|
||||
use oak_audio::params::{AudioParams, SampleFormat};
|
||||
|
||||
/// Audio params for the tests: stereo f32 at 48 kHz.
|
||||
fn stereo() -> AudioParams {
|
||||
@@ -55,15 +55,15 @@ fn wav_params(filename: &str) -> EncodingParams {
|
||||
#[test]
|
||||
fn singleton_lifecycle() {
|
||||
let _guard = lock_manager();
|
||||
oakaudio::manager::ManagerInner::destroy_instance();
|
||||
oak_audio::manager::ManagerInner::destroy_instance();
|
||||
assert!(instance().is_none());
|
||||
|
||||
oakaudio::manager::ManagerInner::create_instance().unwrap();
|
||||
oak_audio::manager::ManagerInner::create_instance().unwrap();
|
||||
assert!(instance().is_some());
|
||||
|
||||
oakaudio::manager::ManagerInner::destroy_instance();
|
||||
oak_audio::manager::ManagerInner::destroy_instance();
|
||||
assert!(instance().is_none());
|
||||
oakaudio::manager::ManagerInner::create_instance().unwrap();
|
||||
oak_audio::manager::ManagerInner::create_instance().unwrap();
|
||||
assert!(instance().is_some());
|
||||
}
|
||||
|
||||
@@ -71,8 +71,8 @@ fn singleton_lifecycle() {
|
||||
/// (`output_started`, buffered params) that earlier tests in this binary
|
||||
/// may have left behind.
|
||||
fn fresh_instance() {
|
||||
oakaudio::manager::ManagerInner::destroy_instance();
|
||||
oakaudio::manager::ManagerInner::create_instance().unwrap();
|
||||
oak_audio::manager::ManagerInner::destroy_instance();
|
||||
oak_audio::manager::ManagerInner::create_instance().unwrap();
|
||||
}
|
||||
|
||||
/// push_to_output accepts raw interleaved bytes and starts the virtual
|
||||
@@ -142,7 +142,7 @@ fn output_control_flags() {
|
||||
m.set_output_notify_interval(1024).unwrap();
|
||||
let err = m.set_output_notify_interval(-1).unwrap_err();
|
||||
assert_eq!(
|
||||
err.downcast_ref::<oakaudio::error::Error>().map(|e| e.code()),
|
||||
err.downcast_ref::<oak_audio::error::Error>().map(|e| e.code()),
|
||||
Some(OAKAUDIO_E_INVALID)
|
||||
);
|
||||
m.clear_buffered_output().unwrap();
|
||||
@@ -192,15 +192,15 @@ fn recording_start_stop() {
|
||||
/// falls back to 0 and device names are absent.
|
||||
#[test]
|
||||
fn config_defaults() {
|
||||
assert_eq!(oakaudio::config::output_buffer_size(), 0);
|
||||
assert!(oakaudio::config::device_name(true).is_err(), "no configured output device");
|
||||
assert!(oakaudio::config::device_name(false).is_err(), "no configured input device");
|
||||
assert_eq!(oak_audio::config::output_buffer_size(), 0);
|
||||
assert!(oak_audio::config::device_name(true).is_err(), "no configured output device");
|
||||
assert!(oak_audio::config::device_name(false).is_err(), "no configured input device");
|
||||
}
|
||||
|
||||
/// PreviewAudioDevice pull-side plumbing (read/notify callback/clock).
|
||||
#[test]
|
||||
fn preview_device_pull_side() {
|
||||
use oakaudio::previewdevice::PreviewAudioDevice;
|
||||
use oak_audio::previewdevice::PreviewAudioDevice;
|
||||
|
||||
let mut dev = PreviewAudioDevice::new();
|
||||
dev.set_params(AudioParams {
|
||||
@@ -288,7 +288,7 @@ fn output_levels_reports_buffered_peaks() {
|
||||
|
||||
// The error mapping is intact.
|
||||
assert_eq!(Error::Invalid.code(), OAKAUDIO_E_INVALID);
|
||||
assert_eq!(Error::Failed("x".to_string()).code(), oakaudio::error::OAKAUDIO_E_FAILED);
|
||||
assert_eq!(Error::Failed("x".to_string()).code(), oak_audio::error::OAKAUDIO_E_FAILED);
|
||||
|
||||
// Leave the singleton as we found it: the push flipped
|
||||
// `output_started`, which other tests' seconds() assertions depend on.
|
||||
@@ -20,9 +20,9 @@
|
||||
//! latency: the exact frame counts are drained after `flush`, while
|
||||
//! identity conversion is an immediate passthrough.
|
||||
|
||||
use oakaudio::error::{Error, OAKAUDIO_E_INVALID, OAKAUDIO_E_STATE, OAKAUDIO_OK};
|
||||
use oakaudio::params::{AudioParams, SampleFormat};
|
||||
use oakaudio::processor::Processor;
|
||||
use oak_audio::error::{Error, OAKAUDIO_E_INVALID, OAKAUDIO_E_STATE, OAKAUDIO_OK};
|
||||
use oak_audio::params::{AudioParams, SampleFormat};
|
||||
use oak_audio::processor::Processor;
|
||||
|
||||
/// Stereo f32_p planes of `frames` ramp samples.
|
||||
fn ramp_planes(frames: usize) -> Vec<Vec<f32>> {
|
||||
@@ -57,7 +57,7 @@ fn plane_mut_ptrs(planes: &mut [Vec<f32>]) -> Vec<*mut f32> {
|
||||
/// The crate error code of a failed call (the API surfaces
|
||||
/// `Box<dyn std::error::Error>`).
|
||||
fn code(err: Box<dyn std::error::Error>) -> i32 {
|
||||
err.downcast_ref::<oakaudio::error::Error>()
|
||||
err.downcast_ref::<oak_audio::error::Error>()
|
||||
.map(|e| e.code())
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
@@ -237,5 +237,5 @@ fn error_codes() {
|
||||
assert_eq!(Error::Invalid.code(), OAKAUDIO_E_INVALID);
|
||||
assert_eq!(Error::State.code(), OAKAUDIO_E_STATE);
|
||||
assert_eq!(Error::Failed("x".to_string()).code(), -60003);
|
||||
assert_eq!(oakaudio::error::OAKAUDIO_OK, OAKAUDIO_OK);
|
||||
assert_eq!(oak_audio::error::OAKAUDIO_OK, OAKAUDIO_OK);
|
||||
}
|
||||
@@ -19,9 +19,9 @@
|
||||
|
||||
mod common;
|
||||
|
||||
use oakcore_rs::Rational;
|
||||
use oakaudio::synchronizer::{place_by_source_time, place_by_waveform_offset, SourceClip};
|
||||
use oakaudio::waveformsync::{
|
||||
use oak_core::Rational;
|
||||
use oak_audio::synchronizer::{place_by_source_time, place_by_waveform_offset, SourceClip};
|
||||
use oak_audio::waveformsync::{
|
||||
estimate_envelope_offset, estimate_envelope_offset_valid, estimate_stretch_and_offset,
|
||||
extract_rms_envelope,
|
||||
};
|
||||
@@ -217,7 +217,7 @@ fn crate_level_unmasked_wrappers() {
|
||||
let mut candidate = vec![0.0f64; 10];
|
||||
candidate[2..].copy_from_slice(&reference[..8]);
|
||||
|
||||
let env = oakaudio::waveformsync::estimate_envelope_offset(&reference, &candidate, 100, 10);
|
||||
let env = oak_audio::waveformsync::estimate_envelope_offset(&reference, &candidate, 100, 10);
|
||||
assert!(env.valid);
|
||||
assert_eq!(env.offset_samples, 200);
|
||||
assert!((env.confidence - 1.0).abs() < 1e-9);
|
||||
@@ -227,7 +227,7 @@ fn crate_level_unmasked_wrappers() {
|
||||
let ref_samples: Vec<f32> = (0..1000).map(|i| reference[i / 100] as f32).collect();
|
||||
let mut cand_samples = vec![0.0f32; 1000];
|
||||
cand_samples[200..].copy_from_slice(&ref_samples[..800]);
|
||||
let raw = oakaudio::waveformsync::estimate_offset(
|
||||
let raw = oak_audio::waveformsync::estimate_offset(
|
||||
&[ref_samples.as_slice()],
|
||||
&[cand_samples.as_slice()],
|
||||
100,
|
||||
@@ -22,8 +22,8 @@ mod common;
|
||||
use std::ffi::CString;
|
||||
|
||||
use common::write_wav;
|
||||
use oakcore_rs::Rational;
|
||||
use oakaudio::waveform::{extract, AudioVisualWaveform, SamplePerChannel};
|
||||
use oak_core::Rational;
|
||||
use oak_audio::waveform::{extract, AudioVisualWaveform, SamplePerChannel};
|
||||
|
||||
/// Fill `w` with 100 samples/channel of a 0..0.99 ramp at 100 Hz (1 s).
|
||||
fn fill_ramp(w: &mut AudioVisualWaveform) {
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
[package]
|
||||
name = "oak-cli"
|
||||
version = "0.1.0"
|
||||
version.workspace = true
|
||||
edition = "2021"
|
||||
description = "Oak Video Editor headless command-line consumer of the oak editor module crates (Rust)"
|
||||
license = "GPL-3.0-or-later"
|
||||
@@ -34,10 +34,10 @@ clap = { version = "4", features = ["derive"] }
|
||||
# oakcodec for the export formats/codecs, oaktask for the export task,
|
||||
# oakcommon/oakcore-rs for the shared value types). No liboakengine dylib,
|
||||
# no C ABI, no build.rs link step, no host shims.
|
||||
oakcommon = { path = "../oakcommon" }
|
||||
oakcore-rs = { path = "../oakcore" }
|
||||
oaknode = { path = "../oaknode" }
|
||||
oaktimeline = { path = "../oaktimeline" }
|
||||
oakcodec = { path = "../oakcodec" }
|
||||
oakrender = { path = "../oakrender" }
|
||||
oaktask = { path = "../oaktask" }
|
||||
oak-common = { path = "../oak-common" }
|
||||
oak-core = { path = "../oak-core" }
|
||||
oak-node = { path = "../oak-node" }
|
||||
oak-timeline = { path = "../oak-timeline" }
|
||||
oak-codec = { path = "../oak-codec" }
|
||||
oak-render = { path = "../oak-render" }
|
||||
oak-task = { path = "../oak-task" }
|
||||
|
||||
@@ -124,7 +124,7 @@ fn run_info(project: &str) -> i32 {
|
||||
|
||||
/// Print one sequence block (`print_sequence` in cli/main.cpp) through the
|
||||
/// module sequence queries.
|
||||
fn print_sequence(project: &engine::ProjectRef, seq_id: oaknode::id::NodeId, index: i64) {
|
||||
fn print_sequence(project: &engine::ProjectRef, seq_id: oak_node::id::NodeId, index: i64) {
|
||||
let (name, length, frame_rate, track_counts, playhead) = {
|
||||
let guard = project.lock().unwrap_or_else(|e| e.into_inner());
|
||||
(
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
//! ([`crate::engine`] + the modules directly) — M14 R2 cut the facade
|
||||
//! dylib out of this crate:
|
||||
//!
|
||||
//! - `probe` → an `oaknode::footage::FootageBehavior` probe
|
||||
//! - `probe` → an `oak_node::footage::FootageBehavior` probe
|
||||
//! - `info` → `crate::engine::load_project` + the project graph
|
||||
//! walks
|
||||
//! - `render` → `crate::engine::render_manager_init` + the video/
|
||||
|
||||
@@ -19,13 +19,13 @@
|
||||
//! cli/main.cpp).
|
||||
//!
|
||||
//! Runs entirely through the module crates (M14 R2): an
|
||||
//! [`oaknode::footage::FootageBehavior`] probes the file through the
|
||||
//! [`oak_node::footage::FootageBehavior`] probes the file through the
|
||||
//! oakcodec decoder registry and the CLI prints what the module records:
|
||||
//! the decoder id, the footage duration and the probed stream inventory
|
||||
//! (per-stream duration in rational seconds). A missing file prints
|
||||
//! `error: probe: file does not exist: <path>` on stderr and exits 1.
|
||||
|
||||
use oaknode::footage::FootageBehavior;
|
||||
use oak_node::footage::FootageBehavior;
|
||||
|
||||
use crate::cmd::{EXIT_ERROR, EXIT_OK};
|
||||
use crate::fmt;
|
||||
@@ -114,7 +114,7 @@ fn run_probe(mediafile: &str) -> i32 {
|
||||
}
|
||||
|
||||
/// A rational as floating-point seconds (0 on a zero denominator).
|
||||
fn rational_secs(r: oakcore_rs::Rational) -> f64 {
|
||||
fn rational_secs(r: oak_core::Rational) -> f64 {
|
||||
if r.denominator() != 0 {
|
||||
r.numerator() as f64 / r.denominator() as f64
|
||||
} else {
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
//! project/sequence/argument failures exit 1; bad seconds exit 64
|
||||
//! (usage). Frame progress goes to stderr (`frame N: T s`).
|
||||
|
||||
use oakcore_rs::TimeRange;
|
||||
use oak_core::TimeRange;
|
||||
|
||||
use crate::cmd::{EXIT_ERROR, EXIT_OK, EXIT_RENDER_UNAVAILABLE, EXIT_USAGE};
|
||||
use crate::engine;
|
||||
@@ -148,7 +148,7 @@ fn run_render(project: &str, start: f64, end: f64, out_dir: &str) -> i32 {
|
||||
if time >= end {
|
||||
break;
|
||||
}
|
||||
let time_r = oakcore_rs::Rational::new(index * i64::from(fr_den), i64::from(fr_num));
|
||||
let time_r = oak_core::Rational::new(index * i64::from(fr_den), i64::from(fr_num));
|
||||
let montage = engine::video_montage(&project_ref, seq_id, time_r);
|
||||
let frame = match engine::render_frame(seq_id, time_r, montage, width, height) {
|
||||
Ok(f) => f,
|
||||
@@ -183,8 +183,8 @@ fn run_render(project: &str, start: f64, end: f64, out_dir: &str) -> i32 {
|
||||
let start_ts = (start * fr_num as f64 / fr_den as f64).round() as i64;
|
||||
let length_ts = ((end - start) * fr_num as f64 / fr_den as f64).round() as i64;
|
||||
let range = TimeRange::new(
|
||||
oakcore_rs::Rational::new(start_ts * i64::from(fr_den), i64::from(fr_num)),
|
||||
oakcore_rs::Rational::new(
|
||||
oak_core::Rational::new(start_ts * i64::from(fr_den), i64::from(fr_num)),
|
||||
oak_core::Rational::new(
|
||||
(start_ts + length_ts) * i64::from(fr_den),
|
||||
i64::from(fr_num),
|
||||
),
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
//! "media in, renders out" round trip (port of `cmd_transcode()` in
|
||||
//! cli/main.cpp), entirely through the module crates (M14 R2).
|
||||
//!
|
||||
//! The source is probed with an [`oaknode::footage::FootageBehavior`]
|
||||
//! The source is probed with an [`oak_node::footage::FootageBehavior`]
|
||||
//! (geometry / frame rate / duration), then a temporary sequence is
|
||||
//! assembled the same way the facade did: a scratch project holds the
|
||||
//! sequence (the facade's documented `oakengine_sequence_new` deviation),
|
||||
@@ -44,8 +44,8 @@
|
||||
//! duration-less sources. Failures exit 1 (general error); bad arguments
|
||||
//! exit 64.
|
||||
|
||||
use oaknode::footage::FootageBehavior;
|
||||
use oaknode::track::TrackType;
|
||||
use oak_node::footage::FootageBehavior;
|
||||
use oak_node::track::TrackType;
|
||||
|
||||
use crate::cmd::{EXIT_ERROR, EXIT_OK, EXIT_USAGE};
|
||||
use crate::engine;
|
||||
@@ -135,7 +135,7 @@ fn run_transcode(input: &str, out: &str, width: Option<&str>, is_ppm: bool) -> i
|
||||
/// streams, one audio track/clip).
|
||||
struct Assembly {
|
||||
project: engine::ProjectRef,
|
||||
sequence: oaknode::id::NodeId,
|
||||
sequence: oak_node::id::NodeId,
|
||||
}
|
||||
|
||||
/// Build the temporary sequence for the render/export stage.
|
||||
@@ -151,7 +151,7 @@ fn assemble_sequence(
|
||||
if let Err(e) = engine::render_manager_init() {
|
||||
return Err(format!("cannot initialize the render manager: {e}"));
|
||||
}
|
||||
let project = oaknode::project::Project::new();
|
||||
let project = oak_node::project::Project::new();
|
||||
let sequence = engine::create_sequence(&project, "transcode");
|
||||
engine::set_sequence_video_params(&project, sequence, out_w, out_h, fr_num, fr_den);
|
||||
|
||||
@@ -211,7 +211,7 @@ fn transcode_ppm(
|
||||
}
|
||||
|
||||
for i in 0..frames {
|
||||
let time = oakcore_rs::Rational::new(i * i64::from(fr_den), i64::from(fr_num));
|
||||
let time = oak_core::Rational::new(i * i64::from(fr_den), i64::from(fr_num));
|
||||
let montage = engine::video_montage(&assembly.project, assembly.sequence, time);
|
||||
let frame = match engine::render_frame(assembly.sequence, time, montage, out_w, out_h) {
|
||||
Ok(f) => f,
|
||||
@@ -290,9 +290,9 @@ fn write_audio_wav(
|
||||
fr_num: i32,
|
||||
fr_den: i32,
|
||||
) -> i32 {
|
||||
let range = oakcore_rs::TimeRange::new(
|
||||
oakcore_rs::Rational::new(0, 1),
|
||||
oakcore_rs::Rational::new(frames * i64::from(fr_den), i64::from(fr_num)),
|
||||
let range = oak_core::TimeRange::new(
|
||||
oak_core::Rational::new(0, 1),
|
||||
oak_core::Rational::new(frames * i64::from(fr_den), i64::from(fr_num)),
|
||||
);
|
||||
let montage = engine::audio_montage(&assembly.project, assembly.sequence, range);
|
||||
let audio = match engine::render_audio(assembly.sequence, range, montage) {
|
||||
|
||||
@@ -29,26 +29,26 @@
|
||||
//! `oakengine_renderer_render_frame`, ...) did over the same module APIs;
|
||||
//! the facade keeps its own copies for the frozen C ABI. Combinators that
|
||||
//! belong upstream (the effect-chain and timeline composites) are M14
|
||||
//! follow-up candidates for `oaknode::ops`; kept local until then.
|
||||
//! follow-up candidates for `oak_node::ops`; kept local until then.
|
||||
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
|
||||
use oakcore_rs::{Rational, TimeRange};
|
||||
use oaknode::block::{self, ClipBlockBehavior};
|
||||
use oaknode::footage::FootageBehavior;
|
||||
use oaknode::graph::Graph;
|
||||
use oaknode::id::NodeId;
|
||||
use oaknode::project::Project;
|
||||
use oaknode::sequence::SequenceBehavior;
|
||||
use oaknode::track::{TrackBehavior, TrackListBehavior, TrackType};
|
||||
use oaknode::value::VideoParams;
|
||||
use oaktimeline::undogeneral::TimelineAddTrackCommand;
|
||||
use oaktimeline::undopointer::TrackPlaceBlockCommand;
|
||||
use oaktimeline::util::NodeRef;
|
||||
use oakrender::manager::RenderManager;
|
||||
use oakrender::procpool::bgra8_to_rgba8;
|
||||
use oakrender::ticket::{
|
||||
use oak_core::{Rational, TimeRange};
|
||||
use oak_node::block::{self, ClipBlockBehavior};
|
||||
use oak_node::footage::FootageBehavior;
|
||||
use oak_node::graph::Graph;
|
||||
use oak_node::id::NodeId;
|
||||
use oak_node::project::Project;
|
||||
use oak_node::sequence::SequenceBehavior;
|
||||
use oak_node::track::{TrackBehavior, TrackListBehavior, TrackType};
|
||||
use oak_node::value::VideoParams;
|
||||
use oak_timeline::undogeneral::TimelineAddTrackCommand;
|
||||
use oak_timeline::undopointer::TrackPlaceBlockCommand;
|
||||
use oak_timeline::util::NodeRef;
|
||||
use oak_render::manager::RenderManager;
|
||||
use oak_render::procpool::bgra8_to_rgba8;
|
||||
use oak_render::ticket::{
|
||||
AudioTicketParams, MontageClip, TicketPayload, VideoTicketParams,
|
||||
};
|
||||
|
||||
@@ -71,7 +71,7 @@ fn lock(p: &ProjectRef) -> MutexGuard<'_, Project> {
|
||||
/// `oakengine_project_load`.
|
||||
pub fn load_project(path: &str) -> Result<ProjectRef, String> {
|
||||
let xml = std::fs::read_to_string(path).map_err(|e| e.to_string())?;
|
||||
let project = oaknode::serializer::load(&xml).map_err(|e| e.to_string())?;
|
||||
let project = oak_node::serializer::load(&xml).map_err(|e| e.to_string())?;
|
||||
let p = Path::new(path);
|
||||
let abs = if p.is_absolute() {
|
||||
p.to_path_buf()
|
||||
@@ -400,7 +400,7 @@ pub fn place_footage_clip(
|
||||
let footage_id = {
|
||||
let mut guard = lock(project);
|
||||
let (mut core, behavior) = FootageBehavior::create();
|
||||
core.set_standard_value("file_in", -1, oaknode::value::NodeValue::Text(filename.to_string()));
|
||||
core.set_standard_value("file_in", -1, oak_node::value::NodeValue::Text(filename.to_string()));
|
||||
let id = guard.graph.add_node(core, behavior);
|
||||
if let Some(f) = footage_behavior_mut(&mut guard.graph, id) {
|
||||
f.filename = filename.to_string();
|
||||
@@ -603,7 +603,7 @@ pub fn audio_montage(p: &ProjectRef, seq_id: NodeId, range: TimeRange) -> Vec<Mo
|
||||
pub fn render_manager_init() -> Result<(), String> {
|
||||
match RenderManager::init() {
|
||||
Ok(()) => Ok(()),
|
||||
Err(oakrender::error::Error::State) => Ok(()),
|
||||
Err(oak_render::error::Error::State) => Ok(()),
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
@@ -620,7 +620,7 @@ pub struct RenderedFrame {
|
||||
pub width: i32,
|
||||
/// Height in pixels.
|
||||
pub height: i32,
|
||||
/// Pixel format (`oakcore_rs::PixelFormat` as int).
|
||||
/// Pixel format (`oak_core::PixelFormat` as int).
|
||||
pub format: i32,
|
||||
/// Bytes per scanline (stride).
|
||||
pub linesize: i32,
|
||||
@@ -657,7 +657,7 @@ pub fn render_frame(
|
||||
m.tickets.wait(id).map_err(|e| e.to_string())?;
|
||||
let result = m.tickets.result(id).ok_or_else(|| "render ticket produced no result".to_string())?;
|
||||
match &result {
|
||||
Ok(TicketPayload::Video(oakrender::texture::Texture::Cpu(frame))) => {
|
||||
Ok(TicketPayload::Video(oak_render::texture::Texture::Cpu(frame))) => {
|
||||
Ok(RenderedFrame {
|
||||
width: frame.width,
|
||||
height: frame.height,
|
||||
@@ -679,7 +679,7 @@ pub fn render_frame(
|
||||
/// Copy a process-backend shm frame (BGRA8 slot) out into the CLI's
|
||||
/// RGBA8 u8 frame layout (format 0, 4 channels — the PPM writer's RGB
|
||||
/// order). Necessary copy: the CLI owns the pixels it writes to disk.
|
||||
fn shm_to_rendered_frame(frame: &oakrender::procpool::ShmFrameRef) -> RenderedFrame {
|
||||
fn shm_to_rendered_frame(frame: &oak_render::procpool::ShmFrameRef) -> RenderedFrame {
|
||||
let meta = &frame.meta;
|
||||
let pixels = frame
|
||||
.shm
|
||||
@@ -778,18 +778,18 @@ pub fn export_sequence(
|
||||
return Err("sequence has no valid frame rate".to_string());
|
||||
}
|
||||
let out_num = frames * i64::from(fr_den);
|
||||
let encoding = oaktask::export::EncodingParams {
|
||||
let encoding = oak_task::export::EncodingParams {
|
||||
filename: out.to_string(),
|
||||
format: oakcodec::exportformat::Format::MPEG4Video as i32,
|
||||
format: oak_codec::exportformat::Format::MPEG4Video as i32,
|
||||
video_enabled: true,
|
||||
video_codec: oakcodec::exportcodec::Codec::H264 as i32,
|
||||
video_codec: oak_codec::exportcodec::Codec::H264 as i32,
|
||||
video_width: width,
|
||||
video_height: height,
|
||||
video_time_base_num: fr_den,
|
||||
video_time_base_den: fr_num,
|
||||
video_pixel_format: 0,
|
||||
audio_enabled: true,
|
||||
audio_codec: oakcodec::exportcodec::Codec::AAC as i32,
|
||||
audio_codec: oak_codec::exportcodec::Codec::AAC as i32,
|
||||
audio_sample_rate: 48000,
|
||||
audio_channel_layout: 0x3,
|
||||
subtitles_enabled: false,
|
||||
@@ -801,8 +801,8 @@ pub fn export_sequence(
|
||||
custom_range_out_num: out_num as i32,
|
||||
custom_range_out_den: fr_num,
|
||||
};
|
||||
let inner = oaktask::export::ExportTask::new((project.clone(), seq_id), encoding);
|
||||
let mut driver = oaktask::task::Task::new("Exporting...", None);
|
||||
let inner = oak_task::export::ExportTask::new((project.clone(), seq_id), encoding);
|
||||
let mut driver = oak_task::task::Task::new("Exporting...", None);
|
||||
driver.set_behavior(Box::new(inner));
|
||||
driver.start().map_err(|_| {
|
||||
driver
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "oakcodec"
|
||||
version = "0.1.0"
|
||||
name = "oak-codec"
|
||||
version.workspace = true
|
||||
edition = "2021"
|
||||
description = "Oak Video Editor media codec module (Rust)"
|
||||
license = "GPL-3.0-or-later"
|
||||
@@ -9,9 +9,9 @@ license = "GPL-3.0-or-later"
|
||||
crate-type = ["staticlib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
oakcommon = { path = "../oakcommon" }
|
||||
oakffmpeg-link = { path = "../oakffmpeg-link" }
|
||||
oakcore-rs = { path = "../oakcore" }
|
||||
oak-common = { path = "../oak-common" }
|
||||
oak-ffmpeg-link = { path = "../oak-ffmpeg-link" }
|
||||
oak-core = { path = "../oak-core" }
|
||||
# Real media decode/encode. The C++ ffmpeg_bridge library existed only to
|
||||
# absorb FFmpeg API churn; the Rust crate calls ffmpeg-next directly.
|
||||
#
|
||||
@@ -22,7 +22,7 @@
|
||||
//! `Unavailable`. Deterministic per-channel filenames derive from the
|
||||
//! source + target audio params.
|
||||
|
||||
use oakcommon::filefunctions::FileFunctions;
|
||||
use oak_common::filefunctions::FileFunctions;
|
||||
use std::path::Path;
|
||||
|
||||
/// Conform state of one audio stream.
|
||||
@@ -20,14 +20,14 @@
|
||||
//! FFmpeg/OIIO subclasses become the [`Decoder`] trait (decision 2 in
|
||||
//! README.md); probe/dispatch lives on the registry functions at the
|
||||
//! bottom of this module. Audio is handled in raw interleaved-float
|
||||
//! buffers matching the C ABI, not `oakcore_rs::SampleBuffer` (which the
|
||||
//! buffers matching the C ABI, not `oak_core::SampleBuffer` (which the
|
||||
//! crate does not export).
|
||||
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex, OnceLock};
|
||||
|
||||
use oakcommon::cancelatom::CancelAtom;
|
||||
use oakcore_rs::{Rational, TimeRange};
|
||||
use oak_common::cancelatom::CancelAtom;
|
||||
use oak_core::{Rational, TimeRange};
|
||||
|
||||
use crate::footagedescription::FootageDescription;
|
||||
use crate::frame::Frame;
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
use std::sync::{Arc, Mutex, OnceLock};
|
||||
|
||||
use oakcore_rs::{PixelFormat, SampleFormat};
|
||||
use oak_core::{PixelFormat, SampleFormat};
|
||||
|
||||
use crate::encodingparams::EncodingParams;
|
||||
use crate::frame::Frame;
|
||||
@@ -21,7 +21,7 @@
|
||||
//! `include/codec/encoder.h`. The Rust struct mirrors the POD verbatim so
|
||||
//! `ffi.rs` can marshal it without translation.
|
||||
|
||||
use oakcore_rs::{PixelFormat, SampleFormat};
|
||||
use oak_core::{PixelFormat, SampleFormat};
|
||||
|
||||
/// `VideoParams::Interlacing` values.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
@@ -19,7 +19,7 @@
|
||||
//! Mirrors `src/codec/src/exportformat.h`. As with `ExportCodec`, the enum
|
||||
//! values are the serialized-file contract and must never be reordered.
|
||||
|
||||
use oakcore_rs::SampleFormat;
|
||||
use oak_core::SampleFormat;
|
||||
|
||||
use crate::exportcodec::Codec;
|
||||
|
||||
@@ -56,10 +56,10 @@ use ffmpeg::software::{resampling, scaling};
|
||||
use ffmpeg::{ChannelLayout, Dictionary, Error as FfmpegError, Rational as FfRational};
|
||||
use ffmpeg_next as ffmpeg;
|
||||
|
||||
use oakcommon::cancelatom::CancelAtom;
|
||||
use oakcommon::ocioutils::PixelFormat as OakPixelFormat;
|
||||
use oakcommon::videoparams::{Interlacing, VideoParams, VideoType};
|
||||
use oakcore_rs::{PixelFormat, Rational, SampleFormat, TimeRange};
|
||||
use oak_common::cancelatom::CancelAtom;
|
||||
use oak_common::ocioutils::PixelFormat as OakPixelFormat;
|
||||
use oak_common::videoparams::{Interlacing, VideoParams, VideoType};
|
||||
use oak_core::{PixelFormat, Rational, SampleFormat, TimeRange};
|
||||
|
||||
use crate::audioparams::AudioParams;
|
||||
use crate::decoder::{CodecStream, Decoder, OakRenderTexture, RetrieveAudioStatus, RetrieveVideoParams};
|
||||
@@ -2435,7 +2435,7 @@ fn c_string_1024(buf: &[u8; 1024]) -> String {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::decoder::RetrieveVideoParams;
|
||||
use oakcore_rs::Rational;
|
||||
use oak_core::Rational;
|
||||
|
||||
fn video_params() -> RetrieveVideoParams {
|
||||
RetrieveVideoParams {
|
||||
+5
-5
@@ -19,13 +19,13 @@
|
||||
//! Mirrors `src/codec/src/footagedescription.h`. A value type describing
|
||||
//! the streams a `Decoder::probe()` found in a file. Video and subtitle
|
||||
//! streams are stored as oakcommon by-value handles; audio streams as
|
||||
//! `oakcore_rs::TimeRangeList`/raw audio params. The original's
|
||||
//! `oak_core::TimeRangeList`/raw audio params. The original's
|
||||
//! `Track::Type` mapping and XML load/save are intentionally not reproduced
|
||||
//! (NOTES.md §4) — use [`FootageDescription::stream_is_video`] etc.
|
||||
|
||||
use oakcommon::subtitleparams::SubtitleParams;
|
||||
use oakcommon::videoparams::VideoParams;
|
||||
use oakcore_rs::{Rational, TimeRange};
|
||||
use oak_common::subtitleparams::SubtitleParams;
|
||||
use oak_common::videoparams::VideoParams;
|
||||
use oak_core::{Rational, TimeRange};
|
||||
|
||||
use crate::audioparams::AudioParams;
|
||||
|
||||
@@ -216,7 +216,7 @@ mod tests {
|
||||
let mut vp = VideoParams::new_basic(
|
||||
1920,
|
||||
1080,
|
||||
oakcommon::ocioutils::PixelFormat::from_code(0),
|
||||
oak_common::ocioutils::PixelFormat::from_code(0),
|
||||
4,
|
||||
1,
|
||||
1,
|
||||
@@ -22,8 +22,8 @@
|
||||
//! itself is a plain `Vec<u8>`. Line-size and pixel-format math lives
|
||||
//! here.
|
||||
|
||||
use oakcommon::videoparams::VideoParams;
|
||||
use oakcore_rs::{PixelFormat, Rational};
|
||||
use oak_common::videoparams::VideoParams;
|
||||
use oak_core::{PixelFormat, Rational};
|
||||
|
||||
/// Number of channels in the internal RGBA pipeline layout
|
||||
/// (`VideoParams::k_internal_channel_count == k_rgba_channel_count == 4`).
|
||||
@@ -331,7 +331,7 @@ impl Frame {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use oakcommon::ocioutils::PixelFormat as OakPixelFormat;
|
||||
use oak_common::ocioutils::PixelFormat as OakPixelFormat;
|
||||
|
||||
fn frame(w: i32, h: i32) -> Frame {
|
||||
let params = VideoParams::new_basic(w, h, OakPixelFormat::from_code(0), 4, 1, 1, 0, 1);
|
||||
@@ -422,7 +422,7 @@ mod tests {
|
||||
#[cfg(test)]
|
||||
mod tests_extra {
|
||||
use super::*;
|
||||
use oakcommon::ocioutils::PixelFormat as OakPixelFormat;
|
||||
use oak_common::ocioutils::PixelFormat as OakPixelFormat;
|
||||
|
||||
fn frame(w: i32, h: i32) -> Frame {
|
||||
let params = VideoParams::new_basic(w, h, OakPixelFormat::from_code(0), 4, 1, 1, 0, 1);
|
||||
@@ -27,7 +27,7 @@ use std::sync::{Arc, Mutex, OnceLock};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use oakcommon::videoparams::VideoParams;
|
||||
use oak_common::videoparams::VideoParams;
|
||||
use crate::frame::Frame;
|
||||
|
||||
/// `olive::FrameManager`: singleton frame pool with background GC.
|
||||
@@ -148,7 +148,7 @@ fn frame_matches(frame: &Frame, params: &VideoParams) -> bool {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use oakcommon::ocioutils::PixelFormat as OakPixelFormat;
|
||||
use oak_common::ocioutils::PixelFormat as OakPixelFormat;
|
||||
|
||||
fn test_params(w: i32, h: i32) -> VideoParams {
|
||||
VideoParams::new_basic(w, h, OakPixelFormat::from_code(0), 4, 1, 1, 0, 1)
|
||||
@@ -62,7 +62,7 @@ pub const CONFIG_KEY_HARDWARE_DECODING: &str = "HardwareDecoding";
|
||||
/// string accessor, same convention as the app's config helpers — the
|
||||
/// store's typed `get_bool` only parses pre-typed Bool entries).
|
||||
pub fn hardware_decoding_enabled() -> bool {
|
||||
match oakcommon::configstore::ConfigStore::instance()
|
||||
match oak_common::configstore::ConfigStore::instance()
|
||||
.get(None, CONFIG_KEY_HARDWARE_DECODING)
|
||||
{
|
||||
Ok(value) => value != "false",
|
||||
@@ -107,4 +107,4 @@ pub(crate) fn lock_tests() -> TestLock {
|
||||
// link flags (the static FFmpeg's transitive dependencies) reach the
|
||||
// final link — rustc prunes the flags of an unreferenced rlib.
|
||||
#[used]
|
||||
static FORCE_FFMPEG_LINK: fn() = oakffmpeg_link::force_link;
|
||||
static FORCE_FFMPEG_LINK: fn() = oak_ffmpeg_link::force_link;
|
||||
@@ -60,7 +60,7 @@ impl Decoder for OIIODecoder {
|
||||
fn probe(
|
||||
&self,
|
||||
_filename: &str,
|
||||
_cancelled: Option<&oakcommon::cancelatom::CancelAtom>,
|
||||
_cancelled: Option<&oak_common::cancelatom::CancelAtom>,
|
||||
) -> Option<crate::footagedescription::FootageDescription> {
|
||||
// Probing is a dylib operation; without it we cannot report anything.
|
||||
None
|
||||
@@ -105,7 +105,7 @@ impl Decoder for OIIODecoder {
|
||||
fn retrieve_audio(
|
||||
&self,
|
||||
_dest: &mut [f32],
|
||||
_range: &oakcore_rs::TimeRange,
|
||||
_range: &oak_core::TimeRange,
|
||||
_sample_rate: i32,
|
||||
_channel_layout: u64,
|
||||
) -> crate::error::Result<crate::decoder::RetrieveAudioStatus> {
|
||||
@@ -118,7 +118,7 @@ impl Decoder for OIIODecoder {
|
||||
_sample_rate: i32,
|
||||
_channel_layout: u64,
|
||||
_sample_format: i32,
|
||||
_cancelled: Option<&oakcommon::cancelatom::CancelAtom>,
|
||||
_cancelled: Option<&oak_common::cancelatom::CancelAtom>,
|
||||
) -> crate::error::Result<()> {
|
||||
Err(crate::error::Error::Failed(Self::NOT_AVAILABLE.to_string()))
|
||||
}
|
||||
@@ -204,11 +204,11 @@ impl Encoder for OIIOEncoder {
|
||||
))
|
||||
}
|
||||
|
||||
fn desired_pixel_format(&self) -> Option<oakcore_rs::PixelFormat> {
|
||||
fn desired_pixel_format(&self) -> Option<oak_core::PixelFormat> {
|
||||
None
|
||||
}
|
||||
|
||||
fn desired_sample_format(&self) -> Option<oakcore_rs::SampleFormat> {
|
||||
fn desired_sample_format(&self) -> Option<oak_core::SampleFormat> {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::decoder::RetrieveVideoParams;
|
||||
use crate::encodingparams::EncodingParams;
|
||||
use oakcore_rs::{Rational, TimeRange};
|
||||
use oak_core::{Rational, TimeRange};
|
||||
|
||||
fn video_params() -> RetrieveVideoParams {
|
||||
RetrieveVideoParams {
|
||||
@@ -30,10 +30,10 @@
|
||||
//! timestamp and time base alongside the raw pixel rows, so a buffer can be
|
||||
//! turned back into an equivalent [`Frame`] without any external state.
|
||||
|
||||
use oakcommon::ocioutils::PixelFormat as OakPixelFormat;
|
||||
use oakcommon::videoparams::VideoParams;
|
||||
use oak_common::ocioutils::PixelFormat as OakPixelFormat;
|
||||
use oak_common::videoparams::VideoParams;
|
||||
use crate::frame::Frame;
|
||||
use oakcore_rs::Rational;
|
||||
use oak_core::Rational;
|
||||
use std::ffi::c_int;
|
||||
|
||||
/// Fixed header size, in bytes, of an OIIO frame buffer.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user