mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-01-28 16:41:52 +08:00
feat(video): 事务化切换与前端统一编排,增强视频输入格式支持
- 后端:切换事务+transition_id,/stream/mode 返回 switching/transition_id 与实际 codec - 事件:新增 mode_switching/mode_ready,config/webrtc_ready/mode_changed 关联事务 - 编码/格式:扩展 NV21/NV16/NV24/RGB/BGR 输入与转换链路,RKMPP direct input 优化 - 前端:useVideoSession 统一切换,失败回退真实切回 MJPEG,菜单格式同步修复 - 清理:useVideoStream 降级为 MJPEG-only
This commit is contained in:
@@ -56,7 +56,10 @@ fn build_common(builder: &mut Build) {
|
||||
|
||||
// Unsupported platforms
|
||||
if target_os != "windows" && target_os != "linux" {
|
||||
panic!("Unsupported OS: {}. Only Windows and Linux are supported.", target_os);
|
||||
panic!(
|
||||
"Unsupported OS: {}. Only Windows and Linux are supported.",
|
||||
target_os
|
||||
);
|
||||
}
|
||||
|
||||
// tool
|
||||
@@ -103,7 +106,9 @@ mod ffmpeg {
|
||||
use std::process::Command;
|
||||
|
||||
// Check if static linking is requested
|
||||
let use_static = std::env::var("FFMPEG_STATIC").map(|v| v == "1").unwrap_or(false);
|
||||
let use_static = std::env::var("FFMPEG_STATIC")
|
||||
.map(|v| v == "1")
|
||||
.unwrap_or(false);
|
||||
let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
|
||||
|
||||
// Try custom library path first:
|
||||
@@ -142,7 +147,7 @@ mod ffmpeg {
|
||||
// VAAPI for x86_64
|
||||
println!("cargo:rustc-link-lib=va");
|
||||
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");
|
||||
} else {
|
||||
// RKMPP for ARM
|
||||
@@ -172,10 +177,7 @@ mod ffmpeg {
|
||||
|
||||
for lib in &libs {
|
||||
// Get cflags
|
||||
if let Ok(output) = Command::new("pkg-config")
|
||||
.args(["--cflags", lib])
|
||||
.output()
|
||||
{
|
||||
if let Ok(output) = Command::new("pkg-config").args(["--cflags", lib]).output() {
|
||||
if output.status.success() {
|
||||
let cflags = String::from_utf8_lossy(&output.stdout);
|
||||
for flag in cflags.split_whitespace() {
|
||||
@@ -193,10 +195,7 @@ mod ffmpeg {
|
||||
vec!["--libs", lib]
|
||||
};
|
||||
|
||||
if let Ok(output) = Command::new("pkg-config")
|
||||
.args(&pkg_config_args)
|
||||
.output()
|
||||
{
|
||||
if let Ok(output) = Command::new("pkg-config").args(&pkg_config_args).output() {
|
||||
if output.status.success() {
|
||||
let libs_str = String::from_utf8_lossy(&output.stdout);
|
||||
for flag in libs_str.split_whitespace() {
|
||||
@@ -221,7 +220,9 @@ mod ffmpeg {
|
||||
panic!("pkg-config failed for {}. Install FFmpeg development libraries: sudo apt install libavcodec-dev libavutil-dev", lib);
|
||||
}
|
||||
} else {
|
||||
panic!("pkg-config not found. Install pkg-config and FFmpeg development libraries.");
|
||||
panic!(
|
||||
"pkg-config not found. Install pkg-config and FFmpeg development libraries."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,7 +302,10 @@ mod ffmpeg {
|
||||
// ARM (aarch64, arm): no X11 needed, uses RKMPP/V4L2
|
||||
v
|
||||
} else {
|
||||
panic!("Unsupported OS: {}. Only Windows and Linux are supported.", target_os);
|
||||
panic!(
|
||||
"Unsupported OS: {}. Only Windows and Linux are supported.",
|
||||
target_os
|
||||
);
|
||||
};
|
||||
|
||||
for lib in dyn_libs.iter() {
|
||||
@@ -312,10 +316,9 @@ mod ffmpeg {
|
||||
fn ffmpeg_ffi() {
|
||||
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
let ffmpeg_ram_dir = manifest_dir.join("cpp").join("common");
|
||||
let ffi_header = ffmpeg_ram_dir
|
||||
.join("ffmpeg_ffi.h")
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
let ffi_header_path = ffmpeg_ram_dir.join("ffmpeg_ffi.h");
|
||||
println!("cargo:rerun-if-changed={}", ffi_header_path.display());
|
||||
let ffi_header = ffi_header_path.to_string_lossy().to_string();
|
||||
bindgen::builder()
|
||||
.header(ffi_header)
|
||||
.rustified_enum("*")
|
||||
@@ -340,8 +343,6 @@ mod ffmpeg {
|
||||
.write_to_file(Path::new(&env::var_os("OUT_DIR").unwrap()).join("ffmpeg_ram_ffi.rs"))
|
||||
.unwrap();
|
||||
|
||||
builder.files(
|
||||
["ffmpeg_ram_encode.cpp"].map(|f| ffmpeg_ram_dir.join(f)),
|
||||
);
|
||||
builder.files(["ffmpeg_ram_encode.cpp"].map(|f| ffmpeg_ram_dir.join(f)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,11 +14,15 @@
|
||||
enum AVPixelFormat {
|
||||
AV_PIX_FMT_YUV420P = 0,
|
||||
AV_PIX_FMT_YUYV422 = 1,
|
||||
AV_PIX_FMT_RGB24 = 2,
|
||||
AV_PIX_FMT_BGR24 = 3,
|
||||
AV_PIX_FMT_YUV422P = 4, // planar YUV 4:2:2
|
||||
AV_PIX_FMT_YUVJ420P = 12, // JPEG full-range YUV420P (same layout as YUV420P)
|
||||
AV_PIX_FMT_YUVJ422P = 13, // JPEG full-range YUV422P (same layout as YUV422P)
|
||||
AV_PIX_FMT_NV12 = 23,
|
||||
AV_PIX_FMT_NV21 = 24,
|
||||
AV_PIX_FMT_NV16 = 101,
|
||||
AV_PIX_FMT_NV24 = 188,
|
||||
};
|
||||
|
||||
int av_log_get_level(void);
|
||||
@@ -26,4 +30,4 @@ void av_log_set_level(int level);
|
||||
void hwcodec_set_av_log_callback();
|
||||
void hwcodec_set_flag_could_not_find_ref_with_poc();
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -388,7 +388,9 @@ private:
|
||||
}
|
||||
_exit:
|
||||
av_packet_unref(pkt_);
|
||||
return encoded ? 0 : -1;
|
||||
// If no packet is produced for this input frame, treat it as EAGAIN.
|
||||
// This is not a fatal error: encoders may buffer internally (e.g., startup delay).
|
||||
return encoded ? 0 : AVERROR(EAGAIN);
|
||||
}
|
||||
|
||||
int fill_frame(AVFrame *frame, uint8_t *data, int data_length,
|
||||
@@ -511,4 +513,4 @@ extern "C" void ffmpeg_ram_request_keyframe(FFmpegRamEncoder *encoder) {
|
||||
} catch (const std::exception &e) {
|
||||
LOG_ERROR(std::string("ffmpeg_ram_request_keyframe failed, ") + std::string(e.what()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ pub fn setup_parent_death_signal() {
|
||||
pub fn child_exit_when_parent_exit(child_process_id: u32) -> bool {
|
||||
unsafe {
|
||||
extern "C" {
|
||||
fn add_process_to_new_job(child_process_id: u32) -> i32;
|
||||
fn add_process_to_new_job(child_process_id: u32) -> i32;
|
||||
}
|
||||
let result = add_process_to_new_job(child_process_id);
|
||||
result == 0
|
||||
|
||||
@@ -3,7 +3,8 @@ use crate::{
|
||||
ffmpeg::{init_av_log, AVPixelFormat},
|
||||
ffmpeg_ram::{
|
||||
ffmpeg_linesize_offset_length, ffmpeg_ram_encode, ffmpeg_ram_free_encoder,
|
||||
ffmpeg_ram_new_encoder, ffmpeg_ram_request_keyframe, ffmpeg_ram_set_bitrate, CodecInfo, AV_NUM_DATA_POINTERS,
|
||||
ffmpeg_ram_new_encoder, ffmpeg_ram_request_keyframe, ffmpeg_ram_set_bitrate, CodecInfo,
|
||||
AV_NUM_DATA_POINTERS,
|
||||
},
|
||||
};
|
||||
use log::trace;
|
||||
@@ -123,6 +124,12 @@ impl Encoder {
|
||||
self.frames as *const _ as *const c_void,
|
||||
ms,
|
||||
);
|
||||
// ffmpeg_ram_encode returns AVERROR(EAGAIN) when the encoder accepts the frame
|
||||
// but does not output a packet yet (e.g., startup delay / internal buffering).
|
||||
// Treat this as a successful call with an empty output list.
|
||||
if result == -11 {
|
||||
return Ok(&mut *self.frames);
|
||||
}
|
||||
if result != 0 {
|
||||
return Err(result);
|
||||
}
|
||||
@@ -358,7 +365,8 @@ impl Encoder {
|
||||
if frames[0].key == 1 && elapsed < TEST_TIMEOUT_MS as _ {
|
||||
debug!(
|
||||
"Encoder {} test passed on attempt {}",
|
||||
codec.name, attempt + 1
|
||||
codec.name,
|
||||
attempt + 1
|
||||
);
|
||||
res.push(codec.clone());
|
||||
passed = true;
|
||||
|
||||
Reference in New Issue
Block a user