diff --git a/src/main.rs b/src/main.rs index cc966c6..e1fb4e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,10 @@ //! MoMentry Playground - Main entry point -//! +//! //! Unified media player with ASR/YOLO/Chunks overlay support use anyhow::Result; -use clap::Parser; use log::{error, info}; +use sdl2::pixels::PixelFormatEnum; use std::path::Path; mod config; @@ -14,42 +14,67 @@ mod web; use config::Config; use overlay::{AsrLoader, YoloLoader}; -use player::{Video, Renderer, PlaybackState}; -use player::state::PlayerState; +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(); + 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 mut video = Video::new(); - let mut renderer = Renderer::new("MoMentry Playground", config.width, config.height)?; - + 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 = None; + let mut texture: Option = None; let mut asr: Option = None; let mut yolo: Option = None; - + if let Some(ref video_path) = config.video { info!("Loading video: {:?}", video_path); - let info_data = video.open(video_path)?; - info!("Video info: {}x{} @ {:.2}fps, {} frames", - info_data.width, info_data.height, info_data.fps, info_data.total_frames); - - renderer.create_texture(info_data.width, info_data.height)?; + 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) { @@ -62,7 +87,7 @@ fn run(config: &Config) -> Result<()> { } } } - + if let Some(ref yolo_path) = config.yolo { info!("Loading YOLO: {:?}", yolo_path); match YoloLoader::load(yolo_path) { @@ -75,82 +100,133 @@ fn run(config: &Config) -> Result<()> { } } } - - if config.fullscreen { - renderer.set_fullscreen(true)?; - } - + let mut player_state = PlayerState::default(); - if let Some(info) = video.get_info() { - player_state.total_frames = info.total_frames; + 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; } - - info!("Main loop started - waiting for events..."); - - if let Some(ref video_path) = config.video { - video.play()?; - player_state.playback = PlaybackState::Playing; - run_playback_loop(&mut video, &mut renderer, &mut player_state, &mut asr, &mut yolo)?; - } - - loop { - std::thread::sleep(std::time::Duration::from_millis(100)); - } -} -fn run_playback_loop( - video: &mut Video, - renderer: &mut Renderer, - state: &mut PlayerState, - asr: &mut Option, - yolo: &mut Option, -) -> Result<()> { - let frame_duration = std::time::Duration::from_millis(16); - - loop { - let start = std::time::Instant::now(); - - match video.read_frame() { - Ok(Some(frame)) => { - state.current_frame = frame.frame_number; - state.current_time_ms = frame.timestamp_ms; - - renderer.update_texture(&frame.data)?; - - if state.show_yolo { - if let Some(ref mut yolo_loader) = yolo { - let detections = yolo_loader.get_detections(frame.frame_number); - for det in detections { - renderer.draw_bbox( - det.x1 as i32, - det.y1 as i32, - (det.x2 - det.x1) as u32, - (det.y2 - det.y1) as u32, - &det.class_name, - ); + 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; + } + } + _ => {} } } } - - renderer.present(); - } - Ok(None) => { - info!("Playback ended"); - break; - } - Err(e) => { - error!("Frame read error: {}", e); - break; + _ => {} } } - - let elapsed = start.elapsed(); - if elapsed < frame_duration { - std::thread::sleep(frame_duration - elapsed); + + 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(()) }