feat(player): update FFmpeg decoder
This commit is contained in:
@@ -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));
|
||||||
|
|||||||
Reference in New Issue
Block a user