fix: resolve SHM hang, DRM device mismatch, and duplicate VAAPI context
BUG-2 (HIGH): SHM Buffer event caused permanent hang In the ZwlrScreencopyFrameV1 dispatcher, receiving a SHM Buffer event left in_flight_surface stuck at AllocQueued forever, preventing queue_alloc_frame() from requesting new frames. Fix: treat Buffer as a metadata offer (v3 protocol), wait for BufferDone to decide failure, and add AllocQueued state guard to LinuxDmabuf handler. BUG-3 (MEDIUM): Portal backend picked wrong GPU on multi-GPU systems state_portal.rs hardcoded /dev/dri/renderD128 then renderD129, which selects the wrong GPU when PipeWire uses a different device. Fix: extract find_drm_render_nodes() as shared utility; defer DRM device selection to first PipeWire frame; test each candidate with av_hwframe_transfer_data to find the GPU that can actually import the DMA-BUF frame. BUG-4 (LOW): VAAPI device context created twice unnecessarily try_finalize_output() created an AvHwDevCtx stored in EverythingButFmt, but negotiate_format() discarded it (_hw_device_ctx) and EncState::new created a new one. Fix: thread the existing hw_device_ctx through negotiate_format() and create_encoder() to EncState::new() which reuses it when provided.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
// 采集门户状态模块 —— 通过 PipeWire/DMA-BUF 进行屏幕采集并编码
|
||||
use std::mem;
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
@@ -39,8 +40,8 @@ pub struct StatePortal {
|
||||
errored: bool,
|
||||
/// 是否为第一帧(首帧跳过帧率限制)
|
||||
first_frame: bool,
|
||||
/// DRM 渲染设备路径(如 /dev/dri/renderD128)
|
||||
drm_device: PathBuf,
|
||||
/// DRM 渲染设备路径(如 /dev/dri/renderD128);None 表示首帧自动检测
|
||||
drm_device: Option<PathBuf>,
|
||||
/// 第一帧的时间戳(纳秒),用于计算相对 PTS
|
||||
first_pts_ns: Option<i64>,
|
||||
}
|
||||
@@ -51,7 +52,11 @@ impl StatePortal {
|
||||
/// 初始化 DRM 设备路径和 PipeWire 采集端点,编码器延迟到第一帧到达时创建。
|
||||
pub fn new(args: Args) -> Result<Self> {
|
||||
let drm_device = resolve_drm_device(&args)?;
|
||||
tracing::info!("Using DRM device: {}", drm_device.display());
|
||||
if let Some(ref drm_device) = drm_device {
|
||||
tracing::info!("Using DRM device: {}", drm_device.display());
|
||||
} else {
|
||||
tracing::info!("DRM device auto-detection enabled");
|
||||
}
|
||||
|
||||
let cap = CapPortal::new(&args)?;
|
||||
|
||||
@@ -92,8 +97,9 @@ impl StatePortal {
|
||||
frame.modifier
|
||||
);
|
||||
|
||||
let drm_path = self.resolve_drm_device_for_frame(&frame)?;
|
||||
let enc = avhw::create_encoder(
|
||||
&self.drm_device,
|
||||
&drm_path,
|
||||
self.args.output.as_ref(),
|
||||
frame.width,
|
||||
frame.height,
|
||||
@@ -101,6 +107,7 @@ impl StatePortal {
|
||||
Transform::Normal,
|
||||
self.args.bitrate,
|
||||
self.args.gop_size,
|
||||
None,
|
||||
)?;
|
||||
|
||||
self.enc = Some(enc);
|
||||
@@ -128,6 +135,44 @@ impl StatePortal {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn resolve_drm_device_for_frame(&mut self, frame: &PwDmaBufFrame) -> Result<PathBuf> {
|
||||
if let Some(ref drm) = self.drm_device {
|
||||
return Ok(drm.clone());
|
||||
}
|
||||
|
||||
let candidates = crate::state::find_drm_render_nodes();
|
||||
if candidates.is_empty() {
|
||||
bail!("No DRM render device found. Specify --drm-device.");
|
||||
}
|
||||
|
||||
let mut failures = Vec::new();
|
||||
for candidate in &candidates {
|
||||
match crate::avhw::test_dma_buf_import(candidate, frame) {
|
||||
Ok(()) => {
|
||||
tracing::info!(
|
||||
"Auto-selected DRM device: {} (can import PipeWire DMA-BUF)",
|
||||
candidate.display()
|
||||
);
|
||||
self.drm_device = Some(candidate.clone());
|
||||
return Ok(candidate.clone());
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::debug!(
|
||||
"DRM device {} cannot import frame: {err:#}",
|
||||
candidate.display()
|
||||
);
|
||||
failures.push(format!("{}: {err:#}", candidate.display()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bail!(
|
||||
"No DRM render device can import the PipeWire DMA-BUF frame. \
|
||||
Specify --drm-device. Tried: {}",
|
||||
failures.join("; ")
|
||||
)
|
||||
}
|
||||
|
||||
/// 处理单帧 DMA-BUF 数据
|
||||
///
|
||||
/// 完整的帧处理流水线:
|
||||
@@ -311,25 +356,14 @@ fn build_drm_descriptor(frame: &PwDmaBufFrame) -> ffi::AVDRMFrameDescriptor {
|
||||
desc
|
||||
}
|
||||
|
||||
use std::os::fd::AsRawFd;
|
||||
|
||||
/// 解析 DRM 渲染设备路径
|
||||
///
|
||||
/// 优先使用命令行指定的设备路径,否则依次尝试
|
||||
/// `/dev/dri/renderD128` 和 `/dev/dri/renderD129`。
|
||||
fn resolve_drm_device(args: &Args) -> Result<PathBuf> {
|
||||
/// 仅使用命令行指定的设备路径;未指定则在首帧到达时自动检测。
|
||||
fn resolve_drm_device(args: &Args) -> Result<Option<PathBuf>> {
|
||||
if let Some(ref drm) = args.drm_device {
|
||||
return Ok(PathBuf::from(drm));
|
||||
return Ok(Some(PathBuf::from(drm)));
|
||||
}
|
||||
|
||||
for render in &["/dev/dri/renderD128", "/dev/dri/renderD129"] {
|
||||
let path = PathBuf::from(render);
|
||||
if path.exists() {
|
||||
return Ok(path);
|
||||
}
|
||||
}
|
||||
|
||||
bail!("No DRM render device found. Specify --drm-device.")
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
Reference in New Issue
Block a user