Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions crates/editor/src/editor_instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1118,12 +1118,23 @@ impl EditorInstance {
_ = preview_rx.changed() => {
continue;
}
frames = outgoing_media.decoders.get_frames_initial(
outgoing.source_time as f32,
project.requires_camera(),
true,
outgoing_offsets,
) => frames,
frames = async {
let decoders = if outgoing.segment.recording_clip
== segment.recording_clip
{
outgoing_media.decoders.transition_side().await
} else {
&outgoing_media.decoders
};
decoders
.get_frames_initial(
outgoing.source_time as f32,
project.requires_camera(),
true,
outgoing_offsets,
)
.await
} => frames,
};
if let Some(outgoing_frames) = outgoing_frames {
let outgoing_zoom = outgoing_zoom_timelines
Expand Down
11 changes: 7 additions & 4 deletions crates/editor/src/playback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -478,9 +478,13 @@ async fn decode_prefetched_frame(request: PrefetchDecodeRequest) -> PrefetchDeco
};
let transition = async {
let transition = transition?;
let transition_decoders = if transition.segment_index == segment_index {
transition.decoders.transition_side().await
} else {
&transition.decoders
};
let segment_frames = if is_initial {
transition
.decoders
transition_decoders
.get_frames_initial(
transition.segment_time as f32,
!hide_camera,
Expand All @@ -489,8 +493,7 @@ async fn decode_prefetched_frame(request: PrefetchDecodeRequest) -> PrefetchDeco
)
.await
} else {
transition
.decoders
transition_decoders
.get_frames(
transition.segment_time as f32,
!hide_camera,
Expand Down
8 changes: 6 additions & 2 deletions crates/export/src/preview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,12 @@ async fn render_preview_frame(
.find(|clip| clip.index == outgoing.segment.recording_clip)
.map(|clip| clip.offsets)
.unwrap_or_default();
let outgoing_frames = outgoing_media
.decoders
let outgoing_decoders = if outgoing.segment.recording_clip == segment.recording_clip {
outgoing_media.decoders.transition_side().await
} else {
outgoing_media.decoders
};
let outgoing_frames = outgoing_decoders
.get_frames(
outgoing.source_time as f32,
project_config.requires_camera() && !settings.cursor_only,
Expand Down
78 changes: 60 additions & 18 deletions crates/rendering/src/decoder/ffmpeg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,9 @@ impl FfmpegDecoder {
// Previous vended frame number since the last reset, for pts-hole
// discovery (the reader vends in pts order between resets).
let mut prev_vended: Option<u32> = None;
// Newest frame the reader has vended since its last reset: where the
// reader is. The last sent frame is not, as it can be a cache hit.
let mut reader_at: Option<u32> = None;

let last_sent_frame = Rc::new(RefCell::new(None::<OutputFrame>));
let first_ever_frame = Rc::new(RefCell::new(None::<OutputFrame>));
Expand Down Expand Up @@ -636,6 +639,9 @@ impl FfmpegDecoder {
let mut sw_gap_hold: Option<OutputFrame> = None;
// Previous vended frame number since the last reset.
let mut sw_prev_vended: Option<u32> = None;
// Newest frame the reader has vended since its last reset: where the
// reader is. The last sent frame is not, as it can be a cache hit.
let mut sw_reader_at: Option<u32> = None;
let sw_last_sent_frame = Rc::new(RefCell::new(None::<OutputFrame>));
let sw_first_ever_frame = Rc::new(RefCell::new(None::<OutputFrame>));
let mut sw_frames = sw_this.frames();
Expand Down Expand Up @@ -669,6 +675,7 @@ impl FfmpegDecoder {
// The pre-decoded frame is a real vend: without seeding
// this, an opening static hold would never be recorded.
sw_prev_vended = Some(current_frame);
sw_reader_at = Some(current_frame);
}

let sw_decoder_type = DecoderType::FFmpegSoftware;
Expand Down Expand Up @@ -748,6 +755,16 @@ impl FfmpegDecoder {
continue;
}

// Nothing precedes the stream's first frame, so it holds
// for any earlier request; a later one needs a seek.
if let Some(first_frame) = sw_first_ever_frame.borrow().clone()
&& requested_frame <= first_frame.number
{
*sw_last_sent_frame.borrow_mut() = Some(first_frame.clone());
let _ = reply.send(first_frame.frame);
continue;
}

if is_backward_seek {
let best_cached_frame = sw_cache
.range(..=requested_frame)
Expand All @@ -766,14 +783,6 @@ impl FfmpegDecoder {
continue;
}

if requested_frame <= MAX_FRAME_LOOKBACK_TOLERANCE
&& let Some(first_frame) = sw_first_ever_frame.borrow().clone()
{
*sw_last_sent_frame.borrow_mut() = Some(first_frame.clone());
let _ = reply.send(first_frame.frame);
continue;
}

if let Err(error) = sw_this.reset(requested_time)
&& let Some(control) = &managed
{
Expand All @@ -787,6 +796,7 @@ impl FfmpegDecoder {
*sw_last_sent_frame.borrow_mut() = None;
sw_cache.clear();
sw_prev_vended = None;
sw_reader_at = None;
}

// Requests inside a KNOWN pts hole are answered with the
Expand Down Expand Up @@ -836,6 +846,11 @@ impl FfmpegDecoder {
let cache_min = requested_frame.saturating_sub(FRAME_CACHE_SIZE as u32 / 2);
let cache_max = requested_frame + FRAME_CACHE_SIZE as u32 / 2;

// The reader has already passed this uncached frame: it fell
// outside the cache window, or the last sent frame was a cache
// hit away from the reader. Reading on cannot reach it.
let behind_reader = sw_reader_at.is_some_and(|at| requested_frame <= at);

let is_far_forward = sw_last_sent_frame.borrow().as_ref().map_or_else(
|| {
if sw_first_ever_frame.borrow().is_some() {
Expand All @@ -855,7 +870,7 @@ impl FfmpegDecoder {
},
);

if is_far_forward {
if is_far_forward || behind_reader {
if let Err(error) = sw_this.reset(requested_time)
&& let Some(control) = &managed
{
Expand All @@ -869,6 +884,7 @@ impl FfmpegDecoder {
*sw_last_sent_frame.borrow_mut() = None;
sw_cache.clear();
sw_prev_vended = None;
sw_reader_at = None;
}

let mut exit = false;
Expand All @@ -895,6 +911,13 @@ impl FfmpegDecoder {
// the vend continuity that hole discovery
// relies on.
sw_prev_vended = None;
// The reader has still moved past it.
if let Ok(frame) = &frame
&& let Some(pts) = frame.pts()
{
sw_reader_at =
Some(pts_to_frame(pts - sw_start_time, sw_time_base, fps));
}
break;
}

Expand All @@ -917,6 +940,7 @@ impl FfmpegDecoder {
record_pts_hole(&mut sw_pts_holes, prev, current_frame);
}
sw_prev_vended = Some(current_frame);
sw_reader_at = Some(current_frame);

let mut cache_frame = CachedFrame::Raw {
frame,
Expand Down Expand Up @@ -1146,6 +1170,7 @@ impl FfmpegDecoder {
// The pre-decoded frame is a real vend: without seeding this,
// an opening static hold would never be recorded.
prev_vended = Some(current_frame);
reader_at = Some(current_frame);
info!(
"FFmpeg decoder '{}': pre-decoded first frame {} ({}x{})",
name, current_frame, video_width, video_height
Expand Down Expand Up @@ -1210,6 +1235,7 @@ impl FfmpegDecoder {
record_pts_hole(&mut pts_holes, prev, current_frame);
}
prev_vended = Some(current_frame);
reader_at = Some(current_frame);
let mut cache_frame = CachedFrame::Raw {
frame,
number: current_frame,
Expand Down Expand Up @@ -1312,6 +1338,16 @@ impl FfmpegDecoder {
continue;
}

// Nothing precedes the stream's first frame, so it holds
// for any earlier request; a later one needs a seek.
if let Some(first_frame) = first_ever_frame.borrow().clone()
&& requested_frame <= first_frame.number
{
*last_sent_frame.borrow_mut() = Some(first_frame.clone());
let _ = reply.send(first_frame.frame);
continue;
}

if is_backward_seek {
let best_cached_frame = cache
.range(..=requested_frame)
Expand All @@ -1330,14 +1366,6 @@ impl FfmpegDecoder {
continue;
}

if requested_frame <= MAX_FRAME_LOOKBACK_TOLERANCE
&& let Some(first_frame) = first_ever_frame.borrow().clone()
{
*last_sent_frame.borrow_mut() = Some(first_frame.clone());
let _ = reply.send(first_frame.frame);
continue;
}

if let Err(error) = this.reset(requested_time)
&& let Some(control) = &managed
{
Expand All @@ -1352,6 +1380,7 @@ impl FfmpegDecoder {
*last_sent_frame.borrow_mut() = None;
cache.clear();
prev_vended = None;
reader_at = None;
}

// Requests inside a KNOWN pts hole are answered with the
Expand Down Expand Up @@ -1406,6 +1435,11 @@ impl FfmpegDecoder {
let cache_min = requested_frame.saturating_sub(FRAME_CACHE_SIZE as u32 / 2);
let cache_max = requested_frame + FRAME_CACHE_SIZE as u32 / 2;

// The reader has already passed this uncached frame: it fell
// outside the cache window, or the last sent frame was a cache
// hit away from the reader. Reading on cannot reach it.
let behind_reader = reader_at.is_some_and(|at| requested_frame <= at);

let is_far_forward = last_sent_frame.borrow().as_ref().map_or_else(
|| {
if first_ever_frame.borrow().is_some() {
Expand All @@ -1423,7 +1457,7 @@ impl FfmpegDecoder {
|last| requested_frame.saturating_sub(last.number) > FRAME_CACHE_SIZE as u32,
);

if is_far_forward {
if is_far_forward || behind_reader {
if let Err(error) = this.reset(requested_time)
&& let Some(control) = &managed
{
Expand All @@ -1438,6 +1472,7 @@ impl FfmpegDecoder {
*last_sent_frame.borrow_mut() = None;
cache.clear();
prev_vended = None;
reader_at = None;
}

let mut exit = false;
Expand All @@ -1463,6 +1498,12 @@ impl FfmpegDecoder {
// The frame just pulled is discarded: it breaks the
// vend continuity that hole discovery relies on.
prev_vended = None;
// The reader has still moved past it.
if let Ok(frame) = &frame
&& let Some(pts) = frame.pts()
{
reader_at = Some(pts_to_frame(pts - start_time, time_base, fps));
}
break;
}

Expand All @@ -1485,6 +1526,7 @@ impl FfmpegDecoder {
record_pts_hole(&mut pts_holes, prev, current_frame);
}
prev_vended = Some(current_frame);
reader_at = Some(current_frame);

let mut cache_frame = CachedFrame::Raw {
frame,
Expand Down
1 change: 1 addition & 0 deletions crates/rendering/src/decoder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2806,6 +2806,7 @@ mod managed_worker_tests {
screen: display.with_max_fallback_distance(crate::SCREEN_MAX_FALLBACK_DISTANCE),
camera: Some(camera.with_max_fallback_distance(crate::CAMERA_MAX_FALLBACK_DISTANCE)),
segment_offset: 10.25,
transition_readers: None,
};
let input = ManagedSegmentVideoInput::new(
0,
Expand Down
Loading
Loading