build: 启用显式 RKMPP 构建

This commit is contained in:
mofeng-git
2026-10-12 22:53:12 +08:00
parent 66a13d3d8c
commit 92f4203bc2
11 changed files with 52 additions and 45 deletions

View File

@@ -11,6 +11,7 @@ categories = ["embedded", "network-programming"]
[features] [features]
default = ["desktop"] default = ["desktop"]
rkmpp = ["hwcodec/rkmpp"]
desktop = [ desktop = [
"dep:tokio", "dep:tokio",
"dep:tokio-util", "dep:tokio-util",

View File

@@ -18,6 +18,10 @@ ARCH_MAP=(
# Build for specific architecture using cross # Build for specific architecture using cross
build_arch() { build_arch() {
local rust_target="$1" local rust_target="$1"
local feature_args=()
case "$rust_target" in
aarch64-*|armv7-*) feature_args=(--features rkmpp) ;;
esac
case "${CHINAMIRRO:-}" in case "${CHINAMIRRO:-}" in
1|true|TRUE|yes|YES|on|ON) 1|true|TRUE|yes|YES|on|ON)
@@ -40,13 +44,13 @@ build_arch() {
CARGO_NET_GIT_FETCH_WITH_CLI=true \ CARGO_NET_GIT_FETCH_WITH_CLI=true \
RUSTUP_DIST_SERVER="$rust_dist" \ RUSTUP_DIST_SERVER="$rust_dist" \
RUSTUP_UPDATE_ROOT="$rust_root" \ RUSTUP_UPDATE_ROOT="$rust_root" \
cross build --release --target "$rust_target" cross build --release --target "$rust_target" "${feature_args[@]}"
return return
;; ;;
esac esac
echo "=== Building: $rust_target (via cross with custom Dockerfile) ===" echo "=== Building: $rust_target (via cross with custom Dockerfile) ==="
cross build --release --target "$rust_target" cross build --release --target "$rust_target" "${feature_args[@]}"
} }
# Main # Main

View File

@@ -268,14 +268,18 @@ build_for_platform() {
if [ "$BUILD_BINARY" = true ]; then if [ "$BUILD_BINARY" = true ]; then
echo_info "Building binary with cross..." echo_info "Building binary with cross..."
cd "$PROJECT_ROOT" cd "$PROJECT_ROOT"
cross build --release --target "$target" local feature_args=()
case "$target" in
aarch64-*|armv7-*) feature_args=(--features rkmpp) ;;
esac
cross build --release --target "$target" "${feature_args[@]}"
fi fi
# Check binary exists # Check binary exists
local binary="$PROJECT_ROOT/target/$target/release/one-kvm" local binary="$PROJECT_ROOT/target/$target/release/one-kvm"
if [ ! -f "$binary" ]; then if [ ! -f "$binary" ]; then
echo_error "Binary not found: $binary" echo_error "Binary not found: $binary"
echo_error "Build it first: cross build --release --target $target" echo_error "Build it first: ./build/build-images.sh"
exit 1 exit 1
fi fi

View File

@@ -154,7 +154,7 @@ mod ffmpeg {
let include_dir = Path::new(&custom_lib_path).join("include"); let include_dir = Path::new(&custom_lib_path).join("include");
let avcodec_lib = lib_dir.join("libavcodec.a"); let avcodec_lib = lib_dir.join("libavcodec.a");
if avcodec_lib.exists() { if avcodec_lib.exists() && use_static {
println!("cargo:info=Using custom FFmpeg from {}", custom_lib_path); println!("cargo:info=Using custom FFmpeg from {}", custom_lib_path);
println!("cargo:rustc-link-search=native={}", lib_dir.display()); println!("cargo:rustc-link-search=native={}", lib_dir.display());
builder.include(&include_dir); builder.include(&include_dir);
@@ -171,7 +171,7 @@ mod ffmpeg {
println!("cargo:rustc-link-lib=va-drm"); println!("cargo:rustc-link-lib=va-drm");
println!("cargo:rustc-link-lib=va-x11"); // Required for vaGetDisplay println!("cargo:rustc-link-lib=va-x11"); // Required for vaGetDisplay
println!("cargo:rustc-link-lib=mfx"); println!("cargo:rustc-link-lib=mfx");
} else { } else if std::env::var_os("CARGO_FEATURE_RKMPP").is_some() {
for lib in ["rockchip_mpp", "rga"] { for lib in ["rockchip_mpp", "rga"] {
if !lib_dir.join(format!("lib{lib}.a")).exists() { if !lib_dir.join(format!("lib{lib}.a")).exists() {
panic!("missing static library: lib{lib}.a"); panic!("missing static library: lib{lib}.a");
@@ -400,8 +400,7 @@ mod ffmpeg {
// RKMPP decode only exists on ARM builds where FFmpeg is compiled with RKMPP support. // RKMPP decode only exists on ARM builds where FFmpeg is compiled with RKMPP support.
// Avoid compiling this file on x86/x64 where `AV_HWDEVICE_TYPE_RKMPP` doesn't exist. // Avoid compiling this file on x86/x64 where `AV_HWDEVICE_TYPE_RKMPP` doesn't exist.
let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default(); let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
let enable_rkmpp = matches!(target_arch.as_str(), "aarch64" | "arm") let enable_rkmpp = std::env::var_os("CARGO_FEATURE_RKMPP").is_some();
|| std::env::var_os("CARGO_FEATURE_RKMPP").is_some();
if enable_rkmpp { if enable_rkmpp {
builder.file(ffmpeg_ram_dir.join("ffmpeg_ram_decode.cpp")); builder.file(ffmpeg_ram_dir.join("ffmpeg_ram_decode.cpp"));
if enable_rkmpp { if enable_rkmpp {
@@ -459,8 +458,7 @@ mod ffmpeg {
.unwrap(); .unwrap();
let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default(); let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
let enable_rkmpp = matches!(target_arch.as_str(), "aarch64" | "arm") let enable_rkmpp = std::env::var_os("CARGO_FEATURE_RKMPP").is_some();
|| std::env::var_os("CARGO_FEATURE_RKMPP").is_some();
if enable_rkmpp { if enable_rkmpp {
// Include RGA headers for NV16->NV12 conversion (RGA im2d API) // Include RGA headers for NV16->NV12 conversion (RGA im2d API)
let rga_sys_dirs = [ let rga_sys_dirs = [

View File

@@ -9,12 +9,12 @@ use std::ffi::c_int;
include!(concat!(env!("OUT_DIR"), "/ffmpeg_ram_ffi.rs")); include!(concat!(env!("OUT_DIR"), "/ffmpeg_ram_ffi.rs"));
#[cfg(any(target_arch = "aarch64", target_arch = "arm", feature = "rkmpp"))] #[cfg(feature = "rkmpp")]
pub mod decode; pub mod decode;
// Provide a small stub on non-ARM builds so dependents can still compile, but decoder // Provide a small stub on non-ARM builds so dependents can still compile, but decoder
// construction will fail (since the C++ RKMPP decoder isn't built/linked). // construction will fail (since the C++ RKMPP decoder isn't built/linked).
#[cfg(not(any(target_arch = "aarch64", target_arch = "arm", feature = "rkmpp")))] #[cfg(not(feature = "rkmpp"))]
pub mod decode { pub mod decode {
use crate::ffmpeg::AVPixelFormat; use crate::ffmpeg::AVPixelFormat;

View File

@@ -2,12 +2,12 @@
pub mod capture; pub mod capture;
pub mod common; pub mod common;
pub mod ffmpeg; pub mod ffmpeg;
#[cfg(any(target_arch = "aarch64", target_arch = "arm", feature = "rkmpp"))] #[cfg(feature = "rkmpp")]
pub mod ffmpeg_hw; pub mod ffmpeg_hw;
pub mod ffmpeg_ram; pub mod ffmpeg_ram;
#[cfg(all( #[cfg(all(
target_os = "linux", target_os = "linux",
any(target_arch = "aarch64", target_arch = "arm", feature = "rkmpp") feature = "rkmpp"
))] ))]
pub mod rkmpp_dmabuf; pub mod rkmpp_dmabuf;

View File

@@ -3,7 +3,7 @@
use std::fs::File; use std::fs::File;
use std::io; use std::io;
use std::os::fd::AsFd; use std::os::fd::AsFd;
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))] #[cfg(feature = "rkmpp")]
use std::os::fd::OwnedFd; use std::os::fd::OwnedFd;
use std::os::unix::fs::OpenOptionsExt; use std::os::unix::fs::OpenOptionsExt;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@@ -31,7 +31,7 @@ use crate::video::device::VideoControlMode;
use crate::video::format::{PixelFormat, Resolution}; use crate::video::format::{PixelFormat, Resolution};
use crate::video::signal::SignalStatus; use crate::video::signal::SignalStatus;
#[cfg(any(test, target_arch = "aarch64", target_arch = "arm"))] #[cfg(any(test, feature = "rkmpp"))]
#[path = "dmabuf_layout.rs"] #[path = "dmabuf_layout.rs"]
mod dmabuf_layout; mod dmabuf_layout;
@@ -79,7 +79,7 @@ pub struct CaptureStream {
bridge_kind: Option<CsiBridgeKind>, bridge_kind: Option<CsiBridgeKind>,
native_hdmirx_state: Option<NativeHdmirxState>, native_hdmirx_state: Option<NativeHdmirxState>,
native_hdmirx_next_state_check: Option<Instant>, native_hdmirx_next_state_check: Option<Instant>,
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))] #[cfg(feature = "rkmpp")]
dma_layout_bytes: Option<usize>, dma_layout_bytes: Option<usize>,
} }
@@ -333,7 +333,7 @@ impl CaptureStream {
mappings.push(plane_maps); mappings.push(plane_maps);
} }
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))] #[cfg(feature = "rkmpp")]
let dma_layout_bytes = PixelFormat::from_v4l2r(actual_fmt.pixelformat).and_then(|format| { let dma_layout_bytes = PixelFormat::from_v4l2r(actual_fmt.pixelformat).and_then(|format| {
dmabuf_layout::DmaCaptureLayout { dmabuf_layout::DmaCaptureLayout {
native_hdmi: is_native_hdmirx, native_hdmi: is_native_hdmirx,
@@ -364,7 +364,7 @@ impl CaptureStream {
bridge_kind: bridge.kind, bridge_kind: bridge.kind,
native_hdmirx_state, native_hdmirx_state,
native_hdmirx_next_state_check, native_hdmirx_next_state_check,
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))] #[cfg(feature = "rkmpp")]
dma_layout_bytes, dma_layout_bytes,
}; };
@@ -491,7 +491,7 @@ impl CaptureStream {
/// Native HDMI NV12/BGR24 and single-planar USB UVC YUYV/NV12/RGB24/MJPEG. /// Native HDMI NV12/BGR24 and single-planar USB UVC YUYV/NV12/RGB24/MJPEG.
/// Actual EXPBUF/import support is probed separately; failure retains copy. /// Actual EXPBUF/import support is probed separately; failure retains copy.
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))] #[cfg(feature = "rkmpp")]
pub(crate) fn supports_rkmpp_dmabuf(&self) -> bool { pub(crate) fn supports_rkmpp_dmabuf(&self) -> bool {
self.dma_layout_bytes.is_some_and(|minimum| { self.dma_layout_bytes.is_some_and(|minimum| {
(2..=16).contains(&self.mappings.len()) (2..=16).contains(&self.mappings.len())
@@ -502,7 +502,7 @@ impl CaptureStream {
}) })
} }
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))] #[cfg(feature = "rkmpp")]
pub(crate) fn export_dmabufs(&self) -> io::Result<Vec<(OwnedFd, usize)>> { pub(crate) fn export_dmabufs(&self) -> io::Result<Vec<(OwnedFd, usize)>> {
if !self.supports_rkmpp_dmabuf() { if !self.supports_rkmpp_dmabuf() {
return Err(io::Error::new( return Err(io::Error::new(
@@ -524,7 +524,7 @@ impl CaptureStream {
/// Run a synchronous consumer while a buffer is dequeued. QBUF occurs only /// Run a synchronous consumer while a buffer is dequeued. QBUF occurs only
/// after the callback returns, including its error path. Consumers must end /// after the callback returns, including its error path. Consumers must end
/// hardware access before returning; see hwcodec::rkmpp_dmabuf::encode. /// hardware access before returning; see hwcodec::rkmpp_dmabuf::encode.
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))] #[cfg(feature = "rkmpp")]
pub(crate) fn with_next_dmabuf<T>( pub(crate) fn with_next_dmabuf<T>(
&mut self, &mut self,
consume: impl FnOnce(usize, usize, Option<OwnedFd>) -> T, consume: impl FnOnce(usize, usize, Option<OwnedFd>) -> T,
@@ -843,17 +843,17 @@ impl CaptureStream {
} }
} }
#[cfg(any(test, target_arch = "aarch64", target_arch = "arm"))] #[cfg(any(test, feature = "rkmpp"))]
struct BufferReturn<F: FnOnce() -> io::Result<()>>(Option<F>); struct BufferReturn<F: FnOnce() -> io::Result<()>>(Option<F>);
#[cfg(any(test, target_arch = "aarch64", target_arch = "arm"))] #[cfg(any(test, feature = "rkmpp"))]
impl<F: FnOnce() -> io::Result<()>> BufferReturn<F> { impl<F: FnOnce() -> io::Result<()>> BufferReturn<F> {
fn finish(mut self) -> io::Result<()> { fn finish(mut self) -> io::Result<()> {
self.0.take().expect("capture lease already returned")() self.0.take().expect("capture lease already returned")()
} }
} }
#[cfg(any(test, target_arch = "aarch64", target_arch = "arm"))] #[cfg(any(test, feature = "rkmpp"))]
impl<F: FnOnce() -> io::Result<()>> Drop for BufferReturn<F> { impl<F: FnOnce() -> io::Result<()>> Drop for BufferReturn<F> {
fn drop(&mut self) { fn drop(&mut self) {
if let Some(return_buffer) = self.0.take() { if let Some(return_buffer) = self.0.take() {

View File

@@ -4,14 +4,14 @@ use std::io;
#[cfg(any( #[cfg(any(
test, test,
all(target_os = "linux", any(target_arch = "aarch64", target_arch = "arm")) all(target_os = "linux", feature = "rkmpp")
))] ))]
use crate::video::device::VideoControlMode; use crate::video::device::VideoControlMode;
use crate::video::signal::SignalStatus; use crate::video::signal::SignalStatus;
#[cfg(any( #[cfg(any(
test, test,
all(target_os = "linux", any(target_arch = "aarch64", target_arch = "arm")) all(target_os = "linux", feature = "rkmpp")
))] ))]
pub(crate) fn capture_recovery_status( pub(crate) fn capture_recovery_status(
control_mode: VideoControlMode, control_mode: VideoControlMode,

View File

@@ -17,7 +17,7 @@ pub mod video_codec;
pub mod vp8; pub mod vp8;
pub mod vp9; pub mod vp9;
#[cfg(all(feature = "desktop", any(target_arch = "aarch64", target_arch = "arm")))] #[cfg(all(feature = "desktop", feature = "rkmpp"))]
pub mod mjpeg_rkmpp; pub mod mjpeg_rkmpp;
pub use convert::{MjpegToNv12Decoder, PixelConverter, Yuv420pBuffer}; pub use convert::{MjpegToNv12Decoder, PixelConverter, Yuv420pBuffer};

View File

@@ -8,7 +8,7 @@ use crate::video::codec::vp8::{VP8Config, VP8Encoder};
use crate::video::codec::vp9::{VP9Config, VP9Encoder}; use crate::video::codec::vp9::{VP9Config, VP9Encoder};
use crate::video::format::{PixelFormat, Resolution}; use crate::video::format::{PixelFormat, Resolution};
use bytes::Bytes; use bytes::Bytes;
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))] #[cfg(feature = "rkmpp")]
use hwcodec::ffmpeg_hw::{ use hwcodec::ffmpeg_hw::{
last_error_message as ffmpeg_hw_last_error, HwMjpegH26xConfig, HwMjpegH26xPipeline, last_error_message as ffmpeg_hw_last_error, HwMjpegH26xConfig, HwMjpegH26xPipeline,
}; };
@@ -22,9 +22,9 @@ pub(super) struct EncoderThreadState {
pub(super) nv12_converter: Option<Nv12Converter>, pub(super) nv12_converter: Option<Nv12Converter>,
pub(super) yuv420p_converter: Option<PixelConverter>, pub(super) yuv420p_converter: Option<PixelConverter>,
pub(super) encoder_needs_yuv420p: bool, pub(super) encoder_needs_yuv420p: bool,
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))] #[cfg(feature = "rkmpp")]
pub(super) ffmpeg_hw_pipeline: Option<HwMjpegH26xPipeline>, pub(super) ffmpeg_hw_pipeline: Option<HwMjpegH26xPipeline>,
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))] #[cfg(feature = "rkmpp")]
pub(super) ffmpeg_hw_enabled: bool, pub(super) ffmpeg_hw_enabled: bool,
pub(super) fps: u32, pub(super) fps: u32,
pub(super) codec: VideoEncoderType, pub(super) codec: VideoEncoderType,
@@ -347,9 +347,9 @@ pub(super) fn build_encoder_state(
} }
}; };
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))] #[cfg(feature = "rkmpp")]
let is_rkmpp_encoder = selected_codec_name.contains("rkmpp"); let is_rkmpp_encoder = selected_codec_name.contains("rkmpp");
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))] #[cfg(feature = "rkmpp")]
if needs_mjpeg_decode if needs_mjpeg_decode
&& is_rkmpp_encoder && is_rkmpp_encoder
&& matches!( && matches!(
@@ -389,9 +389,9 @@ pub(super) fn build_encoder_state(
nv12_converter: None, nv12_converter: None,
yuv420p_converter: None, yuv420p_converter: None,
encoder_needs_yuv420p: false, encoder_needs_yuv420p: false,
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))] #[cfg(feature = "rkmpp")]
ffmpeg_hw_pipeline: Some(pipeline), ffmpeg_hw_pipeline: Some(pipeline),
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))] #[cfg(feature = "rkmpp")]
ffmpeg_hw_enabled: true, ffmpeg_hw_enabled: true,
fps: config.fps, fps: config.fps,
codec: config.output_codec, codec: config.output_codec,
@@ -573,9 +573,9 @@ pub(super) fn build_encoder_state(
nv12_converter, nv12_converter,
yuv420p_converter, yuv420p_converter,
encoder_needs_yuv420p: needs_yuv420p, encoder_needs_yuv420p: needs_yuv420p,
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))] #[cfg(feature = "rkmpp")]
ffmpeg_hw_pipeline: None, ffmpeg_hw_pipeline: None,
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))] #[cfg(feature = "rkmpp")]
ffmpeg_hw_enabled: false, ffmpeg_hw_enabled: false,
fps: config.fps, fps: config.fps,
codec: config.output_codec, codec: config.output_codec,

