Add video.rs with VideoPlayer implementation

This commit is contained in:
2026-03-19 00:31:20 +08:00
parent 152b5eb171
commit 86814d43de

107
src/player/video.rs Normal file
View File

@@ -0,0 +1,107 @@
//! 視頻播放核心實現
use anyhow::{Context, Result};
use std::path::Path;
use std::sync::{Arc, Mutex};
use std::collections::VecDeque;
use crate::player::ffmpeg::{FFmpegDecoder, VideoInfo};
#[derive(Debug, Clone)]
pub struct Frame {
pub data: Vec<u8>,
pub width: u32,
pub height: u32,
pub timestamp_ms: u64,
}
#[derive(Debug, Clone, PartialEq)]
pub enum PlayState {
Stopped,
Playing,
Paused,
}
pub struct VideoPlayer {
decoder: Option<FFmpegDecoder>,
info: Option<VideoInfo>,
state: PlayState,
current_frame: u64,
frame_buffer: Arc<Mutex<VecDeque<Frame>>>,
}
impl VideoPlayer {
pub fn new() -> Result<Self> {
Ok(Self {
decoder: None,
info: None,
state: PlayState::Stopped,
current_frame: 0,
frame_buffer: Arc::new(Mutex::new(VecDeque::new())),
})
}
pub fn open(&mut self, path: &str) -> Result<()> {
let decoder = FFmpegDecoder::new(Path::new(path))
.with_context(|| format!("Failed to open video: {}", path))?;
self.info = Some(decoder.get_info());
self.decoder = Some(decoder);
self.state = PlayState::Stopped;
self.current_frame = 0;
Ok(())
}
pub fn play(&mut self) -> Result<()> {
if self.decoder.is_none() {
anyhow::bail!("No video loaded");
}
self.state = PlayState::Playing;
Ok(())
}
pub fn pause(&mut self) -> Result<()> {
self.state = PlayState::Paused;
Ok(())
}
pub fn stop(&mut self) -> Result<()> {
self.state = PlayState::Stopped;
self.current_frame = 0;
Ok(())
}
pub fn seek_frame(&mut self, frame: u64) -> Result<()> {
if let Some(ref decoder) = self.decoder {
if let Some(info) = &self.info {
let timestamp_ms = (frame * 1000) / info.fps as u64;
decoder.seek(timestamp_ms)?;
self.current_frame = frame;
}
}
Ok(())
}
pub fn seek_time(&mut self, ms: u64) -> Result<()> {
if let Some(ref decoder) = self.decoder {
decoder.seek(ms)?;
if let Some(info) = &self.info {
self.current_frame = (ms * info.fps as u64) / 1000;
}
}
Ok(())
}
pub fn get_state(&self) -> PlayState {
self.state.clone()
}
pub fn get_current_frame(&self) -> u64 {
self.current_frame
}
pub fn get_info(&self) -> Option<&VideoInfo> {
self.info.as_ref()
}
}