如何跨FFI边界处理断言语句?Tauri视频校验程序崩溃求助
解决libswscale断言错误导致的程序崩溃问题
问题背景
实现了一个Tauri命令函数,用于检测指定路径文件是否为有效视频,但传入非视频文件(如.rs文件)时程序直接崩溃。尝试用线程包裹逻辑、panic::catch_unwind捕获异常均无效,控制台报错显示底层libswscale库触发断言失败:
[sunrast @ 0x7f1e44003040] this is not sunras encoded data [image2 @ 0x7f1e44000900] Could not find codec parameters for stream 0 (Video: sunrast, none): unspecified size Consider increasing the value for the 'analyzeduration' (0) and 'probesize' (5000000) options Assertion desc failed at libswscale/swscale_internal.h:725
相关代码:
use std::{panic, thread}; use libvmaf_rs::video::Video; #[tauri::command] pub async fn validate_video(path: String) -> Result<(), String> { let did_panic = thread::spawn(|| match Video::new(path, 640, 480) { Ok(_) => Ok(()), Err(e) => Err(e.frames().into_iter().map(|f| format!("{f:?}")).fold( String::new(), |mut prev, next| { prev.push_str(&next); prev.push_str("\n"); prev }, )), }); match did_panic.join() { Ok(e) => e, Err(e) => Err("Panicked while trying to load video".to_string()), } }
核心原因
这不是Rust层面的panic,而是C语言层的断言失败:libswscale作为FFmpeg的C库,断言失败会直接调用abort()终止进程,完全绕过Rust的panic捕获机制,因此panic::catch_unwind和线程都无法拦截。
解决方案
1. 前置过滤非视频文件
通过文件扩展名或魔术字节初步筛选,避免无效调用触发底层错误:
fn is_likely_video(path: &str) -> bool { // 覆盖常见视频扩展名 let video_extensions = ["mp4", "mov", "avi", "mkv", "flv", "webm", "wmv"]; path.split('.') .last() .map(|ext| video_extensions.contains(&ext.to_lowercase().as_str())) .unwrap_or(false) } #[tauri::command] pub async fn validate_video(path: String) -> Result<(), String> { if !is_likely_video(&path) { return Err("Unrecognized video file extension".to_string()); } // 原有逻辑... let did_panic = thread::spawn(|| match Video::new(path, 640, 480) { Ok(_) => Ok(()), Err(e) => Err(e.frames().into_iter().map(|f| format!("{f:?}")).fold( String::new(), |mut prev, next| { prev.push_str(&next); prev.push_str("\n"); prev }, )), }); match did_panic.join() { Ok(e) => e, Err(e) => Err("Panicked while trying to load video".to_string()), } }
2. 用FFmpeg API提前探测文件
直接通过ffmpeg-next库探测文件流信息,确认是有效视频后再调用libvmaf_rs,从根源避免触发断言:
use ffmpeg_next::{format, media}; fn probe_video_file(path: &str) -> Result<(), String> { // 打开文件并读取流信息 let ictx = format::input(path) .map_err(|e| format!("Failed to open file: {e}"))?; // 查找最佳视频流 let video_stream = ictx.streams() .best(media::Type::Video) .ok_or("No video stream found in file")?; // 校验视频流参数有效性 let codec_params = video_stream.codec_parameters(); if codec_params.width() == 0 || codec_params.height() == 0 { return Err("Invalid video stream parameters".to_string()); } Ok(()) } #[tauri::command] pub async fn validate_video(path: String) -> Result<(), String> { // 前置探测,失败直接返回 probe_video_file(&path)?; // 调用libvmaf_rs处理 let did_panic = thread::spawn(|| match Video::new(path, 640, 480) { Ok(_) => Ok(()), Err(e) => Err(e.frames().into_iter().map(|f| format!("{f:?}")).fold( String::new(), |mut prev, next| { prev.push_str(&next); prev.push_str("\n"); prev }, )), }); match did_panic.join() { Ok(e) => e, Err(e) => Err("Panicked while trying to load video".to_string()), } }
3. 禁用FFmpeg断言(不推荐)
若必须处理任意文件,可在编译FFmpeg时添加--disable-assertions参数禁用断言,但这会隐藏潜在bug,降低程序稳定性,仅作为最后手段。
内容的提问来源于stack exchange,提问作者Brandon Piña
相关产品推荐
相关产品推荐

