feat(player): implement SDL2 video playback with FFmpeg decoder

This commit is contained in:
accusys
2026-03-19 01:23:27 +08:00
parent 2d871a62c2
commit 0b75987fd0
10 changed files with 293 additions and 233 deletions

View File

@@ -27,18 +27,19 @@ impl AsrLoader {
pub fn load(path: &Path) -> Result<Self> {
let content = std::fs::read_to_string(path)
.with_context(|| format!("Failed to read ASR file: {:?}", path))?;
let data: AsrData = serde_json::from_str(&content)
.with_context(|| "Failed to parse ASR JSON")?;
let data: AsrData =
serde_json::from_str(&content).with_context(|| "Failed to parse ASR JSON")?;
Ok(Self { data })
}
pub fn get_segment_at(&self, time_ms: f64) -> Option<&AsrSegment> {
let time_sec = time_ms / 1000.0;
self.data.segments.iter().find(|seg| {
time_sec >= seg.start && time_sec < seg.end
})
self.data
.segments
.iter()
.find(|seg| time_sec >= seg.start && time_sec < seg.end)
}
pub fn get_text_at(&self, time_ms: f64) -> Option<String> {

View File

@@ -1,5 +1,5 @@
//! Overlay module
//!
//!
//! ASR subtitle and YOLO bbox overlay management
pub mod asr;

View File

@@ -5,7 +5,8 @@ use lru::LruCache;
use serde::Deserialize;
use std::collections::HashMap;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::io::BufReader;
use std::num::NonZeroUsize;
use std::path::Path;
#[derive(Debug, Clone, Deserialize)]
@@ -52,53 +53,39 @@ pub struct YoloData {
pub struct YoloLoader {
data: YoloData,
cache: LruCache<u64, Vec<Detection>>,
frame_index: HashMap<u64, usize>,
file_path: String,
}
impl YoloLoader {
const CACHE_SIZE: usize = 60;
pub fn load(path: &Path) -> Result<Self> {
let file_path = path.to_string_lossy().to_string();
let file = File::open(path)
.with_context(|| format!("Failed to open YOLO file: {:?}", path))?;
let file =
File::open(path).with_context(|| format!("Failed to open YOLO file: {:?}", path))?;
let reader = BufReader::new(file);
let data: YoloData = serde_json::from_reader(reader)
.with_context(|| "Failed to parse YOLO JSON")?;
let mut frame_index = HashMap::new();
for (i, (key, frame)) in data.frames.iter().enumerate() {
if let Ok(frame_num) = key.parse::<u64>() {
frame_index.insert(frame_num, i);
}
}
Ok(Self {
data,
cache: LruCache::new(Self::CACHE_SIZE),
frame_index,
file_path,
})
let data: YoloData =
serde_json::from_reader(reader).with_context(|| "Failed to parse YOLO JSON")?;
let cache = LruCache::new(NonZeroUsize::new(Self::CACHE_SIZE).unwrap());
Ok(Self { data, cache })
}
pub fn get_detections(&mut self, frame: u64) -> Vec<&Detection> {
pub fn get_detections(&mut self, frame: u64) -> Vec<Detection> {
if let Some(dets) = self.cache.get(&frame) {
return dets.iter().collect();
return dets.clone();
}
if let Some(frame_data) = self.data.frames.get(&frame.to_string()) {
let dets: Vec<Detection> = frame_data.detections.clone();
let dets = frame_data.detections.clone();
self.cache.put(frame, dets.clone());
dets.iter().collect()
dets
} else {
Vec::new()
}
}
pub fn get_detections_at_time(&mut self, time_ms: u64) -> Vec<&Detection> {
pub fn get_detections_at_time(&mut self, time_ms: u64) -> Vec<Detection> {
let fps = self.data.metadata.fps;
let frame = ((time_ms as f64 / 1000.0) * fps) as u64;
self.get_detections(frame)