Skip to content

Commit 23f8516

Browse files
Checkpoint before follow-up message
Co-authored-by: richiemcilroy1 <richiemcilroy1@gmail.com>
1 parent 9bb14bb commit 23f8516

File tree

2 files changed

+101
-24
lines changed

2 files changed

+101
-24
lines changed

crates/editor/src/playback.rs

Lines changed: 100 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ use cap_audio::{
44
use cap_media::MediaError;
55
use cap_media_info::AudioInfo;
66
use cap_project::{ProjectConfiguration, XY};
7-
use cap_rendering::{ProjectUniforms, RenderVideoConstants};
7+
use cap_rendering::{DecodedSegmentFrames, ProjectUniforms, RenderVideoConstants};
88
use 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};
1313
use tokio::{sync::watch, time::Instant};
14-
use tracing::{error, info, warn};
14+
use tracing::{error, info, trace, warn};
1515

1616
use 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)]
2427
pub 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+
4958
impl 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

crates/rendering/src/decoder/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub fn pts_to_frame(pts: i64, time_base: Rational, fps: u32) -> u32 {
4747
.round() as u32
4848
}
4949

50-
pub const FRAME_CACHE_SIZE: usize = 300;
50+
pub const FRAME_CACHE_SIZE: usize = 500;
5151

5252
#[derive(Clone)]
5353
pub struct AsyncVideoDecoderHandle {

0 commit comments

Comments
 (0)