feat(player): add chunk/scene markers on progress bar

This commit is contained in:
accusys
2026-03-19 01:54:12 +08:00
parent dbcd7ba5e9
commit 722af4fe87
16 changed files with 137 additions and 11 deletions

View File

@@ -19,6 +19,9 @@ pub struct Config {
#[arg(short = 'y', long = "yolo", help = "YOLO JSON file path")]
pub yolo: Option<PathBuf>,
#[arg(short = 'c', long = "chunks", help = "Chunk/Cut JSON file path")]
pub chunks: Option<PathBuf>,
#[arg(
short = 'w',
long = "width",

View File

@@ -15,7 +15,7 @@ mod player;
mod web;
use config::Config;
use overlay::{AsrLoader, YoloLoader};
use overlay::{AsrLoader, ChunkLoader, YoloLoader};
use player::ffmpeg::FFmpegDecoder;
use player::state::{PlaybackState, PlayerState};
@@ -63,6 +63,7 @@ fn run(config: &Config) -> Result<()> {
let mut video_info = None;
let mut asr: Option<AsrLoader> = None;
let mut yolo: Option<YoloLoader> = None;
let mut chunks: Option<ChunkLoader> = None;
let mut is_fullscreen = false;
if let Some(ref video_path) = config.video {
@@ -111,6 +112,19 @@ fn run(config: &Config) -> Result<()> {
}
}
if let Some(ref chunks_path) = config.chunks {
info!("Loading Chunks: {:?}", chunks_path);
match ChunkLoader::load(Path::new(chunks_path)) {
Ok(loader) => {
info!("Chunks loaded: {} scenes", loader.scene_count());
chunks = Some(loader);
}
Err(e) => {
error!("Failed to load Chunks: {}", e);
}
}
}
let mut player_state = PlayerState::default();
if let Some(ref info) = video_info {
player_state.total_frames = info.frame_count;
@@ -510,6 +524,58 @@ fn run(config: &Config) -> Result<()> {
progress_width,
progress_bar_height as u32,
));
if player_state.show_chunks {
if let Some(ref chunk_loader) = chunks {
let bar_start = 10i32;
let bar_width = config.width as i32 - 20;
let boundaries = chunk_loader.get_scene_boundaries();
for &boundary_frame in &boundaries {
let ratio = boundary_frame as f64 / player_state.total_frames as f64;
let x_pos = (bar_start + (bar_width as f64 * ratio) as i32)
.min(bar_start + bar_width - 1);
if x_pos > bar_start && x_pos < bar_start + bar_width {
canvas.set_draw_color(sdl2::pixels::Color::RGB(255, 200, 50));
let _ = canvas.fill_rect(Rect::new(
x_pos,
progress_y - 2,
2,
(progress_bar_height + 4) as u32,
));
}
}
if let Some(current_scene) =
chunk_loader.get_current_scene(player_state.current_frame)
{
if let Some(ref font) = font {
let scene_text = format!("Scene {}", current_scene.scene_number);
if let Ok(surface) = font
.render(&scene_text)
.solid(sdl2::pixels::Color::RGB(255, 200, 50))
{
if let Ok(tex) =
texture_creator.create_texture_from_surface(&surface)
{
let rect = Rect::new(
bar_start + bar_width - surface.width() as i32 - 10,
progress_y - 25,
surface.width(),
surface.height(),
);
canvas.set_draw_color(sdl2::pixels::Color::RGBA(0, 0, 0, 180));
let _ = canvas.fill_rect(Rect::new(
rect.x() - 4,
rect.y() - 2,
rect.width() + 8,
rect.height() + 4,
));
canvas.copy(&tex, None, Some(rect)).ok();
}
}
}
}
}
}
}
canvas.present();

52
src/overlay/chunk.rs Normal file
View File

@@ -0,0 +1,52 @@
use anyhow::Context;
use log::info;
use serde::Deserialize;
use std::fs;
use std::path::Path;
#[derive(Debug, Deserialize)]
pub struct CutData {
pub frame_count: u64,
pub fps: f64,
pub scenes: Vec<Scene>,
}
#[derive(Debug, Deserialize, Clone)]
pub struct Scene {
pub scene_number: u32,
pub start_frame: u64,
pub end_frame: u64,
pub start_time: f64,
pub end_time: f64,
}
#[derive(Debug)]
pub struct ChunkLoader {
pub data: CutData,
}
impl ChunkLoader {
pub fn load(path: &Path) -> anyhow::Result<Self> {
let content = fs::read_to_string(path)
.with_context(|| format!("Failed to read chunk file: {:?}", path))?;
let data: CutData = serde_json::from_str(&content)
.with_context(|| format!("Failed to parse chunk JSON: {:?}", path))?;
info!("Loaded {} scenes from {:?}", data.scenes.len(), path);
Ok(Self { data })
}
pub fn scene_count(&self) -> usize {
self.data.scenes.len()
}
pub fn get_scene_boundaries(&self) -> Vec<u64> {
self.data.scenes.iter().map(|s| s.start_frame).collect()
}
pub fn get_current_scene(&self, frame: u64) -> Option<&Scene> {
self.data
.scenes
.iter()
.find(|s| frame >= s.start_frame && frame <= s.end_frame)
}
}

View File

@@ -1,9 +1,11 @@
//! Overlay module
//!
//! ASR subtitle and YOLO bbox overlay management
//! ASR subtitle, YOLO bbox, and chunk marker overlay management
pub mod asr;
pub mod chunk;
pub mod yolo;
pub use asr::AsrLoader;
pub use chunk::ChunkLoader;
pub use yolo::YoloLoader;

File diff suppressed because one or more lines are too long

View File

@@ -1,11 +1,12 @@
/Users/accusys/momentry_playground/target/release/deps/momentry-d0e10ec51fcac34b.d: src/main.rs src/config.rs src/overlay/mod.rs src/overlay/asr.rs src/overlay/yolo.rs src/player/mod.rs src/player/ffmpeg.rs src/player/renderer.rs src/player/state.rs src/player/video.rs src/web/mod.rs src/web/bridge.rs
/Users/accusys/momentry_playground/target/release/deps/momentry-d0e10ec51fcac34b.d: src/main.rs src/config.rs src/overlay/mod.rs src/overlay/asr.rs src/overlay/chunk.rs src/overlay/yolo.rs src/player/mod.rs src/player/ffmpeg.rs src/player/renderer.rs src/player/state.rs src/player/video.rs src/web/mod.rs src/web/bridge.rs
/Users/accusys/momentry_playground/target/release/deps/momentry-d0e10ec51fcac34b: src/main.rs src/config.rs src/overlay/mod.rs src/overlay/asr.rs src/overlay/yolo.rs src/player/mod.rs src/player/ffmpeg.rs src/player/renderer.rs src/player/state.rs src/player/video.rs src/web/mod.rs src/web/bridge.rs
/Users/accusys/momentry_playground/target/release/deps/momentry-d0e10ec51fcac34b: src/main.rs src/config.rs src/overlay/mod.rs src/overlay/asr.rs src/overlay/chunk.rs src/overlay/yolo.rs src/player/mod.rs src/player/ffmpeg.rs src/player/renderer.rs src/player/state.rs src/player/video.rs src/web/mod.rs src/web/bridge.rs
src/main.rs:
src/config.rs:
src/overlay/mod.rs:
src/overlay/asr.rs:
src/overlay/chunk.rs:
src/overlay/yolo.rs:
src/player/mod.rs:
src/player/ffmpeg.rs:

View File

@@ -1,8 +1,8 @@
/Users/accusys/momentry_playground/target/release/deps/momentry_playground-f7686bde04eb7bb7.d: src/lib.rs src/player/mod.rs src/player/ffmpeg.rs src/player/renderer.rs src/player/state.rs src/player/video.rs src/overlay/mod.rs src/overlay/asr.rs src/overlay/yolo.rs src/web/mod.rs src/web/bridge.rs src/config.rs
/Users/accusys/momentry_playground/target/release/deps/momentry_playground-f7686bde04eb7bb7.d: src/lib.rs src/player/mod.rs src/player/ffmpeg.rs src/player/renderer.rs src/player/state.rs src/player/video.rs src/overlay/mod.rs src/overlay/asr.rs src/overlay/chunk.rs src/overlay/yolo.rs src/web/mod.rs src/web/bridge.rs src/config.rs
/Users/accusys/momentry_playground/target/release/deps/libmomentry_playground-f7686bde04eb7bb7.rlib: src/lib.rs src/player/mod.rs src/player/ffmpeg.rs src/player/renderer.rs src/player/state.rs src/player/video.rs src/overlay/mod.rs src/overlay/asr.rs src/overlay/yolo.rs src/web/mod.rs src/web/bridge.rs src/config.rs
/Users/accusys/momentry_playground/target/release/deps/libmomentry_playground-f7686bde04eb7bb7.rlib: src/lib.rs src/player/mod.rs src/player/ffmpeg.rs src/player/renderer.rs src/player/state.rs src/player/video.rs src/overlay/mod.rs src/overlay/asr.rs src/overlay/chunk.rs src/overlay/yolo.rs src/web/mod.rs src/web/bridge.rs src/config.rs
/Users/accusys/momentry_playground/target/release/deps/libmomentry_playground-f7686bde04eb7bb7.rmeta: src/lib.rs src/player/mod.rs src/player/ffmpeg.rs src/player/renderer.rs src/player/state.rs src/player/video.rs src/overlay/mod.rs src/overlay/asr.rs src/overlay/yolo.rs src/web/mod.rs src/web/bridge.rs src/config.rs
/Users/accusys/momentry_playground/target/release/deps/libmomentry_playground-f7686bde04eb7bb7.rmeta: src/lib.rs src/player/mod.rs src/player/ffmpeg.rs src/player/renderer.rs src/player/state.rs src/player/video.rs src/overlay/mod.rs src/overlay/asr.rs src/overlay/chunk.rs src/overlay/yolo.rs src/web/mod.rs src/web/bridge.rs src/config.rs
src/lib.rs:
src/player/mod.rs:
@@ -12,6 +12,7 @@ src/player/state.rs:
src/player/video.rs:
src/overlay/mod.rs:
src/overlay/asr.rs:
src/overlay/chunk.rs:
src/overlay/yolo.rs:
src/web/mod.rs:
src/web/bridge.rs:

View File

@@ -1 +1 @@
/Users/accusys/momentry_playground/target/release/libmomentry_playground.rlib: /Users/accusys/momentry_playground/src/config.rs /Users/accusys/momentry_playground/src/lib.rs /Users/accusys/momentry_playground/src/overlay/asr.rs /Users/accusys/momentry_playground/src/overlay/mod.rs /Users/accusys/momentry_playground/src/overlay/yolo.rs /Users/accusys/momentry_playground/src/player/ffmpeg.rs /Users/accusys/momentry_playground/src/player/mod.rs /Users/accusys/momentry_playground/src/player/renderer.rs /Users/accusys/momentry_playground/src/player/state.rs /Users/accusys/momentry_playground/src/player/video.rs /Users/accusys/momentry_playground/src/web/bridge.rs /Users/accusys/momentry_playground/src/web/mod.rs
/Users/accusys/momentry_playground/target/release/libmomentry_playground.rlib: /Users/accusys/momentry_playground/src/config.rs /Users/accusys/momentry_playground/src/lib.rs /Users/accusys/momentry_playground/src/overlay/asr.rs /Users/accusys/momentry_playground/src/overlay/chunk.rs /Users/accusys/momentry_playground/src/overlay/mod.rs /Users/accusys/momentry_playground/src/overlay/yolo.rs /Users/accusys/momentry_playground/src/player/ffmpeg.rs /Users/accusys/momentry_playground/src/player/mod.rs /Users/accusys/momentry_playground/src/player/renderer.rs /Users/accusys/momentry_playground/src/player/state.rs /Users/accusys/momentry_playground/src/player/video.rs /Users/accusys/momentry_playground/src/web/bridge.rs /Users/accusys/momentry_playground/src/web/mod.rs

Binary file not shown.

View File

@@ -1 +1 @@
/Users/accusys/momentry_playground/target/release/momentry: /Users/accusys/momentry_playground/src/config.rs /Users/accusys/momentry_playground/src/lib.rs /Users/accusys/momentry_playground/src/main.rs /Users/accusys/momentry_playground/src/overlay/asr.rs /Users/accusys/momentry_playground/src/overlay/mod.rs /Users/accusys/momentry_playground/src/overlay/yolo.rs /Users/accusys/momentry_playground/src/player/ffmpeg.rs /Users/accusys/momentry_playground/src/player/mod.rs /Users/accusys/momentry_playground/src/player/renderer.rs /Users/accusys/momentry_playground/src/player/state.rs /Users/accusys/momentry_playground/src/player/video.rs /Users/accusys/momentry_playground/src/web/bridge.rs /Users/accusys/momentry_playground/src/web/mod.rs
/Users/accusys/momentry_playground/target/release/momentry: /Users/accusys/momentry_playground/src/config.rs /Users/accusys/momentry_playground/src/lib.rs /Users/accusys/momentry_playground/src/main.rs /Users/accusys/momentry_playground/src/overlay/asr.rs /Users/accusys/momentry_playground/src/overlay/chunk.rs /Users/accusys/momentry_playground/src/overlay/mod.rs /Users/accusys/momentry_playground/src/overlay/yolo.rs /Users/accusys/momentry_playground/src/player/ffmpeg.rs /Users/accusys/momentry_playground/src/player/mod.rs /Users/accusys/momentry_playground/src/player/renderer.rs /Users/accusys/momentry_playground/src/player/state.rs /Users/accusys/momentry_playground/src/player/video.rs /Users/accusys/momentry_playground/src/web/bridge.rs /Users/accusys/momentry_playground/src/web/mod.rs