feat(player): update FFmpeg decoder

This commit is contained in:
2026-03-19 01:25:44 +08:00
parent f55d623dca
commit 105a6ce834

View File

@@ -1,10 +1,9 @@
//! FFmpeg 封裝 //! FFmpeg wrapper
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use std::io::{BufReader, Read};
use std::path::Path; use std::path::Path;
use std::process::{Command, Stdio, Child, ChildStdout}; use std::process::{Child, ChildStdout, Command, Stdio};
use std::io::{Read, BufReader};
use std::sync::{Arc, Mutex};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct VideoInfo { pub struct VideoInfo {
@@ -39,8 +38,10 @@ impl FFmpegDecoder {
fn probe(path: &Path) -> Result<VideoInfo> { fn probe(path: &Path) -> Result<VideoInfo> {
let output = Command::new("ffprobe") let output = Command::new("ffprobe")
.args([ .args([
"-v", "quiet", "-v",
"-print_format", "json", "quiet",
"-print_format",
"json",
"-show_format", "-show_format",
"-show_streams", "-show_streams",
path.to_str().unwrap_or(""), path.to_str().unwrap_or(""),
@@ -48,14 +49,12 @@ impl FFmpegDecoder {
.output() .output()
.context("Failed to run ffprobe")?; .context("Failed to run ffprobe")?;
let json: serde_json::Value = serde_json::from_slice(&output.stdout) let json: serde_json::Value =
.context("Failed to parse ffprobe output")?; serde_json::from_slice(&output.stdout).context("Failed to parse ffprobe output")?;
let video_stream = json["streams"] let video_stream = json["streams"]
.as_array() .as_array()
.and_then(|streams| { .and_then(|streams| streams.iter().find(|s| s["codec_type"] == "video"))
streams.iter().find(|s| s["codec_type"] == "video")
})
.context("No video stream found")?; .context("No video stream found")?;
let width = video_stream["width"].as_u64().unwrap_or(0) as u32; let width = video_stream["width"].as_u64().unwrap_or(0) as u32;
@@ -65,7 +64,10 @@ impl FFmpegDecoder {
let (num, den) = { let (num, den) = {
let parts: Vec<&str> = fps_str.split('/').collect(); let parts: Vec<&str> = fps_str.split('/').collect();
if parts.len() == 2 { if parts.len() == 2 {
(parts[0].parse::<f64>().unwrap_or(30.0), parts[1].parse::<f64>().unwrap_or(1.0)) (
parts[0].parse::<f64>().unwrap_or(30.0),
parts[1].parse::<f64>().unwrap_or(1.0),
)
} else { } else {
(fps_str.parse::<f64>().unwrap_or(30.0), 1.0) (fps_str.parse::<f64>().unwrap_or(30.0), 1.0)
} }
@@ -77,7 +79,10 @@ impl FFmpegDecoder {
let duration_ms = (duration_sec * 1000.0) as u64; let duration_ms = (duration_sec * 1000.0) as u64;
let frame_count = (duration_sec * fps) as u64; let frame_count = (duration_sec * fps) as u64;
let codec = video_stream["codec_name"].as_str().unwrap_or("unknown").to_string(); let codec = video_stream["codec_name"]
.as_str()
.unwrap_or("unknown")
.to_string();
Ok(VideoInfo { Ok(VideoInfo {
width, width,
@@ -94,16 +99,20 @@ impl FFmpegDecoder {
} }
pub fn start_decoding(&mut self, start_ms: u64) -> Result<()> { pub fn start_decoding(&mut self, start_ms: u64) -> Result<()> {
self.stop()?; self.stop();
let start_sec = start_ms as f64 / 1000.0; let start_sec = start_ms as f64 / 1000.0;
let mut child = Command::new("ffmpeg") let mut child = Command::new("ffmpeg")
.args([ .args([
"-ss", &format!("{}", start_sec), "-ss",
"-i", &self.path, &format!("{}", start_sec),
"-f", "rawvideo", "-i",
"-pix_fmt", "rgb24", &self.path,
"-f",
"rawvideo",
"-pix_fmt",
"rgb24",
"-", "-",
]) ])
.stdout(Stdio::piped()) .stdout(Stdio::piped())
@@ -111,8 +120,7 @@ impl FFmpegDecoder {
.spawn() .spawn()
.context("Failed to start ffmpeg")?; .context("Failed to start ffmpeg")?;
let stdout = child.stdout.take() let stdout = child.stdout.take().context("Failed to capture stdout")?;
.context("Failed to capture stdout")?;
self.process = Some(child); self.process = Some(child);
self.stdout = Some(BufReader::new(stdout)); self.stdout = Some(BufReader::new(stdout));