@@ -4,14 +4,14 @@ use cap_audio::{
44use cap_media:: MediaError ;
55use cap_media_info:: AudioInfo ;
66use cap_project:: { ProjectConfiguration , XY } ;
7- use cap_rendering:: { ProjectUniforms , RenderVideoConstants } ;
7+ use cap_rendering:: { DecodedSegmentFrames , ProjectUniforms , RenderVideoConstants } ;
88use cpal:: {
99 BufferSize , SampleFormat , SupportedBufferSize ,
1010 traits:: { DeviceTrait , HostTrait , StreamTrait } ,
1111} ;
12- use std:: { sync:: Arc , time:: Duration } ;
12+ use std:: { collections :: VecDeque , sync:: Arc , time:: Duration } ;
1313use tokio:: { sync:: watch, time:: Instant } ;
14- use tracing:: { error, info, warn} ;
14+ use tracing:: { error, info, trace , warn} ;
1515
1616use crate :: {
1717 audio:: { AudioPlaybackBuffer , AudioSegment } ,
@@ -20,6 +20,9 @@ use crate::{
2020 segments:: get_audio_segments,
2121} ;
2222
23+ const PREFETCH_BUFFER_SIZE : usize = 12 ;
24+ const PREFETCH_AHEAD_FRAMES : u32 = 16 ;
25+
2326#[ derive( Debug ) ]
2427pub enum PlaybackStartError {
2528 InvalidFps ,
@@ -46,6 +49,12 @@ pub struct PlaybackHandle {
4649 event_rx : watch:: Receiver < PlaybackEvent > ,
4750}
4851
52+ struct PrefetchedFrame {
53+ frame_number : u32 ,
54+ segment_frames : DecodedSegmentFrames ,
55+ segment_index : u32 ,
56+ }
57+
4958impl Playback {
5059 pub async fn start (
5160 self ,
@@ -90,8 +99,44 @@ impl Playback {
9099
91100 let frame_duration = Duration :: from_secs_f64 ( 1.0 / fps_f64) ;
92101 let mut frame_number = self . start_frame_number ;
102+ let mut prefetch_buffer: VecDeque < PrefetchedFrame > = VecDeque :: with_capacity ( PREFETCH_BUFFER_SIZE ) ;
103+ let mut prefetch_frame = self . start_frame_number ;
104+ let mut last_rendered_frame: Option < u32 > = None ;
105+ let max_frame_skip = 2u32 ;
93106
94107 ' playback: loop {
108+ while prefetch_buffer. len ( ) < PREFETCH_BUFFER_SIZE && !* stop_rx. borrow ( ) {
109+ let prefetch_time = prefetch_frame as f64 / fps_f64;
110+ if prefetch_time >= duration {
111+ break ;
112+ }
113+
114+ let project = self . project . borrow ( ) . clone ( ) ;
115+ if let Some ( ( segment_time, segment) ) = project. get_segment_time ( prefetch_time) {
116+ if let Some ( segment_media) = self . segment_medias . get ( segment. recording_clip as usize ) {
117+ let clip_offsets = project
118+ . clips
119+ . iter ( )
120+ . find ( |v| v. index == segment. recording_clip )
121+ . map ( |v| v. offsets )
122+ . unwrap_or_default ( ) ;
123+
124+ if let Some ( segment_frames) = segment_media
125+ . decoders
126+ . get_frames ( segment_time as f32 , !project. camera . hide , clip_offsets)
127+ . await
128+ {
129+ prefetch_buffer. push_back ( PrefetchedFrame {
130+ frame_number : prefetch_frame,
131+ segment_frames,
132+ segment_index : segment. recording_clip ,
133+ } ) ;
134+ }
135+ }
136+ }
137+ prefetch_frame = prefetch_frame. saturating_add ( 1 ) ;
138+ }
139+
95140 let frame_offset = frame_number. saturating_sub ( self . start_frame_number ) as f64 ;
96141 let next_deadline = start + frame_duration. mul_f64 ( frame_offset) ;
97142
@@ -111,30 +156,44 @@ impl Playback {
111156
112157 let project = self . project . borrow ( ) . clone ( ) ;
113158
114- let Some ( ( segment_time, segment) ) = project. get_segment_time ( playback_time) else {
115- break ;
116- } ;
159+ let prefetched = prefetch_buffer. iter ( ) . position ( |p| p. frame_number == frame_number) ;
160+
161+ let segment_frames_opt = if let Some ( idx) = prefetched {
162+ let prefetched = prefetch_buffer. remove ( idx) ;
163+ Some ( ( prefetched. segment_frames , prefetched. segment_index ) )
164+ } else {
165+ let Some ( ( segment_time, segment) ) = project. get_segment_time ( playback_time) else {
166+ break ;
167+ } ;
117168
118- let Some ( segment_media) = self . segment_medias . get ( segment. recording_clip as usize )
119- else {
120- continue ;
121- } ;
169+ let Some ( segment_media) = self . segment_medias . get ( segment. recording_clip as usize ) else {
170+ frame_number = frame_number . saturating_add ( 1 ) ;
171+ continue ;
172+ } ;
122173
123- let clip_offsets = project
124- . clips
125- . iter ( )
126- . find ( |v| v. index == segment. recording_clip )
127- . map ( |v| v. offsets )
128- . unwrap_or_default ( ) ;
174+ let clip_offsets = project
175+ . clips
176+ . iter ( )
177+ . find ( |v| v. index == segment. recording_clip )
178+ . map ( |v| v. offsets )
179+ . unwrap_or_default ( ) ;
180+
181+ let data = tokio:: select! {
182+ _ = stop_rx. changed( ) => break ' playback,
183+ data = segment_media
184+ . decoders
185+ . get_frames( segment_time as f32 , !project. camera. hide, clip_offsets) => data,
186+ } ;
129187
130- let data = tokio:: select! {
131- _ = stop_rx. changed( ) => break ' playback,
132- data = segment_media
133- . decoders
134- . get_frames( segment_time as f32 , !project. camera. hide, clip_offsets) => data,
188+ data. map ( |frames| ( frames, segment. recording_clip ) )
135189 } ;
136190
137- if let Some ( segment_frames) = data {
191+ if let Some ( ( segment_frames, segment_index) ) = segment_frames_opt {
192+ let Some ( segment_media) = self . segment_medias . get ( segment_index as usize ) else {
193+ frame_number = frame_number. saturating_add ( 1 ) ;
194+ continue ;
195+ } ;
196+
138197 let uniforms = ProjectUniforms :: new (
139198 & self . render_constants ,
140199 & project,
@@ -148,6 +207,8 @@ impl Playback {
148207 self . renderer
149208 . render_frame ( segment_frames, uniforms, segment_media. cursor . clone ( ) )
150209 . await ;
210+
211+ last_rendered_frame = Some ( frame_number) ;
151212 }
152213
153214 event_tx. send ( PlaybackEvent :: Frame ( frame_number) ) . ok ( ) ;
@@ -158,7 +219,23 @@ impl Playback {
158219 + ( start. elapsed ( ) . as_secs_f64 ( ) * fps_f64) . floor ( ) as u32 ;
159220
160221 if frame_number < expected_frame {
161- frame_number = expected_frame;
222+ let frames_behind = expected_frame - frame_number;
223+ if frames_behind <= max_frame_skip {
224+ frame_number = expected_frame;
225+ trace ! ( "Skipping {} frames to catch up" , frames_behind) ;
226+ } else {
227+ frame_number = frame_number + max_frame_skip;
228+ trace ! ( "Limiting frame skip to {} (was {} behind)" , max_frame_skip, frames_behind) ;
229+ }
230+
231+ while let Some ( front) = prefetch_buffer. front ( ) {
232+ if front. frame_number < frame_number {
233+ prefetch_buffer. pop_front ( ) ;
234+ } else {
235+ break ;
236+ }
237+ }
238+ prefetch_frame = prefetch_frame. max ( frame_number) ;
162239 }
163240 }
164241
0 commit comments