From f8a031c90c1e3920e7c76d37af41ebc7e8848a3f Mon Sep 17 00:00:00 2001 From: mofeng Date: Mon, 9 Feb 2026 14:54:46 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E6=A0=91=E8=8E=93?= =?UTF-8?q?=E6=B4=BE=20v4l2m2m=20=E7=BC=96=E7=A0=81=E5=99=A8=E6=8E=A2?= =?UTF-8?q?=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 3 +- .../cpp/common/platform/linux/linux.cpp | 15 ++++++-- libs/hwcodec/cpp/common/util.cpp | 6 ++-- libs/hwcodec/src/ffmpeg.rs | 32 ++++++++++++++++- libs/hwcodec/src/ffmpeg_ram/encode.rs | 34 +++++++++++++++++-- src/main.rs | 7 ++-- 6 files changed, 85 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c4f7dc1a..23687974 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,8 @@ serde_json = "1" # Logging tracing = "0.1" -tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] } +tracing-subscriber = { version = "0.3", features = ["env-filter", "json", "tracing-log"] } +tracing-log = "0.2" # Error handling thiserror = "2" diff --git a/libs/hwcodec/cpp/common/platform/linux/linux.cpp b/libs/hwcodec/cpp/common/platform/linux/linux.cpp index 92c5ff7e..9574de02 100644 --- a/libs/hwcodec/cpp/common/platform/linux/linux.cpp +++ b/libs/hwcodec/cpp/common/platform/linux/linux.cpp @@ -162,10 +162,19 @@ int linux_support_v4l2m2m() { }; // Check common V4L2 M2M device paths used by various ARM SoCs + // /dev/video10 - Standard on many SoCs + // /dev/video11 - Standard on many SoCs (often decoder) + // /dev/video0 - Some platforms (like RPi) might use this + // /dev/video1 - Alternate RPi path + // /dev/video2 - Alternate path + // /dev/video32 - Some Allwinner/Rockchip legacy const char *m2m_devices[] = { - "/dev/video10", // Common M2M encoder device - "/dev/video11", // Common M2M decoder device - "/dev/video0", // Some SoCs use video0 for M2M + "/dev/video10", + "/dev/video11", + "/dev/video0", + "/dev/video1", + "/dev/video2", + "/dev/video32", }; for (size_t i = 0; i < sizeof(m2m_devices) / sizeof(m2m_devices[0]); i++) { diff --git a/libs/hwcodec/cpp/common/util.cpp b/libs/hwcodec/cpp/common/util.cpp index a65d5a7f..9661bc2c 100644 --- a/libs/hwcodec/cpp/common/util.cpp +++ b/libs/hwcodec/cpp/common/util.cpp @@ -147,11 +147,11 @@ bool set_lantency_free(void *priv_data, const std::string &name) { // V4L2 M2M hardware encoder - minimize buffer latency if (name.find("v4l2m2m") != std::string::npos) { // Minimize number of output buffers for lower latency - if ((ret = av_opt_set_int(priv_data, "num_output_buffers", 2, 0)) < 0) { + if ((ret = av_opt_set_int(priv_data, "num_output_buffers", 4, 0)) < 0) { LOG_WARN(std::string("v4l2m2m set num_output_buffers failed, ret = ") + av_err2str(ret)); // Not fatal } - if ((ret = av_opt_set_int(priv_data, "num_capture_buffers", 2, 0)) < 0) { + if ((ret = av_opt_set_int(priv_data, "num_capture_buffers", 4, 0)) < 0) { LOG_WARN(std::string("v4l2m2m set num_capture_buffers failed, ret = ") + av_err2str(ret)); // Not fatal } @@ -500,4 +500,4 @@ bool has_flag_could_not_find_ref_with_poc() { extern "C" void hwcodec_set_flag_could_not_find_ref_with_poc() { util_decode::g_flag_could_not_find_ref_with_poc = true; -} \ No newline at end of file +} diff --git a/libs/hwcodec/src/ffmpeg.rs b/libs/hwcodec/src/ffmpeg.rs index bb57dd7f..0ae134d1 100644 --- a/libs/hwcodec/src/ffmpeg.rs +++ b/libs/hwcodec/src/ffmpeg.rs @@ -6,6 +6,7 @@ include!(concat!(env!("OUT_DIR"), "/ffmpeg_ffi.rs")); use serde_derive::{Deserialize, Serialize}; +use std::env; #[derive(Debug, Eq, PartialEq, Clone, Copy, Serialize, Deserialize)] pub enum AVHWDeviceType { @@ -53,7 +54,36 @@ pub extern "C" fn hwcodec_av_log_callback(level: i32, message: *const std::os::r pub(crate) fn init_av_log() { static INIT: std::sync::Once = std::sync::Once::new(); INIT.call_once(|| unsafe { - av_log_set_level(AV_LOG_ERROR as i32); + av_log_set_level(parse_ffmpeg_log_level()); hwcodec_set_av_log_callback(); }); } + +fn parse_ffmpeg_log_level() -> i32 { + let raw = match env::var("ONE_KVM_FFMPEG_LOG") { + Ok(value) => value, + Err(_) => return AV_LOG_ERROR as i32, + }; + + let value = raw.trim().to_ascii_lowercase(); + if value.is_empty() { + return AV_LOG_ERROR as i32; + } + + if let Ok(level) = value.parse::() { + return level; + } + + match value.as_str() { + "quiet" => AV_LOG_QUIET as i32, + "panic" => AV_LOG_PANIC as i32, + "fatal" => AV_LOG_FATAL as i32, + "error" => AV_LOG_ERROR as i32, + "warn" | "warning" => AV_LOG_WARNING as i32, + "info" => AV_LOG_INFO as i32, + "verbose" => AV_LOG_VERBOSE as i32, + "debug" => AV_LOG_DEBUG as i32, + "trace" => AV_LOG_TRACE as i32, + _ => AV_LOG_ERROR as i32, + } +} diff --git a/libs/hwcodec/src/ffmpeg_ram/encode.rs b/libs/hwcodec/src/ffmpeg_ram/encode.rs index dff0a135..263b04fc 100644 --- a/libs/hwcodec/src/ffmpeg_ram/encode.rs +++ b/libs/hwcodec/src/ffmpeg_ram/encode.rs @@ -352,16 +352,46 @@ impl Encoder { debug!("Encoder {} created successfully", codec.name); let mut passed = false; let mut last_err: Option = None; + let is_v4l2m2m = codec.name.contains("v4l2m2m"); - let max_attempts = 1; + let max_attempts = if is_v4l2m2m { 5 } else { 1 }; for attempt in 0..max_attempts { + if is_v4l2m2m { + encoder.request_keyframe(); + } let pts = (attempt as i64) * 33; // 33ms is an approximation for 30 FPS (1000 / 30) let start = std::time::Instant::now(); match encoder.encode(&yuv, pts) { Ok(frames) => { let elapsed = start.elapsed().as_millis(); - if frames.len() == 1 { + if is_v4l2m2m { + if !frames.is_empty() && elapsed < TEST_TIMEOUT_MS as _ { + debug!( + "Encoder {} test passed on attempt {} (frames: {})", + codec.name, + attempt + 1, + frames.len() + ); + res.push(codec.clone()); + passed = true; + break; + } else if frames.is_empty() { + debug!( + "Encoder {} test produced no output on attempt {}", + codec.name, + attempt + 1 + ); + } else { + debug!( + "Encoder {} test failed on attempt {} - frames: {}, timeout: {}ms", + codec.name, + attempt + 1, + frames.len(), + elapsed + ); + } + } else if frames.len() == 1 { if frames[0].key == 1 && elapsed < TEST_TIMEOUT_MS as _ { debug!( "Encoder {} test passed on attempt {}", diff --git a/src/main.rs b/src/main.rs index d360ae80..43f04907 100644 --- a/src/main.rs +++ b/src/main.rs @@ -712,10 +712,13 @@ fn init_logging(level: LogLevel, verbose_count: u8) { let env_filter = tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| filter.into()); - tracing_subscriber::registry() + if let Err(err) = tracing_subscriber::registry() .with(env_filter) .with(tracing_subscriber::fmt::layer()) - .init(); + .try_init() + { + eprintln!("failed to initialize tracing: {}", err); + } } /// Get the application data directory