feat(player): add progress bar, fullscreen toggle, Home/End keys
This commit is contained in:
144
src/main.rs
144
src/main.rs
@@ -42,7 +42,7 @@ fn run(config: &Config) -> Result<()> {
|
|||||||
|
|
||||||
let ttf_context = ttf::init().map_err(|e| anyhow::anyhow!("TTF init failed: {}", e))?;
|
let ttf_context = ttf::init().map_err(|e| anyhow::anyhow!("TTF init failed: {}", e))?;
|
||||||
let font: Option<Font> = ttf_context
|
let font: Option<Font> = ttf_context
|
||||||
.load_font("/System/Library/Fonts/Supplemental/Arial.ttf", 18)
|
.load_font("/System/Library/Fonts/Supplemental/Arial.ttf", 16)
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
let window = video_subsystem
|
let window = video_subsystem
|
||||||
@@ -63,6 +63,7 @@ fn run(config: &Config) -> Result<()> {
|
|||||||
let mut video_info = None;
|
let mut video_info = None;
|
||||||
let mut asr: Option<AsrLoader> = None;
|
let mut asr: Option<AsrLoader> = None;
|
||||||
let mut yolo: Option<YoloLoader> = None;
|
let mut yolo: Option<YoloLoader> = None;
|
||||||
|
let mut is_fullscreen = false;
|
||||||
|
|
||||||
if let Some(ref video_path) = config.video {
|
if let Some(ref video_path) = config.video {
|
||||||
info!("Loading video: {:?}", video_path);
|
info!("Loading video: {:?}", video_path);
|
||||||
@@ -121,8 +122,8 @@ fn run(config: &Config) -> Result<()> {
|
|||||||
.event_pump()
|
.event_pump()
|
||||||
.map_err(|e| anyhow::anyhow!("Event pump failed: {}", e))?;
|
.map_err(|e| anyhow::anyhow!("Event pump failed: {}", e))?;
|
||||||
|
|
||||||
let font_size = 18;
|
let header_height: i32 = 50;
|
||||||
let info_height: i32 = 50;
|
let progress_height: i32 = 30;
|
||||||
|
|
||||||
info!("Main loop started - waiting for events...");
|
info!("Main loop started - waiting for events...");
|
||||||
|
|
||||||
@@ -173,24 +174,47 @@ fn run(config: &Config) -> Result<()> {
|
|||||||
sdl2::keyboard::Keycode::M => {
|
sdl2::keyboard::Keycode::M => {
|
||||||
player_state.muted = !player_state.muted;
|
player_state.muted = !player_state.muted;
|
||||||
}
|
}
|
||||||
|
sdl2::keyboard::Keycode::F => {
|
||||||
|
is_fullscreen = !is_fullscreen;
|
||||||
|
}
|
||||||
|
sdl2::keyboard::Keycode::Home => {
|
||||||
|
if let Some(ref mut dec) = decoder {
|
||||||
|
dec.seek(0).ok();
|
||||||
|
player_state.current_frame = 0;
|
||||||
|
player_state.current_time_ms = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sdl2::keyboard::Keycode::End => {
|
||||||
|
if let Some(ref mut dec) = decoder {
|
||||||
|
let last_frame = player_state.total_frames.saturating_sub(1);
|
||||||
|
dec.seek(
|
||||||
|
((last_frame as f64 / player_state.fps) * 1000.0) as u64,
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
player_state.current_frame = last_frame;
|
||||||
|
player_state.current_time_ms = player_state.duration_ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
sdl2::keyboard::Keycode::Left => {
|
sdl2::keyboard::Keycode::Left => {
|
||||||
let step = if shift { 60 } else { 1 };
|
let step = if shift { 60 } else { 1 };
|
||||||
if let Some(ref mut dec) = decoder {
|
if let Some(ref mut dec) = decoder {
|
||||||
let current = player_state.current_frame.saturating_sub(step);
|
let current = player_state.current_frame.saturating_sub(step);
|
||||||
dec.seek(
|
dec.seek(((current as f64 / player_state.fps) * 1000.0) as u64)
|
||||||
((current as f64 / player_state.fps) * 1000.0) as u64,
|
.ok();
|
||||||
)?;
|
|
||||||
player_state.current_frame = current;
|
player_state.current_frame = current;
|
||||||
|
player_state.current_time_ms =
|
||||||
|
((current as f64 / player_state.fps) * 1000.0) as u64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sdl2::keyboard::Keycode::Right => {
|
sdl2::keyboard::Keycode::Right => {
|
||||||
let step = if shift { 60 } else { 1 };
|
let step = if shift { 60 } else { 1 };
|
||||||
if let Some(ref mut dec) = decoder {
|
if let Some(ref mut dec) = decoder {
|
||||||
let current = player_state.current_frame + step;
|
let current = player_state.current_frame + step;
|
||||||
dec.seek(
|
dec.seek(((current as f64 / player_state.fps) * 1000.0) as u64)
|
||||||
((current as f64 / player_state.fps) * 1000.0) as u64,
|
.ok();
|
||||||
)?;
|
|
||||||
player_state.current_frame = current;
|
player_state.current_frame = current;
|
||||||
|
player_state.current_time_ms =
|
||||||
|
((current as f64 / player_state.fps) * 1000.0) as u64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sdl2::keyboard::Keycode::Up => {
|
sdl2::keyboard::Keycode::Up => {
|
||||||
@@ -240,6 +264,13 @@ fn run(config: &Config) -> Result<()> {
|
|||||||
canvas.set_draw_color(sdl2::pixels::Color::BLACK);
|
canvas.set_draw_color(sdl2::pixels::Color::BLACK);
|
||||||
canvas.clear();
|
canvas.clear();
|
||||||
|
|
||||||
|
let fs_type = if is_fullscreen {
|
||||||
|
sdl2::video::FullscreenType::Desktop
|
||||||
|
} else {
|
||||||
|
sdl2::video::FullscreenType::Off
|
||||||
|
};
|
||||||
|
canvas.window_mut().set_fullscreen(fs_type).ok();
|
||||||
|
|
||||||
if player_state.playback == PlaybackState::Playing {
|
if player_state.playback == PlaybackState::Playing {
|
||||||
if let Some(ref mut dec) = decoder {
|
if let Some(ref mut dec) = decoder {
|
||||||
if let Some(ref mut tex) = texture {
|
if let Some(ref mut tex) = texture {
|
||||||
@@ -252,7 +283,8 @@ fn run(config: &Config) -> Result<()> {
|
|||||||
as u64;
|
as u64;
|
||||||
|
|
||||||
tex.update(None, &data, (info.width * 3) as usize)
|
tex.update(None, &data, (info.width * 3) as usize)
|
||||||
.map_err(|e| anyhow::anyhow!("Texture update failed: {}", e))?;
|
.map_err(|e| anyhow::anyhow!("Texture update failed: {}", e))
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
@@ -273,11 +305,12 @@ fn run(config: &Config) -> Result<()> {
|
|||||||
.map(|i| (i.width, i.height))
|
.map(|i| (i.width, i.height))
|
||||||
.unwrap_or((config.width, config.height));
|
.unwrap_or((config.width, config.height));
|
||||||
|
|
||||||
|
let video_area_height = config.height as i32 - header_height - progress_height;
|
||||||
let scale: f64 = if player_state.zoom != 1.0 {
|
let scale: f64 = if player_state.zoom != 1.0 {
|
||||||
player_state.zoom as f64
|
player_state.zoom as f64
|
||||||
} else {
|
} else {
|
||||||
let scale_x = config.width as f64 / vid_width as f64;
|
let scale_x = config.width as f64 / vid_width as f64;
|
||||||
let scale_y = (config.height as i32 - info_height) as f64 / vid_height as f64;
|
let scale_y = video_area_height as f64 / vid_height as f64;
|
||||||
scale_x.min(scale_y).min(1.0)
|
scale_x.min(scale_y).min(1.0)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -285,7 +318,7 @@ fn run(config: &Config) -> Result<()> {
|
|||||||
let scaled_h = (vid_height as f64 * scale) as u32;
|
let scaled_h = (vid_height as f64 * scale) as u32;
|
||||||
let offset_x =
|
let offset_x =
|
||||||
((config.width as i32 - scaled_w as i32) / 2) as i32 + player_state.pan_x as i32;
|
((config.width as i32 - scaled_w as i32) / 2) as i32 + player_state.pan_x as i32;
|
||||||
let offset_y = ((config.height as i32 - info_height - scaled_h as i32) / 2) as i32
|
let offset_y = ((config.height as i32 - progress_height - scaled_h as i32) / 2) as i32
|
||||||
+ player_state.pan_y as i32;
|
+ player_state.pan_y as i32;
|
||||||
|
|
||||||
if let Some(ref mut tex) = texture {
|
if let Some(ref mut tex) = texture {
|
||||||
@@ -316,7 +349,7 @@ fn run(config: &Config) -> Result<()> {
|
|||||||
texture_creator.create_texture_from_surface(&surface)
|
texture_creator.create_texture_from_surface(&surface)
|
||||||
{
|
{
|
||||||
let lw = surface.width().min(w as u32);
|
let lw = surface.width().min(w as u32);
|
||||||
let label_rect = Rect::new(x1, y1.saturating_sub(20), lw, 18);
|
let label_rect = Rect::new(x1, y1.saturating_sub(18), lw, 16);
|
||||||
canvas.copy(&tex_label, None, Some(label_rect)).ok();
|
canvas.copy(&tex_label, None, Some(label_rect)).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -339,14 +372,18 @@ fn run(config: &Config) -> Result<()> {
|
|||||||
{
|
{
|
||||||
let query = tex_label.query();
|
let query = tex_label.query();
|
||||||
let x = (config.width - query.width) / 2;
|
let x = (config.width - query.width) / 2;
|
||||||
let y = config.height - query.height - 20;
|
let y = config.height as i32
|
||||||
let rect = Rect::new(x as i32, y as i32, query.width, query.height);
|
- progress_height
|
||||||
|
- query.height as i32
|
||||||
|
- 10;
|
||||||
|
let rect =
|
||||||
|
Rect::new(x as i32, y as i32, query.width as u32, query.height);
|
||||||
canvas.set_draw_color(sdl2::pixels::Color::RGBA(0, 0, 0, 200));
|
canvas.set_draw_color(sdl2::pixels::Color::RGBA(0, 0, 0, 200));
|
||||||
let _ = canvas.fill_rect(Rect::new(
|
let _ = canvas.fill_rect(Rect::new(
|
||||||
rect.x() - 8,
|
rect.x() - 6,
|
||||||
rect.y() - 4,
|
rect.y() - 3,
|
||||||
rect.width() + 16,
|
rect.width() + 12,
|
||||||
rect.height() + 8,
|
rect.height() + 6,
|
||||||
));
|
));
|
||||||
canvas.copy(&tex_label, None, Some(rect)).ok();
|
canvas.copy(&tex_label, None, Some(rect)).ok();
|
||||||
}
|
}
|
||||||
@@ -358,37 +395,66 @@ fn run(config: &Config) -> Result<()> {
|
|||||||
|
|
||||||
if let Some(ref f) = font {
|
if let Some(ref f) = font {
|
||||||
let time_str = format_time(player_state.current_time_ms);
|
let time_str = format_time(player_state.current_time_ms);
|
||||||
|
let duration_str = format_time(player_state.duration_ms);
|
||||||
let line1 = format!(
|
let line1 = format!(
|
||||||
"Time: {} Frame: {}/{} FPS: {:.1}",
|
"{} / {} Frame: {}/{} {:.1}fps",
|
||||||
time_str, player_state.current_frame, player_state.total_frames, player_state.fps
|
time_str,
|
||||||
|
duration_str,
|
||||||
|
player_state.current_frame,
|
||||||
|
player_state.total_frames,
|
||||||
|
player_state.fps
|
||||||
);
|
);
|
||||||
let line2 = format!(
|
let line2 = format!(
|
||||||
"[S]ubtitle:{} [Y]OLO:{} [C]hunks [M]ute [+/-]Zoom{:.1}x [`]Reset",
|
"[S]ub {} [Y]olo {} [C]hunks [M]ute [+/-]Zoom{:.1}x [F]ullscreen [`]Reset",
|
||||||
if player_state.show_subtitle {
|
if player_state.show_subtitle {
|
||||||
" ON "
|
"ON"
|
||||||
} else {
|
} else {
|
||||||
" OFF"
|
"OFF"
|
||||||
},
|
|
||||||
if player_state.show_yolo {
|
|
||||||
" ON "
|
|
||||||
} else {
|
|
||||||
" OFF"
|
|
||||||
},
|
},
|
||||||
|
if player_state.show_yolo { "ON" } else { "OFF" },
|
||||||
player_state.zoom
|
player_state.zoom
|
||||||
);
|
);
|
||||||
|
|
||||||
for (i, line) in [line1, line2].iter().enumerate() {
|
if let Ok(surface) = f
|
||||||
if let Ok(surface) = f
|
.render(&line1)
|
||||||
.render(line)
|
.solid(sdl2::pixels::Color::RGB(200, 200, 200))
|
||||||
.solid(sdl2::pixels::Color::RGB(180, 180, 180))
|
{
|
||||||
{
|
if let Ok(tex_label) = texture_creator.create_texture_from_surface(&surface) {
|
||||||
if let Ok(tex_label) = texture_creator.create_texture_from_surface(&surface) {
|
let rect = Rect::new(10, 10, surface.width(), surface.height());
|
||||||
let rect =
|
canvas.copy(&tex_label, None, Some(rect)).ok();
|
||||||
Rect::new(10, 8 + (i as i32 * 20), surface.width(), surface.height());
|
|
||||||
canvas.copy(&tex_label, None, Some(rect)).ok();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Ok(surface) = f
|
||||||
|
.render(&line2)
|
||||||
|
.solid(sdl2::pixels::Color::RGB(150, 150, 150))
|
||||||
|
{
|
||||||
|
if let Ok(tex_label) = texture_creator.create_texture_from_surface(&surface) {
|
||||||
|
let rect = Rect::new(10, 30, surface.width(), surface.height());
|
||||||
|
canvas.copy(&tex_label, None, Some(rect)).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let progress_y = config.height as i32 - progress_height + 5;
|
||||||
|
let progress_bar_height = 8i32;
|
||||||
|
canvas.set_draw_color(sdl2::pixels::Color::RGB(50, 50, 50));
|
||||||
|
let _ = canvas.fill_rect(Rect::new(
|
||||||
|
10,
|
||||||
|
progress_y,
|
||||||
|
(config.width - 20) as u32,
|
||||||
|
progress_bar_height as u32,
|
||||||
|
));
|
||||||
|
|
||||||
|
if player_state.total_frames > 0 {
|
||||||
|
let progress = player_state.current_frame as f64 / player_state.total_frames as f64;
|
||||||
|
let progress_width = ((config.width - 20) as f64 * progress) as u32;
|
||||||
|
canvas.set_draw_color(sdl2::pixels::Color::RGB(100, 150, 255));
|
||||||
|
let _ = canvas.fill_rect(Rect::new(
|
||||||
|
10,
|
||||||
|
progress_y,
|
||||||
|
progress_width,
|
||||||
|
progress_bar_height as u32,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.present();
|
canvas.present();
|
||||||
|
|||||||
Reference in New Issue
Block a user