mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-03-15 07:26:44 +08:00
feat: 支持树莓派 v4l2m2m 编码器探测
This commit is contained in:
@@ -28,7 +28,8 @@ serde_json = "1"
|
|||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
tracing = "0.1"
|
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
|
# Error handling
|
||||||
thiserror = "2"
|
thiserror = "2"
|
||||||
|
|||||||
@@ -162,10 +162,19 @@ int linux_support_v4l2m2m() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Check common V4L2 M2M device paths used by various ARM SoCs
|
// 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[] = {
|
const char *m2m_devices[] = {
|
||||||
"/dev/video10", // Common M2M encoder device
|
"/dev/video10",
|
||||||
"/dev/video11", // Common M2M decoder device
|
"/dev/video11",
|
||||||
"/dev/video0", // Some SoCs use video0 for M2M
|
"/dev/video0",
|
||||||
|
"/dev/video1",
|
||||||
|
"/dev/video2",
|
||||||
|
"/dev/video32",
|
||||||
};
|
};
|
||||||
|
|
||||||
for (size_t i = 0; i < sizeof(m2m_devices) / sizeof(m2m_devices[0]); i++) {
|
for (size_t i = 0; i < sizeof(m2m_devices) / sizeof(m2m_devices[0]); i++) {
|
||||||
|
|||||||
@@ -147,11 +147,11 @@ bool set_lantency_free(void *priv_data, const std::string &name) {
|
|||||||
// V4L2 M2M hardware encoder - minimize buffer latency
|
// V4L2 M2M hardware encoder - minimize buffer latency
|
||||||
if (name.find("v4l2m2m") != std::string::npos) {
|
if (name.find("v4l2m2m") != std::string::npos) {
|
||||||
// Minimize number of output buffers for lower latency
|
// 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));
|
LOG_WARN(std::string("v4l2m2m set num_output_buffers failed, ret = ") + av_err2str(ret));
|
||||||
// Not fatal
|
// 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));
|
LOG_WARN(std::string("v4l2m2m set num_capture_buffers failed, ret = ") + av_err2str(ret));
|
||||||
// Not fatal
|
// 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() {
|
extern "C" void hwcodec_set_flag_could_not_find_ref_with_poc() {
|
||||||
util_decode::g_flag_could_not_find_ref_with_poc = true;
|
util_decode::g_flag_could_not_find_ref_with_poc = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
include!(concat!(env!("OUT_DIR"), "/ffmpeg_ffi.rs"));
|
include!(concat!(env!("OUT_DIR"), "/ffmpeg_ffi.rs"));
|
||||||
|
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use std::env;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, Eq, PartialEq, Clone, Copy, Serialize, Deserialize)]
|
||||||
pub enum AVHWDeviceType {
|
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() {
|
pub(crate) fn init_av_log() {
|
||||||
static INIT: std::sync::Once = std::sync::Once::new();
|
static INIT: std::sync::Once = std::sync::Once::new();
|
||||||
INIT.call_once(|| unsafe {
|
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();
|
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::<i32>() {
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -352,16 +352,46 @@ impl Encoder {
|
|||||||
debug!("Encoder {} created successfully", codec.name);
|
debug!("Encoder {} created successfully", codec.name);
|
||||||
let mut passed = false;
|
let mut passed = false;
|
||||||
let mut last_err: Option<i32> = None;
|
let mut last_err: Option<i32> = 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 {
|
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 pts = (attempt as i64) * 33; // 33ms is an approximation for 30 FPS (1000 / 30)
|
||||||
let start = std::time::Instant::now();
|
let start = std::time::Instant::now();
|
||||||
match encoder.encode(&yuv, pts) {
|
match encoder.encode(&yuv, pts) {
|
||||||
Ok(frames) => {
|
Ok(frames) => {
|
||||||
let elapsed = start.elapsed().as_millis();
|
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 _ {
|
if frames[0].key == 1 && elapsed < TEST_TIMEOUT_MS as _ {
|
||||||
debug!(
|
debug!(
|
||||||
"Encoder {} test passed on attempt {}",
|
"Encoder {} test passed on attempt {}",
|
||||||
|
|||||||
@@ -712,10 +712,13 @@ fn init_logging(level: LogLevel, verbose_count: u8) {
|
|||||||
let env_filter =
|
let env_filter =
|
||||||
tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| filter.into());
|
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(env_filter)
|
||||||
.with(tracing_subscriber::fmt::layer())
|
.with(tracing_subscriber::fmt::layer())
|
||||||
.init();
|
.try_init()
|
||||||
|
{
|
||||||
|
eprintln!("failed to initialize tracing: {}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the application data directory
|
/// Get the application data directory
|
||||||
|
|||||||
Reference in New Issue
Block a user