@@ -20,6 +20,11 @@ use crate::{
2020 segments:: get_audio_segments,
2121} ;
2222
23+ #[ derive( Debug ) ]
24+ pub enum PlaybackStartError {
25+ InvalidFps ,
26+ }
27+
2328pub struct Playback {
2429 pub renderer : Arc < editor:: RendererHandle > ,
2530 pub render_constants : Arc < RenderVideoConstants > ,
@@ -42,7 +47,18 @@ pub struct PlaybackHandle {
4247}
4348
4449impl Playback {
45- pub async fn start ( self , fps : u32 , resolution_base : XY < u32 > ) -> PlaybackHandle {
50+ pub async fn start (
51+ self ,
52+ fps : u32 ,
53+ resolution_base : XY < u32 > ,
54+ ) -> Result < PlaybackHandle , PlaybackStartError > {
55+ let fps_f64 = fps as f64 ;
56+
57+ if !( fps_f64. is_finite ( ) && fps_f64 > 0.0 ) {
58+ warn ! ( fps, "Invalid FPS provided for playback start" ) ;
59+ return Err ( PlaybackStartError :: InvalidFps ) ;
60+ }
61+
4662 let ( stop_tx, mut stop_rx) = watch:: channel ( false ) ;
4763 stop_rx. borrow_and_update ( ) ;
4864
@@ -72,60 +88,83 @@ impl Playback {
7288 }
7389 . spawn ( ) ;
7490
75- loop {
76- let time =
77- ( self . start_frame_number as f64 / fps as f64 ) + start. elapsed ( ) . as_secs_f64 ( ) ;
78- let frame_number = ( time * fps as f64 ) . floor ( ) as u32 ;
91+ let frame_duration = Duration :: from_secs_f64 ( 1.0 / fps_f64) ;
92+ let mut frame_number = self . start_frame_number ;
93+
94+ ' playback: loop {
95+ let frame_offset = frame_number. saturating_sub ( self . start_frame_number ) as f64 ;
96+ let next_deadline = start + frame_duration. mul_f64 ( frame_offset) ;
7997
80- if frame_number as f64 >= fps as f64 * duration {
98+ tokio:: select! {
99+ _ = stop_rx. changed( ) => break ' playback,
100+ _ = tokio:: time:: sleep_until( next_deadline) => { }
101+ }
102+
103+ if * stop_rx. borrow ( ) {
81104 break ;
82- } ;
105+ }
106+
107+ let playback_time = frame_number as f64 / fps_f64;
108+ if playback_time >= duration {
109+ break ;
110+ }
83111
84112 let project = self . project . borrow ( ) . clone ( ) ;
85113
86- if let Some ( ( segment_time, segment_i) ) = project. get_segment_time ( time ) {
87- let segment = & self . segments [ segment_i as usize ] ;
88- let clip_config = project . clips . iter ( ) . find ( |v| v . index == segment_i ) ;
89- let clip_offsets = clip_config . map ( |v| v . offsets ) . unwrap_or_default ( ) ;
114+ let Some ( ( segment_time, segment_i) ) = project. get_segment_time ( playback_time )
115+ else {
116+ break ;
117+ } ;
90118
91- let data = tokio:: select! {
92- _ = stop_rx. changed( ) => { break ; } ,
93- data = segment. decoders. get_frames( segment_time as f32 , !project. camera. hide, clip_offsets) => { data }
94- } ;
119+ let segment = & self . segments [ segment_i as usize ] ;
120+ let clip_offsets = project
121+ . clips
122+ . iter ( )
123+ . find ( |v| v. index == segment_i)
124+ . map ( |v| v. offsets )
125+ . unwrap_or_default ( ) ;
126+
127+ let data = tokio:: select! {
128+ _ = stop_rx. changed( ) => break ' playback,
129+ data = segment
130+ . decoders
131+ . get_frames( segment_time as f32 , !project. camera. hide, clip_offsets) => data,
132+ } ;
95133
96- if let Some ( segment_frames) = data {
97- let uniforms = ProjectUniforms :: new (
98- & self . render_constants ,
99- & project,
100- frame_number,
101- fps,
102- resolution_base,
103- & segment. cursor ,
104- & segment_frames,
105- ) ;
106-
107- self . renderer
108- . render_frame ( segment_frames, uniforms, segment. cursor . clone ( ) )
109- . await ;
110- }
111- }
134+ if let Some ( segment_frames) = data {
135+ let uniforms = ProjectUniforms :: new (
136+ & self . render_constants ,
137+ & project,
138+ frame_number,
139+ fps,
140+ resolution_base,
141+ & segment. cursor ,
142+ & segment_frames,
143+ ) ;
112144
113- tokio:: time:: sleep_until (
114- start
115- + ( frame_number - self . start_frame_number )
116- * Duration :: from_secs_f32 ( 1.0 / fps as f32 ) ,
117- )
118- . await ;
145+ self . renderer
146+ . render_frame ( segment_frames, uniforms, segment. cursor . clone ( ) )
147+ . await ;
148+ }
119149
120150 event_tx. send ( PlaybackEvent :: Frame ( frame_number) ) . ok ( ) ;
151+
152+ frame_number = frame_number. saturating_add ( 1 ) ;
153+
154+ let expected_frame = self . start_frame_number
155+ + ( start. elapsed ( ) . as_secs_f64 ( ) * fps_f64) . floor ( ) as u32 ;
156+
157+ if frame_number < expected_frame {
158+ frame_number = expected_frame;
159+ }
121160 }
122161
123162 stop_tx. send ( true ) . ok ( ) ;
124163
125164 event_tx. send ( PlaybackEvent :: Stop ) . ok ( ) ;
126165 } ) ;
127166
128- handle
167+ Ok ( handle)
129168 }
130169}
131170
0 commit comments