From 72a008767415a39700873612b21ba402520d4b16 Mon Sep 17 00:00:00 2001 From: wong-justin Date: Tue, 15 Oct 2024 09:33:29 -0400 Subject: [PATCH] refactor goto-functions to draw frame too --- src/main.rs | 136 ++++++++++++++++++++++++---------------------------- 1 file changed, 62 insertions(+), 74 deletions(-) diff --git a/src/main.rs b/src/main.rs index 8bbfd7e..6f43ec0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -488,7 +488,7 @@ impl FrameIterator { return view_string; } - fn skip_some_frames(&mut self, num_frames: u32) { + fn skip_some_frames(&mut self, num_frames: u32) -> String { // When you only want to advance a few frames, // without spawning a new ffmpeg process, // but also without calling chafa.draw each frame as you would in loop { take_frame() } @@ -498,16 +498,17 @@ impl FrameIterator { for _ in 0..num_frames { self.stdout.read_exact(&mut self.pixel_buffer); } + return self.take_frame(); } - fn goto_timestamp(&mut self, timestamp: SecondsFloat) -> Result<(), Box> { + fn goto_timestamp(&mut self, timestamp: SecondsFloat) -> Result> { // Start new process at any position in video. // This should be faster than reading far ahead in the old process, // and this enables "backward seeking" too. let new_stdout = FrameIterator::_create_decoding_process(&self.video_path, timestamp)?; self.stdout = new_stdout; - Ok(()) + Ok(self.take_frame()) } } @@ -705,11 +706,9 @@ fn init() -> Result { // --- UPDATE --- // fn update(m: &mut Model, terminal_event: Event) -> UpdateResult { - m.needs_to_clear = false; match terminal_event { Event::Key(keyevent) => { - if (keyevent.modifiers == KeyModifiers::CONTROL && keyevent.code == KeyCode::Char('c')) || keyevent.code == KeyCode::Char('q') { @@ -744,7 +743,53 @@ fn update(m: &mut Model, terminal_event: Event) -> UpdateResult { return UpdateResult::Continue; } - // --- find the next frame number to render --- // + let whole_elapsed_frames = frames_since_prev_instant(m); + let is_too_fast = whole_elapsed_frames == 0; + match is_too_fast { + true => { + // slow down by not drawing anything during this tick + m.prev_instant = now; + return UpdateResult::Continue; + }, + false => { + // now we know the next frame number to render + m.frame = m.frame_iterator.skip_some_frames(whole_elapsed_frames - 1); + m.frame_number += whole_elapsed_frames as u32; + } + } + + // --- update stats --- // + + let time_to_update_fps = + m.frame_iterator.num_frames_rendered % NUM_FRAMES_TO_TRACK_FPS as u32 == 0; + if time_to_update_fps { + let recent_time_elapsed: SecondsFloat = (now - m.last_fps_check).as_secs_f64(); + m.recent_fps = Some(NUM_FRAMES_TO_TRACK_FPS as f64 / recent_time_elapsed); + m.last_fps_check = now; + } else { + // Either too early and not enough recent data, + // or waiting for next moment to check fps. + // No need to constantly update fps every frame. + } + + let time_to_log = m.frame_iterator.num_frames_rendered % 100 == 1; + if time_to_log { + log::info!( + "{:?} {:?} {:?} {:?}", + now - m.start, + m.prev_instant, + m.frame_iterator.num_frames_rendered, + m.frame_number + ); + } + + m.prev_instant = now; + return UpdateResult::Continue; +} + +fn frames_since_prev_instant(m: &mut Model) -> u32 { + // find how many frames elapsed since last tick, + // and modify leftover time // example converting elapsed ms to elapsed frames: // @@ -758,6 +803,7 @@ fn update(m: &mut Model, terminal_event: Event) -> UpdateResult { // rounding_err = ---------- * --------- = 0.007 seconds // 30 frames // + let now = std::time::Instant::now(); let elapsed_secs = (now - m.prev_instant).as_secs_f64(); // how many frames should have passed since last tick; sometimes 0, usually 1 or more @@ -769,6 +815,7 @@ fn update(m: &mut Model, terminal_event: Event) -> UpdateResult { let rounding_err: SecondsFloat = elapsed_secs - (whole_elapsed_frames as f64 * m.VIDEO_METADATA.seconds_per_frame); m.accumulated_time += rounding_err; + let need_leap_frame = m.accumulated_time > m.VIDEO_METADATA.seconds_per_frame; if need_leap_frame { whole_elapsed_frames += 1; @@ -781,57 +828,7 @@ fn update(m: &mut Model, terminal_event: Event) -> UpdateResult { // extra_time, // m.accumulated_time); - // TODO: - // cap fps if we're rendering too quickly. - // i think we should choose to save CPU by capping at decent framerate - // than to be faithful to high fps and use extra CPU - // aka - // if < 15 ms elapsed, that means we're getting +60 fps - // so probably better skip rendering at this moment and save some CPU cycles - // and still get smooth 30fps, - // than overuse CPU to get more than is necessary - // aka - // let is_too_fast = immediate_fps > min(m.VIDEO_METADATA.fps, 30fps) || elapsed_frames == 0 - - let is_too_fast = whole_elapsed_frames == 0; - if is_too_fast { - // slow down by not drawing anything during this tick - m.prev_instant = now; - return UpdateResult::Continue; - } - - // now we know the next frame number to render - m.frame_iterator.skip_some_frames(whole_elapsed_frames - 1); - m.frame = m.frame_iterator.take_frame(); - m.frame_number += whole_elapsed_frames as u32; - - // --- update stats --- // - - let time_to_update_fps = - m.frame_iterator.num_frames_rendered % NUM_FRAMES_TO_TRACK_FPS as u32 == 0; - if time_to_update_fps { - let recent_time_elapsed: SecondsFloat = (now - m.last_fps_check).as_secs_f64(); - m.recent_fps = Some(NUM_FRAMES_TO_TRACK_FPS as f64 / recent_time_elapsed); - m.last_fps_check = now; - } else { - // Either too early and not enough recent data, - // or waiting for next moment to check fps. - // No need to constantly update fps every frame. - } - - let time_to_log = m.frame_iterator.num_frames_rendered % 100 == 1; - if time_to_log { - log::info!( - "{:?} {:?} {:?} {:?}", - now - m.start, - m.prev_instant, - m.frame_iterator.num_frames_rendered, - m.frame_number - ); - } - - m.prev_instant = now; - return UpdateResult::Continue; + return whole_elapsed_frames; } fn toggle_paused(m: &mut Model) { @@ -847,12 +844,14 @@ fn toggle_controls_visibility(m: &mut Model) { m.needs_to_clear = true; // controls will still show at bottom unless cleared/drawn over } +// note: any function that modifies playerhead position aka m.frame_number in Segment mode +// must check if segment number has changed + fn seek_backwards_15s(m: &mut Model) { let frames_to_backtrack = (m.VIDEO_METADATA.fps * 15.0) as u32; m.frame_number = std::cmp::max(m.frame_number as i32 - frames_to_backtrack as i32, 0) as u32; let timestamp = m.frame_number as SecondsFloat / m.VIDEO_METADATA.fps; - m.frame_iterator.goto_timestamp(timestamp).unwrap(); - m.frame = m.frame_iterator.take_frame(); + m.frame = m.frame_iterator.goto_timestamp(timestamp).unwrap(); // TODO: update current segment // let old_position = m.hovering.position; @@ -864,8 +863,7 @@ fn seek_forwards_15s(m: &mut Model) { let frames_to_skip = (m.VIDEO_METADATA.fps * 15.0) as u32; m.frame_number += frames_to_skip; let timestamp = m.frame_number as SecondsFloat / m.VIDEO_METADATA.fps; - m.frame_iterator.goto_timestamp(timestamp).unwrap(); - m.frame = m.frame_iterator.take_frame(); + m.frame = m.frame_iterator.goto_timestamp(timestamp).unwrap(); // TODO: update current segment // let upcoming_markers = @@ -889,8 +887,7 @@ fn goto_prev_marker(m: &mut Model) { }; let timestamp: SecondsFloat = m.markers[new_position as usize]; m.frame_number = (timestamp * m.VIDEO_METADATA.fps) as u32; - m.frame_iterator.goto_timestamp(timestamp).unwrap(); - m.frame = m.frame_iterator.take_frame(); + m.frame = m.frame_iterator.goto_timestamp(timestamp).unwrap(); m.paused = true; } } @@ -917,8 +914,7 @@ fn goto_next_marker(m: &mut Model) { }; let timestamp: SecondsFloat = m.markers[new_position]; m.frame_number = (timestamp * m.VIDEO_METADATA.fps) as u32; - m.frame_iterator.goto_timestamp(timestamp).unwrap(); - m.frame = m.frame_iterator.take_frame(); + m.frame = m.frame_iterator.goto_timestamp(timestamp).unwrap(); m.paused = true; } }; @@ -926,7 +922,6 @@ fn goto_next_marker(m: &mut Model) { fn create_marker(m: &mut Model) { // create new marker at current timestamp - // match m.hovering.mode { HoverMode::Markers => (), HoverMode::Segments => { @@ -939,7 +934,6 @@ fn create_marker(m: &mut Model) { fn delete_marker(m: &mut Model) { // delete current marker and enter segments mode - // match m.hovering.mode { HoverMode::Segments => (), HoverMode::Markers => { @@ -953,18 +947,12 @@ fn advance_one_frame(m: &mut Model) { match m.paused { false => (), true => { - m.frame = m.frame_iterator.take_frame(); + m.frame = m.frame_iterator.skip_some_frames(1); m.frame_number += 1; } } } - - - - - - // --- VIEW --- // fn format_secs_to_mm_ss(seconds: SecondsFloat) -> String {