build(ffmpeg): static GPL FFmpeg 8.0 via project script + FFMPEG_DIR
- tooling/ffmpeg/build-ffmpeg.sh builds release/8.0 static+PIC into .cache/ffmpeg: GPL/version3, every free-license external codec lib probed via pkg-config (enabled when present), per-OS hardware acceleration (VideoToolbox/AudioToolbox, VAAPI/VDPAU/libdrm, D3D11VA/DXVA2/MediaFoundation, nvenc when ffnvcodec exists) - tooling/install-deps.sh installs those libraries on Homebrew / MSYS2 UCRT64 / Debian-Ubuntu / Fedora / Arch; nothing in the build sudo's - ffmpeg-next's own build feature is unusable (every crate-version to FFmpeg-release pairing is broken upstream: 9.0.0->FF9 AVCodec fields, 8.1.0->FF8.1 new enum variants, 8.0.0->FF8 FF_PROFILE rename), so ffmpeg-next 9 + FFmpeg 8.x headers via FFMPEG_DIR it is - new links-crate oakffmpeg-link emits the static FFmpeg's transitive link flags from its .pc files (cargo only propagates them from links crates, and rustc prunes the flags unless the rlib is referenced — hence the force_link statics) - docs/build.md updated for the Rust workspace flow
This commit is contained in:
Generated
+7
-1
@@ -2329,7 +2329,7 @@ dependencies = [
|
||||
"log",
|
||||
"presser",
|
||||
"thiserror 1.0.69",
|
||||
"windows 0.57.0",
|
||||
"windows 0.58.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3928,6 +3928,7 @@ dependencies = [
|
||||
"oakcodec",
|
||||
"oakcommon",
|
||||
"oakcore-rs",
|
||||
"oakffmpeg-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3936,6 +3937,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"ffmpeg-next",
|
||||
"oakcore-rs",
|
||||
"oakffmpeg-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3972,6 +3974,10 @@ dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oakffmpeg-link"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "oaknode"
|
||||
version = "0.1.0"
|
||||
|
||||
@@ -9,10 +9,12 @@ 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" }
|
||||
# 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).
|
||||
# crate calls ffmpeg-next directly (same as oakcodec). The oakffmpeg-link
|
||||
# dependency above emits the transitive link flags of the static FFmpeg.
|
||||
ffmpeg-next = "9"
|
||||
|
||||
@@ -43,3 +43,9 @@ pub mod processor;
|
||||
pub mod synchronizer;
|
||||
pub mod waveform;
|
||||
pub mod waveformsync;
|
||||
|
||||
// Keep the oakffmpeg-link rlib referenced so its build script's native
|
||||
// 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;
|
||||
|
||||
@@ -15,7 +15,20 @@ crate-type = ["staticlib", "rlib"]
|
||||
test-stubs = []
|
||||
|
||||
[dependencies]
|
||||
oakffmpeg-link = { path = "../oakffmpeg-link" }
|
||||
oakcore-rs = { path = "../oakcore" }
|
||||
# Real media decode/encode. The C++ ffmpeg_bridge library existed only to
|
||||
# absorb FFmpeg API churn; the Rust crate calls ffmpeg-next directly.
|
||||
ffmpeg-next = "9"
|
||||
#
|
||||
# ffmpeg-next 9.x pairs with FFmpeg 8.x headers, which most distros do
|
||||
# not ship yet — build a matching GPL FFmpeg (all free-license codecs +
|
||||
# per-OS hardware acceleration, static+PIC) with
|
||||
# tooling/ffmpeg/build-ffmpeg.sh and point cargo at it via FFMPEG_DIR
|
||||
# (see docs/build.md). The `static` feature makes the linker prefer the
|
||||
# archives.
|
||||
#
|
||||
# ffmpeg-next's own `build` feature is NOT usable: it clones
|
||||
# release/<crate-version>, and every such pairing is broken upstream
|
||||
# (9.0.0 -> FFmpeg 9.0 removed AVCodec fields; 8.1.0 -> FFmpeg 8.1 added
|
||||
# enum variants; 8.0.0 -> FFmpeg 8.0 renamed FF_PROFILE_*).
|
||||
ffmpeg-next = { version = "9.0", features = ["static"] }
|
||||
|
||||
@@ -52,3 +52,9 @@ pub mod timecodemetadata;
|
||||
|
||||
#[cfg(test)]
|
||||
mod realmedia_tests;
|
||||
|
||||
// Keep the oakffmpeg-link rlib referenced so its build script's native
|
||||
// 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;
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "oakffmpeg-link"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "Oak Video Editor internal link helper: transitive dependencies of a static FFmpeg (Rust)"
|
||||
license = "GPL-3.0-or-later"
|
||||
# A dedicated `links` crate so the emitted link directives propagate to
|
||||
# every dependent's final link (cargo only propagates rustc-link-* from
|
||||
# `links` crates). ffmpeg-sys-next already claims links = "ffmpeg".
|
||||
links = "oak_ffmpeg_static_deps"
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
@@ -0,0 +1,105 @@
|
||||
// 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/>.
|
||||
|
||||
//! Build-time link configuration for the `oakffmpeg-link` crate.
|
||||
//!
|
||||
//! ffmpeg-sys-next only emits the link directives for FFmpeg's own
|
||||
//! libraries. When FFmpeg is linked STATICALLY (the project FFmpeg built
|
||||
//! by tooling/ffmpeg/build-ffmpeg.sh, pointed at via `FFMPEG_DIR`), its
|
||||
//! archives reference every external codec library they were configured
|
||||
//! with (x264, x265, dav1d, ..., plus system frameworks on macOS), and
|
||||
//! the final link fails with undefined symbols unless those transitive
|
||||
//! dependencies are linked too. FFmpeg's own `.pc` files carry the exact
|
||||
//! list in `Libs.private`, so this script asks pkg-config for the static
|
||||
//! link line and forwards it to cargo.
|
||||
//!
|
||||
//! No-op when `FFMPEG_DIR` is unset (shared system FFmpeg carries its
|
||||
//! own transitive dependencies).
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
let Ok(dir) = std::env::var("FFMPEG_DIR") else {
|
||||
return;
|
||||
};
|
||||
let pc_dir = PathBuf::from(&dir).join("lib").join("pkgconfig");
|
||||
if !pc_dir.exists() {
|
||||
return;
|
||||
}
|
||||
println!("cargo:rerun-if-env-changed=FFMPEG_DIR");
|
||||
|
||||
let pkg_path = std::env::var("PKG_CONFIG_PATH").unwrap_or_default();
|
||||
let output = Command::new("pkg-config")
|
||||
.arg("--static")
|
||||
.arg("--libs")
|
||||
.args([
|
||||
"libavformat",
|
||||
"libavcodec",
|
||||
"libavfilter",
|
||||
"libavdevice",
|
||||
"libavutil",
|
||||
"libswscale",
|
||||
"libswresample",
|
||||
])
|
||||
.env(
|
||||
"PKG_CONFIG_PATH",
|
||||
format!("{}{}{}", pc_dir.display(), if pkg_path.is_empty() { "" } else { ":" }, pkg_path),
|
||||
)
|
||||
.output()
|
||||
.expect("pkg-config is required when FFMPEG_DIR is set");
|
||||
if !output.status.success() {
|
||||
panic!(
|
||||
"pkg-config --static --libs failed for the FFMPEG_DIR install: {}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
}
|
||||
|
||||
for token in String::from_utf8_lossy(&output.stdout).split_whitespace() {
|
||||
if let Some(path) = token.strip_prefix("-L") {
|
||||
println!("cargo:rustc-link-search=native={path}");
|
||||
} else if let Some(lib) = token.strip_prefix("-l") {
|
||||
// System libs (m, z, bz2, iconv, ...) and externals alike; the
|
||||
// linker picks .a or .dylib per -L search order.
|
||||
println!("cargo:rustc-link-lib={lib}");
|
||||
} else if token == "-framework" {
|
||||
// Handled on the next token (see below).
|
||||
} else if let Some(framework) = token.strip_prefix("-framework=") {
|
||||
println!("cargo:rustc-link-lib=framework={framework}");
|
||||
}
|
||||
// Bare tokens after "-framework" are handled by the stateful pass
|
||||
// below; pkg-config prints "-framework Foo" as two tokens.
|
||||
}
|
||||
// Second pass for the two-token "-framework Foo" form.
|
||||
let stdout = String::from_utf8_lossy(&output.stdout).into_owned();
|
||||
let tokens: Vec<&str> = stdout.split_whitespace().collect();
|
||||
for pair in tokens.windows(2) {
|
||||
if pair[0] == "-framework" {
|
||||
println!("cargo:rustc-link-lib=framework={}", pair[1]);
|
||||
}
|
||||
}
|
||||
|
||||
// Some externals (Homebrew lame, snappy) reach the FFmpeg archives via
|
||||
// the configure-time --extra-ldflags instead of Requires.private, so
|
||||
// their -L never shows up above; add the Homebrew lib dir as a
|
||||
// fallback search path.
|
||||
#[cfg(target_os = "macos")]
|
||||
for prefix in ["/opt/homebrew/lib", "/usr/local/lib"] {
|
||||
if std::path::Path::new(prefix).exists() {
|
||||
println!("cargo:rustc-link-search=native={prefix}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// 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/>.
|
||||
|
||||
//! Empty crate whose build script emits the transitive link flags of a
|
||||
//! static FFmpeg (see build.rs). Depend on it from every crate that uses
|
||||
//! `ffmpeg-next` so the final binary/test links.
|
||||
|
||||
/// Referenced by dependents (`#[used] static … = force_link`) so this
|
||||
/// rlib is never pruned as unused — the build script's native link flags
|
||||
/// only reach the final link while the rlib is linked.
|
||||
pub fn force_link() {}
|
||||
@@ -2,6 +2,36 @@
|
||||
|
||||
This document describes how to build Oak Video Editor from source on Windows, Linux, and macOS.
|
||||
|
||||
> **Note (2026):** Oak is now a Rust workspace; `cargo build` at the
|
||||
> repository root produces the app, the CLI, the worker and
|
||||
> `liboakengine`. The CMake instructions below are kept for historical
|
||||
> reference only. Rust dependencies are pulled from crates.io; the only
|
||||
> native libraries still needed are FFmpeg (see the next section),
|
||||
> OpenColorIO (optional; `ocio-sys` builds a stub without it) and a
|
||||
> C/C++ toolchain for the FFI shims.
|
||||
>
|
||||
> ### FFmpeg for the Rust build
|
||||
>
|
||||
> `ffmpeg-next` 9.x pairs with FFmpeg 8.x headers, which most distros do
|
||||
> not ship yet. Build a project-owned FFmpeg — GPL parts enabled, every
|
||||
> free-license external codec library that is installed, per-OS hardware
|
||||
> acceleration, static+PIC — with the project scripts:
|
||||
>
|
||||
> ```sh
|
||||
> tooling/install-deps.sh # Homebrew / MSYS2 UCRT64 / Debian / Fedora / Arch
|
||||
> tooling/ffmpeg/build-ffmpeg.sh # clones release/8.0, installs into .cache/ffmpeg
|
||||
> export FFMPEG_DIR="$(pwd)/.cache/ffmpeg"
|
||||
> cargo build
|
||||
> ```
|
||||
>
|
||||
> External libraries are probed with `pkg-config` and silently skipped
|
||||
> when missing. Unset `FFMPEG_DIR` to fall back to the system
|
||||
> `pkg-config` FFmpeg (must be 8.x for ffmpeg-next to compile).
|
||||
> ffmpeg-next's `build` cargo feature is deliberately not used: it
|
||||
> clones release/<crate-version>, and every such pairing is broken
|
||||
> upstream (9.0.0 → FFmpeg 9.0 removed AVCodec fields; 8.1.0 → FFmpeg
|
||||
> 8.1 added enum variants; 8.0.0 → FFmpeg 8.0 renamed FF_PROFILE_*).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- CMake 3.20+
|
||||
|
||||
Executable
+93
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env bash
|
||||
# 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/>.
|
||||
|
||||
# Installs the system dependencies of the Oak Rust workspace:
|
||||
# the free-license external codec/filter libraries FFmpeg is configured
|
||||
# with (see crates/oakcodec/Cargo.toml), plus the build tools. FFmpeg
|
||||
# itself is built from source by cargo (ffmpeg-next `build` feature) and
|
||||
# is NOT installed here.
|
||||
#
|
||||
# Supported: Homebrew (macOS), MSYS2 UCRT64 (Windows), Debian/Ubuntu,
|
||||
# Fedora, Arch. Run it yourself — nothing in the build invokes it
|
||||
# automatically (it needs sudo on Linux).
|
||||
#
|
||||
# Usage: tooling/install-deps.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
run() { echo "+ $*"; "$@"; }
|
||||
|
||||
if [[ "$OSTYPE" == msys* || "$OSTYPE" == cygwin* || -n "${MSYSTEM:-}" ]]; then
|
||||
if [ "${MSYSTEM:-}" != "UCRT64" ]; then
|
||||
echo "Please run this from the MSYS2 UCRT64 shell (MSYSTEM=$MSYSTEM)." >&2
|
||||
exit 1
|
||||
fi
|
||||
run pacman -S --needed --noconfirm \
|
||||
mingw-w64-ucrt-x86_64-toolchain mingw-w64-ucrt-x86_64-pkgconf \
|
||||
mingw-w64-ucrt-x86_64-nasm \
|
||||
mingw-w64-ucrt-x86_64-x264 mingw-w64-ucrt-x86_64-x265 \
|
||||
mingw-w64-ucrt-x86_64-dav1d mingw-w64-ucrt-x86_64-libvpx \
|
||||
mingw-w64-ucrt-x86_64-openh264 mingw-w64-ucrt-x86_64-openjpeg2 \
|
||||
mingw-w64-ucrt-x86_64-libtheora mingw-w64-ucrt-x86_64-libwebp \
|
||||
mingw-w64-ucrt-x86_64-lame mingw-w64-ucrt-x86_64-opus \
|
||||
mingw-w64-ucrt-x86_64-libvorbis mingw-w64-ucrt-x86_64-speex \
|
||||
mingw-w64-ucrt-x86_64-snappy mingw-w64-ucrt-x86_64-libass \
|
||||
mingw-w64-ucrt-x86_64-freetype mingw-w64-ucrt-x86_64-fribidi \
|
||||
mingw-w64-ucrt-x86_64-fontconfig mingw-w64-ucrt-x86_64-gnutls
|
||||
exit 0
|
||||
fi
|
||||
|
||||
case "$(uname -s)" in
|
||||
Darwin)
|
||||
run brew install pkg-config nasm \
|
||||
x264 x265 dav1d libvpx openh264 openjpeg libtheora libwebp \
|
||||
lame opus libvorbis speex snappy libass freetype fribidi \
|
||||
fontconfig gnutls
|
||||
;;
|
||||
Linux)
|
||||
if command -v apt-get >/dev/null; then
|
||||
run sudo apt-get update
|
||||
run sudo apt-get install -y build-essential pkg-config nasm \
|
||||
libx264-dev libx265-dev libdav1d-dev libvpx-dev \
|
||||
libopenh264-dev libopenjp2-7-dev libtheora-dev libwebp-dev \
|
||||
libmp3lame-dev libopus-dev libvorbis-dev libspeex-dev \
|
||||
libsnappy-dev libass-dev libfreetype-dev libfribidi-dev \
|
||||
libfontconfig-dev libgnutls28-dev
|
||||
elif command -v dnf >/dev/null; then
|
||||
run sudo dnf install -y gcc gcc-c++ pkgconf-pkg-config nasm \
|
||||
x264-devel x265-devel dav1d-devel libvpx-devel \
|
||||
openh264-devel openjpeg2-devel libtheora-devel libwebp-devel \
|
||||
lame-devel opus-devel libvorbis-devel speex-devel \
|
||||
snappy-devel libass-devel freetype-devel fribidi-devel \
|
||||
fontconfig-devel gnutls-devel
|
||||
elif command -v pacman >/dev/null; then
|
||||
run sudo pacman -S --needed --noconfirm base-devel pkgconf nasm \
|
||||
x264 x265 dav1d libvpx openh264 openjpeg2 libtheora libwebp \
|
||||
lame opus libvorbis speex snappy libass freetype2 fribidi \
|
||||
fontconfig gnutls
|
||||
else
|
||||
echo "Unsupported Linux distribution (need apt-get, dnf or pacman)." >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported platform: $(uname -s)" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "Done. 'cargo build' now compiles FFmpeg 8.1 from source with these libraries."
|
||||
Reference in New Issue
Block a user