View File

@@ -35,13 +35,13 @@ use super::encoder_state::{build_encoder_state, should_parallel_decode_mjpeg, En
use super::frame_mailbox::{FrameMailbox, PublishResult}; use super::frame_mailbox::{FrameMailbox, PublishResult};
use super::mjpeg_admission::{MjpegAdmissionPolicy, MjpegAdmissionStage, MjpegDropReason}; use super::mjpeg_admission::{MjpegAdmissionPolicy, MjpegAdmissionStage, MjpegDropReason};
#[cfg(all(target_os = "linux", any(target_arch = "aarch64", target_arch = "arm")))] #[cfg(all(target_os = "linux", feature = "rkmpp"))]
#[path = "dmabuf.rs"] #[path = "dmabuf.rs"]
mod dmabuf; mod dmabuf;
#[cfg(any( #[cfg(any(
test, test,
all(target_os = "linux", any(target_arch = "aarch64", target_arch = "arm")) all(target_os = "linux", feature = "rkmpp")
))] ))]
fn rkmpp_dma_eligible( fn rkmpp_dma_eligible(
backend: Option<EncoderBackend>, backend: Option<EncoderBackend>,
@@ -269,7 +269,7 @@ fn spawn_mjpeg_decode_workers(
senders senders
} }
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))] #[cfg(feature = "rkmpp")]
use hwcodec::ffmpeg_hw::last_error_message as ffmpeg_hw_last_error; use hwcodec::ffmpeg_hw::last_error_message as ffmpeg_hw_last_error;
/// Encoded video frame for distribution /// Encoded video frame for distribution
@@ -726,7 +726,7 @@ impl SharedVideoPipeline {
match cmd { match cmd {
PipelineCmd::SetBitrate { preset } => { PipelineCmd::SetBitrate { preset } => {
let bitrate_kbps = preset.bitrate_kbps(); let bitrate_kbps = preset.bitrate_kbps();
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))] #[cfg(feature = "rkmpp")]
if state.ffmpeg_hw_enabled { if state.ffmpeg_hw_enabled {
if let Some(ref mut pipeline) = state.ffmpeg_hw_pipeline { if let Some(ref mut pipeline) = state.ffmpeg_hw_pipeline {
pipeline pipeline
@@ -1154,7 +1154,7 @@ impl SharedVideoPipeline {
Err(e) => return Err(e), Err(e) => return Err(e),
}; };
#[cfg(all(target_os = "linux", any(target_arch = "aarch64", target_arch = "arm")))] #[cfg(all(target_os = "linux", feature = "rkmpp"))]
if dmabuf::eligible(&config) { if dmabuf::eligible(&config) {
if let Some(stream) = preopened.as_ref().filter(|s| s.supports_rkmpp_dmabuf()) { if let Some(stream) = preopened.as_ref().filter(|s| s.supports_rkmpp_dmabuf()) {
match dmabuf::prepare(stream, &config) { match dmabuf::prepare(stream, &config) {
@@ -1817,7 +1817,7 @@ impl SharedVideoPipeline {
let pts_ms = self.pts_ms(); let pts_ms = self.pts_ms();
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))] #[cfg(feature = "rkmpp")]
if state.ffmpeg_hw_enabled { if state.ffmpeg_hw_enabled {
if input_format != PixelFormat::Mjpeg { if input_format != PixelFormat::Mjpeg {
return Err(AppError::VideoError( return Err(AppError::VideoError(
@@ -2527,9 +2527,9 @@ mod tests {
nv12_converter: None, nv12_converter: None,
yuv420p_converter: None, yuv420p_converter: None,
encoder_needs_yuv420p: false, encoder_needs_yuv420p: false,
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))] #[cfg(feature = "rkmpp")]
ffmpeg_hw_pipeline: None, ffmpeg_hw_pipeline: None,
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))] #[cfg(feature = "rkmpp")]
ffmpeg_hw_enabled: false, ffmpeg_hw_enabled: false,
fps: 30, fps: 30,
codec: VideoEncoderType::H264, codec: VideoEncoderType::H264,