233 lines
8.7 KiB
Rust
233 lines
8.7 KiB
Rust
//! MoMentry Playground - Main entry point
|
|
//!
|
|
//! Unified media player with ASR/YOLO/Chunks overlay support
|
|
|
|
use anyhow::Result;
|
|
use log::{error, info};
|
|
use sdl2::pixels::PixelFormatEnum;
|
|
use std::path::Path;
|
|
|
|
mod config;
|
|
mod overlay;
|
|
mod player;
|
|
mod web;
|
|
|
|
use config::Config;
|
|
use overlay::{AsrLoader, YoloLoader};
|
|
use player::ffmpeg::FFmpegDecoder;
|
|
use player::state::{PlaybackState, PlayerState};
|
|
|
|
fn main() -> Result<()> {
|
|
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
|
|
|
|
let config = Config::load()?;
|
|
info!("MoMentry Playground starting...");
|
|
info!("Window: {}x{}", config.width, config.height);
|
|
|
|
if let Err(e) = run(&config) {
|
|
error!("Application error: {}", e);
|
|
std::process::exit(1);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn run(config: &Config) -> Result<()> {
|
|
let sdl_context = sdl2::init().map_err(|e| anyhow::anyhow!("SDL init failed: {}", e))?;
|
|
let video_subsystem = sdl_context
|
|
.video()
|
|
.map_err(|e| anyhow::anyhow!("Video subsystem failed: {}", e))?;
|
|
|
|
let window = video_subsystem
|
|
.window("MoMentry Playground", config.width, config.height)
|
|
.position_centered()
|
|
.build()
|
|
.map_err(|e| anyhow::anyhow!("Window creation failed: {}", e))?;
|
|
|
|
let mut canvas = window
|
|
.into_canvas()
|
|
.build()
|
|
.map_err(|e| anyhow::anyhow!("Canvas creation failed: {}", e))?;
|
|
|
|
let texture_creator = canvas.texture_creator();
|
|
|
|
let mut decoder: Option<FFmpegDecoder> = None;
|
|
let mut texture: Option<sdl2::render::Texture> = None;
|
|
let mut asr: Option<AsrLoader> = None;
|
|
let mut yolo: Option<YoloLoader> = None;
|
|
|
|
if let Some(ref video_path) = config.video {
|
|
info!("Loading video: {:?}", video_path);
|
|
let path = Path::new(video_path);
|
|
let mut dec = FFmpegDecoder::new(path)?;
|
|
let info = dec.get_info();
|
|
info!(
|
|
"Video info: {}x{} @ {:.2}fps, {} frames",
|
|
info.width, info.height, info.fps, info.frame_count
|
|
);
|
|
|
|
let tex = texture_creator
|
|
.create_texture_streaming(PixelFormatEnum::RGB24, info.width, info.height)
|
|
.map_err(|e| anyhow::anyhow!("Texture creation failed: {}", e))?;
|
|
|
|
texture = Some(tex);
|
|
dec.start_decoding(0)?;
|
|
decoder = Some(dec);
|
|
}
|
|
|
|
if let Some(ref asr_path) = config.asr {
|
|
info!("Loading ASR: {:?}", asr_path);
|
|
match AsrLoader::load(asr_path) {
|
|
Ok(loader) => {
|
|
info!("ASR loaded: {} segments", loader.segment_count());
|
|
asr = Some(loader);
|
|
}
|
|
Err(e) => {
|
|
error!("Failed to load ASR: {}", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
if let Some(ref yolo_path) = config.yolo {
|
|
info!("Loading YOLO: {:?}", yolo_path);
|
|
match YoloLoader::load(yolo_path) {
|
|
Ok(loader) => {
|
|
info!("YOLO loaded: {} frames", loader.total_frames());
|
|
yolo = Some(loader);
|
|
}
|
|
Err(e) => {
|
|
error!("Failed to load YOLO: {}", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
let mut player_state = PlayerState::default();
|
|
if let Some(ref dec) = decoder {
|
|
let info = dec.get_info();
|
|
player_state.total_frames = info.frame_count;
|
|
player_state.duration_ms = info.duration_ms;
|
|
player_state.fps = info.fps;
|
|
}
|
|
|
|
let mut event_pump = sdl_context
|
|
.event_pump()
|
|
.map_err(|e| anyhow::anyhow!("Event pump failed: {}", e))?;
|
|
|
|
info!("Main loop started - waiting for events...");
|
|
|
|
let mut running = true;
|
|
while running {
|
|
for event in event_pump.poll_iter() {
|
|
match event {
|
|
sdl2::event::Event::Quit { .. } => {
|
|
running = false;
|
|
}
|
|
sdl2::event::Event::KeyDown { keycode, .. } => {
|
|
if let Some(key) = keycode {
|
|
match key {
|
|
sdl2::keyboard::Keycode::Escape => running = false,
|
|
sdl2::keyboard::Keycode::Space => {
|
|
player_state.playback =
|
|
if player_state.playback == PlaybackState::Playing {
|
|
PlaybackState::Paused
|
|
} else {
|
|
PlaybackState::Playing
|
|
};
|
|
}
|
|
sdl2::keyboard::Keycode::S => {
|
|
player_state.show_subtitle = !player_state.show_subtitle;
|
|
}
|
|
sdl2::keyboard::Keycode::Y => {
|
|
player_state.show_yolo = !player_state.show_yolo;
|
|
}
|
|
sdl2::keyboard::Keycode::C => {
|
|
player_state.show_chunks = !player_state.show_chunks;
|
|
}
|
|
sdl2::keyboard::Keycode::M => {
|
|
player_state.muted = !player_state.muted;
|
|
}
|
|
sdl2::keyboard::Keycode::Left => {
|
|
if let Some(ref mut dec) = decoder {
|
|
let current = player_state.current_frame.saturating_sub(1);
|
|
dec.seek(
|
|
((current as f64 / player_state.fps) * 1000.0) as u64,
|
|
)?;
|
|
player_state.current_frame = current;
|
|
}
|
|
}
|
|
sdl2::keyboard::Keycode::Right => {
|
|
if let Some(ref mut dec) = decoder {
|
|
let current = player_state.current_frame + 1;
|
|
dec.seek(
|
|
((current as f64 / player_state.fps) * 1000.0) as u64,
|
|
)?;
|
|
player_state.current_frame = current;
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
canvas.set_draw_color(sdl2::pixels::Color::BLACK);
|
|
canvas.clear();
|
|
|
|
if player_state.playback == PlaybackState::Playing {
|
|
if let Some(ref mut dec) = decoder {
|
|
if let Some(ref mut tex) = texture {
|
|
match dec.read_frame() {
|
|
Ok(Some(data)) => {
|
|
let info = dec.get_info();
|
|
player_state.current_frame += 1;
|
|
player_state.current_time_ms =
|
|
((player_state.current_frame as f64 / info.fps) * 1000.0) as u64;
|
|
|
|
tex.update(None, &data, (info.width * 3) as usize)
|
|
.map_err(|e| anyhow::anyhow!("Texture update failed: {}", e))?;
|
|
|
|
canvas
|
|
.copy(tex, None, None)
|
|
.map_err(|e| anyhow::anyhow!("Copy failed: {}", e))?;
|
|
|
|
if player_state.show_yolo {
|
|
if let Some(ref mut yolo_loader) = yolo {
|
|
let detections =
|
|
yolo_loader.get_detections(player_state.current_frame);
|
|
for det in detections {
|
|
let x1 = det.x1 as i32;
|
|
let y1 = det.y1 as i32;
|
|
let w = (det.x2 - det.x1) as u32;
|
|
let h = (det.y2 - det.y1) as u32;
|
|
|
|
canvas.set_draw_color(sdl2::pixels::Color::RGB(0, 255, 0));
|
|
let _ =
|
|
canvas.draw_rect(sdl2::rect::Rect::new(x1, y1, w, h));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Ok(None) => {
|
|
info!("Playback ended");
|
|
break;
|
|
}
|
|
Err(e) => {
|
|
error!("Frame read error: {}", e);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
canvas.present();
|
|
|
|
std::thread::sleep(std::time::Duration::from_millis(16));
|
|
}
|
|
|
|
info!("Application closed");
|
|
Ok(())
|
|
}
|