Single mechanical restructure commit: - root Cargo.toml = oakapp bin + workspace; one cargo build produces oakapp, oak-cli, oak-worker, liboakengine.dylib - app/rust/src -> src/ (app at repo root, no rust/ nesting) - src/<mod>/rust -> crates/oak<mod>; src/oakcore-rs -> crates/oakcore; src/bindings/oakotio -> crates/oakotio; src/engine/rust -> crates/oakengine (keeps cdylib+staticlib+rlib) - public C headers include/<mod>/ -> crates/oakengine/include/<mod>/ - OFX SDK headers vendored into crates/oakplugin/ofx/ (HostSupport gone) - legacy deleted: old src/ C++ modules, engine/, core/, ffmpeg_bridge/, app/ (Qt), cli/worker C++, root CMakeLists, third_party/KDDockWidgets submodule, otio-install, all build-* output (~40GB) - oakstorage kept but excluded from the workspace (skeleton w/ todos); gpui excluded (own workspace) - verified: cargo build green, cargo test --workspace 1845/0 (with the documented OCIO_RS_* env override for the homebrew OCIO)
147 lines
4.4 KiB
Rust
147 lines
4.4 KiB
Rust
// 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/>.
|
|
|
|
//! CPU frame payloads and the [`VideoParamsPod`] value (the Rust mirror of
|
|
//! the `oakrender_video_params` POD in `include/render/renderer.h`).
|
|
//!
|
|
//! The C ABI frame functions (`oakrender_codec_frame_*`) marshal this
|
|
//! type; the FFI layer stores [`Frame`] values in `OakCodecFrame` handles.
|
|
|
|
use oakcore_rs::{PixelFormat, Rational};
|
|
|
|
/// Mirror of the `oakrender_video_params` POD (include/render/renderer.h,
|
|
/// field order and semantics verbatim). Stored inside [`crate::texture::Frame`]
|
|
/// so the ffi `*_get_params` exports can report the full metadata.
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
pub struct VideoParamsPod {
|
|
/// Frame width (full resolution).
|
|
pub width: i32,
|
|
/// Frame height (full resolution).
|
|
pub height: i32,
|
|
/// Frame duration numerator (e.g. 1001/30000 s).
|
|
pub time_base_num: i32,
|
|
/// Frame duration denominator.
|
|
pub time_base_den: i32,
|
|
/// `olive::PixelFormat::Format` as int.
|
|
pub format: i32,
|
|
/// Pixel aspect numerator.
|
|
pub pixel_aspect_num: i32,
|
|
/// Pixel aspect denominator.
|
|
pub pixel_aspect_den: i32,
|
|
/// `olive::VideoParams::Interlacing` as int.
|
|
pub interlacing: i32,
|
|
/// `olive::VideoParams::ColorRange` as int.
|
|
pub color_range: i32,
|
|
/// Preview resolution divider (1 = full).
|
|
pub divider: i32,
|
|
/// `olive::VideoParams::Type` (0 = video).
|
|
pub video_type: i32,
|
|
/// 0/1.
|
|
pub premultiplied_alpha: i32,
|
|
}
|
|
|
|
impl Default for VideoParamsPod {
|
|
fn default() -> Self {
|
|
Self {
|
|
width: 0,
|
|
height: 0,
|
|
time_base_num: 1,
|
|
time_base_den: 1,
|
|
format: PixelFormat::F32 as i32,
|
|
pixel_aspect_num: 1,
|
|
pixel_aspect_den: 1,
|
|
interlacing: 0,
|
|
color_range: 0,
|
|
divider: 1,
|
|
video_type: 0,
|
|
premultiplied_alpha: 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl VideoParamsPod {
|
|
/// The default render size used when a ticket carries no force size and
|
|
/// the output node's video params cannot be queried (oakcommon bridge
|
|
/// pending).
|
|
pub const DEFAULT_WIDTH: i32 = 1920;
|
|
/// See [`VideoParamsPod::DEFAULT_WIDTH`].
|
|
pub const DEFAULT_HEIGHT: i32 = 1080;
|
|
|
|
/// Frame rate as a rational (time base flipped; null for a null time
|
|
/// base).
|
|
pub fn frame_rate(&self) -> Rational {
|
|
if self.time_base_num <= 0 || self.time_base_den <= 0 {
|
|
return Rational::NULL;
|
|
}
|
|
Rational::new(self.time_base_den as i64, self.time_base_num as i64)
|
|
}
|
|
|
|
/// The time base (frame duration).
|
|
pub fn time_base(&self) -> Rational {
|
|
if self.time_base_num <= 0 || self.time_base_den <= 0 {
|
|
return Rational::NULL;
|
|
}
|
|
Rational::new(self.time_base_num as i64, self.time_base_den as i64)
|
|
}
|
|
|
|
/// The internal (fixed) channel count — the C++ engine uses 4 for the
|
|
/// video pipeline; not exposed in the POD.
|
|
pub const INTERNAL_CHANNEL_COUNT: i32 = 4;
|
|
|
|
/// Pixel dimensions of the data buffer honoring the preview divider
|
|
/// (C++ `VideoParams::effective_width/height` with a divider).
|
|
pub fn effective_width(&self) -> i32 {
|
|
(self.width / self.divider.max(1)).max(0)
|
|
}
|
|
|
|
/// See [`VideoParamsPod::effective_width`].
|
|
pub fn effective_height(&self) -> i32 {
|
|
(self.height / self.divider.max(1)).max(0)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn pod_defaults() {
|
|
let p = VideoParamsPod::default();
|
|
assert_eq!(p.format, PixelFormat::F32 as i32);
|
|
assert_eq!(p.time_base(), Rational::new(1, 1));
|
|
}
|
|
|
|
#[test]
|
|
fn effective_size_honors_divider() {
|
|
let mut p = VideoParamsPod::default();
|
|
p.width = 3840;
|
|
p.height = 2160;
|
|
p.divider = 2;
|
|
assert_eq!(p.effective_width(), 1920);
|
|
assert_eq!(p.effective_height(), 1080);
|
|
}
|
|
|
|
#[test]
|
|
fn frame_rate_is_flipped_time_base() {
|
|
let mut p = VideoParamsPod::default();
|
|
p.time_base_num = 1001;
|
|
p.time_base_den = 30000;
|
|
assert_eq!(p.frame_rate(), Rational::new(30000, 1001));
|
|
assert_eq!(p.time_base(), Rational::new(1001, 30000));
|
|
}
|
|
}
|