@@ -488,7 +488,7 @@ impl FrameIterator {
488
488
return view_string;
489
489
}
490
490
491
- fn skip_some_frames ( & mut self , num_frames : u32 ) {
491
+ fn skip_some_frames ( & mut self , num_frames : u32 ) -> String {
492
492
// When you only want to advance a few frames,
493
493
// without spawning a new ffmpeg process,
494
494
// but also without calling chafa.draw each frame as you would in loop { take_frame() }
@@ -498,16 +498,17 @@ impl FrameIterator {
498
498
for _ in 0 ..num_frames {
499
499
self . stdout . read_exact ( & mut self . pixel_buffer ) ;
500
500
}
501
+ return self . take_frame ( ) ;
501
502
}
502
503
503
- fn goto_timestamp ( & mut self , timestamp : SecondsFloat ) -> Result < ( ) , Box < dyn Error > > {
504
+ fn goto_timestamp ( & mut self , timestamp : SecondsFloat ) -> Result < String , Box < dyn Error > > {
504
505
// Start new process at any position in video.
505
506
// This should be faster than reading far ahead in the old process,
506
507
// and this enables "backward seeking" too.
507
508
508
509
let new_stdout = FrameIterator :: _create_decoding_process ( & self . video_path , timestamp) ?;
509
510
self . stdout = new_stdout;
510
- Ok ( ( ) )
511
+ Ok ( self . take_frame ( ) )
511
512
}
512
513
}
513
514
@@ -705,11 +706,9 @@ fn init() -> Result<Model, String> {
705
706
// --- UPDATE --- //
706
707
707
708
fn update ( m : & mut Model , terminal_event : Event ) -> UpdateResult {
708
-
709
709
m. needs_to_clear = false ;
710
710
match terminal_event {
711
711
Event :: Key ( keyevent) => {
712
-
713
712
if ( keyevent. modifiers == KeyModifiers :: CONTROL && keyevent. code == KeyCode :: Char ( 'c' ) )
714
713
|| keyevent. code == KeyCode :: Char ( 'q' )
715
714
{
@@ -744,7 +743,53 @@ fn update(m: &mut Model, terminal_event: Event) -> UpdateResult {
744
743
return UpdateResult :: Continue ;
745
744
}
746
745
747
- // --- find the next frame number to render --- //
746
+ let whole_elapsed_frames = frames_since_prev_instant ( m) ;
747
+ let is_too_fast = whole_elapsed_frames == 0 ;
748
+ match is_too_fast {
749
+ true => {
750
+ // slow down by not drawing anything during this tick
751
+ m. prev_instant = now;
752
+ return UpdateResult :: Continue ;
753
+ } ,
754
+ false => {
755
+ // now we know the next frame number to render
756
+ m. frame = m. frame_iterator . skip_some_frames ( whole_elapsed_frames - 1 ) ;
757
+ m. frame_number += whole_elapsed_frames as u32 ;
758
+ }
759
+ }
760
+
761
+ // --- update stats --- //
762
+
763
+ let time_to_update_fps =
764
+ m. frame_iterator . num_frames_rendered % NUM_FRAMES_TO_TRACK_FPS as u32 == 0 ;
765
+ if time_to_update_fps {
766
+ let recent_time_elapsed: SecondsFloat = ( now - m. last_fps_check ) . as_secs_f64 ( ) ;
767
+ m. recent_fps = Some ( NUM_FRAMES_TO_TRACK_FPS as f64 / recent_time_elapsed) ;
768
+ m. last_fps_check = now;
769
+ } else {
770
+ // Either too early and not enough recent data,
771
+ // or waiting for next moment to check fps.
772
+ // No need to constantly update fps every frame.
773
+ }
774
+
775
+ let time_to_log = m. frame_iterator . num_frames_rendered % 100 == 1 ;
776
+ if time_to_log {
777
+ log:: info!(
778
+ "{:?} {:?} {:?} {:?}" ,
779
+ now - m. start,
780
+ m. prev_instant,
781
+ m. frame_iterator. num_frames_rendered,
782
+ m. frame_number
783
+ ) ;
784
+ }
785
+
786
+ m. prev_instant = now;
787
+ return UpdateResult :: Continue ;
788
+ }
789
+
790
+ fn frames_since_prev_instant ( m : & mut Model ) -> u32 {
791
+ // find how many frames elapsed since last tick,
792
+ // and modify leftover time
748
793
749
794
// example converting elapsed ms to elapsed frames:
750
795
//
@@ -758,6 +803,7 @@ fn update(m: &mut Model, terminal_event: Event) -> UpdateResult {
758
803
// rounding_err = ---------- * --------- = 0.007 seconds
759
804
// 30 frames
760
805
//
806
+ let now = std:: time:: Instant :: now ( ) ;
761
807
let elapsed_secs = ( now - m. prev_instant ) . as_secs_f64 ( ) ;
762
808
763
809
// 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 {
769
815
let rounding_err: SecondsFloat =
770
816
elapsed_secs - ( whole_elapsed_frames as f64 * m. VIDEO_METADATA . seconds_per_frame ) ;
771
817
m. accumulated_time += rounding_err;
818
+
772
819
let need_leap_frame = m. accumulated_time > m. VIDEO_METADATA . seconds_per_frame ;
773
820
if need_leap_frame {
774
821
whole_elapsed_frames += 1 ;
@@ -781,57 +828,7 @@ fn update(m: &mut Model, terminal_event: Event) -> UpdateResult {
781
828
// extra_time,
782
829
// m.accumulated_time);
783
830
784
- // TODO:
785
- // cap fps if we're rendering too quickly.
786
- // i think we should choose to save CPU by capping at decent framerate
787
- // than to be faithful to high fps and use extra CPU
788
- // aka
789
- // if < 15 ms elapsed, that means we're getting +60 fps
790
- // so probably better skip rendering at this moment and save some CPU cycles
791
- // and still get smooth 30fps,
792
- // than overuse CPU to get more than is necessary
793
- // aka
794
- // let is_too_fast = immediate_fps > min(m.VIDEO_METADATA.fps, 30fps) || elapsed_frames == 0
795
-
796
- let is_too_fast = whole_elapsed_frames == 0 ;
797
- if is_too_fast {
798
- // slow down by not drawing anything during this tick
799
- m. prev_instant = now;
800
- return UpdateResult :: Continue ;
801
- }
802
-
803
- // now we know the next frame number to render
804
- m. frame_iterator . skip_some_frames ( whole_elapsed_frames - 1 ) ;
805
- m. frame = m. frame_iterator . take_frame ( ) ;
806
- m. frame_number += whole_elapsed_frames as u32 ;
807
-
808
- // --- update stats --- //
809
-
810
- let time_to_update_fps =
811
- m. frame_iterator . num_frames_rendered % NUM_FRAMES_TO_TRACK_FPS as u32 == 0 ;
812
- if time_to_update_fps {
813
- let recent_time_elapsed: SecondsFloat = ( now - m. last_fps_check ) . as_secs_f64 ( ) ;
814
- m. recent_fps = Some ( NUM_FRAMES_TO_TRACK_FPS as f64 / recent_time_elapsed) ;
815
- m. last_fps_check = now;
816
- } else {
817
- // Either too early and not enough recent data,
818
- // or waiting for next moment to check fps.
819
- // No need to constantly update fps every frame.
820
- }
821
-
822
- let time_to_log = m. frame_iterator . num_frames_rendered % 100 == 1 ;
823
- if time_to_log {
824
- log:: info!(
825
- "{:?} {:?} {:?} {:?}" ,
826
- now - m. start,
827
- m. prev_instant,
828
- m. frame_iterator. num_frames_rendered,
829
- m. frame_number
830
- ) ;
831
- }
832
-
833
- m. prev_instant = now;
834
- return UpdateResult :: Continue ;
831
+ return whole_elapsed_frames;
835
832
}
836
833
837
834
fn toggle_paused ( m : & mut Model ) {
@@ -847,12 +844,14 @@ fn toggle_controls_visibility(m: &mut Model) {
847
844
m. needs_to_clear = true ; // controls will still show at bottom unless cleared/drawn over
848
845
}
849
846
847
+ // note: any function that modifies playerhead position aka m.frame_number in Segment mode
848
+ // must check if segment number has changed
849
+
850
850
fn seek_backwards_15s ( m : & mut Model ) {
851
851
let frames_to_backtrack = ( m. VIDEO_METADATA . fps * 15.0 ) as u32 ;
852
852
m. frame_number = std:: cmp:: max ( m. frame_number as i32 - frames_to_backtrack as i32 , 0 ) as u32 ;
853
853
let timestamp = m. frame_number as SecondsFloat / m. VIDEO_METADATA . fps ;
854
- m. frame_iterator . goto_timestamp ( timestamp) . unwrap ( ) ;
855
- m. frame = m. frame_iterator . take_frame ( ) ;
854
+ m. frame = m. frame_iterator . goto_timestamp ( timestamp) . unwrap ( ) ;
856
855
857
856
// TODO: update current segment
858
857
// let old_position = m.hovering.position;
@@ -864,8 +863,7 @@ fn seek_forwards_15s(m: &mut Model) {
864
863
let frames_to_skip = ( m. VIDEO_METADATA . fps * 15.0 ) as u32 ;
865
864
m. frame_number += frames_to_skip;
866
865
let timestamp = m. frame_number as SecondsFloat / m. VIDEO_METADATA . fps ;
867
- m. frame_iterator . goto_timestamp ( timestamp) . unwrap ( ) ;
868
- m. frame = m. frame_iterator . take_frame ( ) ;
866
+ m. frame = m. frame_iterator . goto_timestamp ( timestamp) . unwrap ( ) ;
869
867
870
868
// TODO: update current segment
871
869
// let upcoming_markers =
@@ -889,8 +887,7 @@ fn goto_prev_marker(m: &mut Model) {
889
887
} ;
890
888
let timestamp: SecondsFloat = m. markers [ new_position as usize ] ;
891
889
m. frame_number = ( timestamp * m. VIDEO_METADATA . fps ) as u32 ;
892
- m. frame_iterator . goto_timestamp ( timestamp) . unwrap ( ) ;
893
- m. frame = m. frame_iterator . take_frame ( ) ;
890
+ m. frame = m. frame_iterator . goto_timestamp ( timestamp) . unwrap ( ) ;
894
891
m. paused = true ;
895
892
}
896
893
}
@@ -917,16 +914,14 @@ fn goto_next_marker(m: &mut Model) {
917
914
} ;
918
915
let timestamp: SecondsFloat = m. markers [ new_position] ;
919
916
m. frame_number = ( timestamp * m. VIDEO_METADATA . fps ) as u32 ;
920
- m. frame_iterator . goto_timestamp ( timestamp) . unwrap ( ) ;
921
- m. frame = m. frame_iterator . take_frame ( ) ;
917
+ m. frame = m. frame_iterator . goto_timestamp ( timestamp) . unwrap ( ) ;
922
918
m. paused = true ;
923
919
}
924
920
} ;
925
921
}
926
922
927
923
fn create_marker ( m : & mut Model ) {
928
924
// create new marker at current timestamp
929
- //
930
925
match m. hovering . mode {
931
926
HoverMode :: Markers => ( ) ,
932
927
HoverMode :: Segments => {
@@ -939,7 +934,6 @@ fn create_marker(m: &mut Model) {
939
934
940
935
fn delete_marker ( m : & mut Model ) {
941
936
// delete current marker and enter segments mode
942
- //
943
937
match m. hovering . mode {
944
938
HoverMode :: Segments => ( ) ,
945
939
HoverMode :: Markers => {
@@ -953,18 +947,12 @@ fn advance_one_frame(m: &mut Model) {
953
947
match m. paused {
954
948
false => ( ) ,
955
949
true => {
956
- m. frame = m. frame_iterator . take_frame ( ) ;
950
+ m. frame = m. frame_iterator . skip_some_frames ( 1 ) ;
957
951
m. frame_number += 1 ;
958
952
}
959
953
}
960
954
}
961
955
962
-
963
-
964
-
965
-
966
-
967
-
968
956
// --- VIEW --- //
969
957
970
958
fn format_secs_to_mm_ss ( seconds : SecondsFloat ) -> String {
0 commit comments