@@ -50,7 +50,7 @@ pub mod zoom_focus_interpolation;
5050
5151pub use coord:: * ;
5252pub use decoder:: { DecodedFrame , DecoderStatus , DecoderType , PixelFormat } ;
53- pub use frame_pipeline:: { GpuOutputFormat , Nv12RenderedFrame , RenderedFrame } ;
53+ pub use frame_pipeline:: { GpuOutputFormat , Nv12RenderedFrame , RenderedFrame , SharedNv12Buffer } ;
5454pub use project_recordings:: { ProjectRecordingsMeta , SegmentRecordings , Video } ;
5555
5656use mask:: interpolate_masks;
@@ -461,10 +461,22 @@ pub async fn render_video_to_channel(
461461
462462 let click_spring = project. cursor . click_spring_config ( ) ;
463463
464- let mut zoom_focus_interpolators : Vec < ZoomFocusInterpolator > = render_segments
464+ let precomputed_cursor_timelines : Vec < Arc < PrecomputedCursorTimeline > > = render_segments
465465 . iter ( )
466466 . map ( |segment| {
467- ZoomFocusInterpolator :: new (
467+ Arc :: new ( PrecomputedCursorTimeline :: new (
468+ & segment. cursor ,
469+ cursor_smoothing,
470+ Some ( click_spring) ,
471+ ) )
472+ } )
473+ . collect ( ) ;
474+
475+ let mut zoom_focus_interpolators: Vec < ZoomFocusInterpolator > = render_segments
476+ . iter ( )
477+ . zip ( precomputed_cursor_timelines. iter ( ) )
478+ . map ( |( segment, precomputed_cursor) | {
479+ ZoomFocusInterpolator :: new_with_precomputed_cursor (
468480 & segment. cursor ,
469481 cursor_smoothing,
470482 click_spring,
@@ -475,17 +487,11 @@ pub async fn render_video_to_channel(
475487 . as_ref ( )
476488 . map ( |t| t. zoom_segments . as_slice ( ) )
477489 . unwrap_or ( & [ ] ) ,
490+ Some ( precomputed_cursor. clone ( ) ) ,
478491 )
479492 } )
480493 . collect ( ) ;
481494
482- let precomputed_cursor_timelines: Vec < PrecomputedCursorTimeline > = render_segments
483- . iter ( )
484- . map ( |segment| {
485- PrecomputedCursorTimeline :: new ( & segment. cursor , cursor_smoothing, Some ( click_spring) )
486- } )
487- . collect ( ) ;
488-
489495 let mut frame_number = 0 ;
490496
491497 let mut frame_renderer = FrameRenderer :: new ( constants) ;
@@ -703,7 +709,11 @@ pub async fn render_video_to_channel(
703709 frame_number = current_frame_number,
704710 segment_time = segment_time,
705711 consecutive_failures = consecutive_failures,
706- max_retries = DECODE_MAX_RETRIES ,
712+ max_retries = if is_initial_frame {
713+ DECODE_MAX_RETRIES_INITIAL
714+ } else {
715+ DECODE_MAX_RETRIES_STEADY
716+ } ,
707717 "Frame decode failed after retries - using previous frame"
708718 ) ;
709719 let mut fallback = last_frame. clone ( ) ;
@@ -715,7 +725,11 @@ pub async fn render_video_to_channel(
715725 tracing:: error!(
716726 frame_number = current_frame_number,
717727 segment_time = segment_time,
718- max_retries = DECODE_MAX_RETRIES ,
728+ max_retries = if is_initial_frame {
729+ DECODE_MAX_RETRIES_INITIAL
730+ } else {
731+ DECODE_MAX_RETRIES_STEADY
732+ } ,
719733 "First frame decode failed after retries - cannot continue"
720734 ) ;
721735 continue ;
@@ -775,11 +789,23 @@ pub async fn render_video_to_channel_nv12(
775789
776790 let click_spring = project. cursor . click_spring_config ( ) ;
777791
792+ let precomputed_cursor_timelines: Vec < Arc < PrecomputedCursorTimeline > > = render_segments
793+ . iter ( )
794+ . map ( |segment| {
795+ Arc :: new ( PrecomputedCursorTimeline :: new (
796+ & segment. cursor ,
797+ cursor_smoothing,
798+ Some ( click_spring) ,
799+ ) )
800+ } )
801+ . collect ( ) ;
802+
778803 let zoom_build_start = Instant :: now ( ) ;
779804 let mut zoom_focus_interpolators: Vec < ZoomFocusInterpolator > = render_segments
780805 . iter ( )
781- . map ( |segment| {
782- ZoomFocusInterpolator :: new (
806+ . zip ( precomputed_cursor_timelines. iter ( ) )
807+ . map ( |( segment, precomputed_cursor) | {
808+ ZoomFocusInterpolator :: new_with_precomputed_cursor (
783809 & segment. cursor ,
784810 cursor_smoothing,
785811 click_spring,
@@ -790,6 +816,7 @@ pub async fn render_video_to_channel_nv12(
790816 . as_ref ( )
791817 . map ( |t| t. zoom_segments . as_slice ( ) )
792818 . unwrap_or ( & [ ] ) ,
819+ Some ( precomputed_cursor. clone ( ) ) ,
793820 )
794821 } )
795822 . collect ( ) ;
@@ -798,25 +825,6 @@ pub async fn render_video_to_channel_nv12(
798825 }
799826 let zoom_focus_interpolators_construct_ms = zoom_build_start. elapsed ( ) . as_millis ( ) as u64 ;
800827
801- let cursor_smoothing_for_precompute =
802- ( !project. cursor . raw ) . then_some ( spring_mass_damper:: SpringMassDamperSimulationConfig {
803- tension : project. cursor . tension ,
804- mass : project. cursor . mass ,
805- friction : project. cursor . friction ,
806- } ) ;
807- let click_spring_for_precompute = project. cursor . click_spring_config ( ) ;
808-
809- let precomputed_cursor_timelines: Vec < PrecomputedCursorTimeline > = render_segments
810- . iter ( )
811- . map ( |segment| {
812- PrecomputedCursorTimeline :: new (
813- & segment. cursor ,
814- cursor_smoothing_for_precompute,
815- Some ( click_spring_for_precompute) ,
816- )
817- } )
818- . collect ( ) ;
819-
820828 let mut frame_number = 0 ;
821829
822830 let renderer_setup_start = Instant :: now ( ) ;
@@ -1149,7 +1157,11 @@ pub async fn render_video_to_channel_nv12(
11491157 frame_number = current_frame_number,
11501158 segment_time = segment_time,
11511159 consecutive_failures = consecutive_failures,
1152- max_retries = DECODE_MAX_RETRIES ,
1160+ max_retries = if is_initial_frame {
1161+ DECODE_MAX_RETRIES_INITIAL
1162+ } else {
1163+ DECODE_MAX_RETRIES_STEADY
1164+ } ,
11531165 "Frame decode failed after retries - using previous NV12 frame"
11541166 ) ;
11551167 let mut fallback = last_frame. clone_metadata_with_data ( ) ;
@@ -1166,7 +1178,11 @@ pub async fn render_video_to_channel_nv12(
11661178 tracing:: error!(
11671179 frame_number = current_frame_number,
11681180 segment_time = segment_time,
1169- max_retries = DECODE_MAX_RETRIES ,
1181+ max_retries = if is_initial_frame {
1182+ DECODE_MAX_RETRIES_INITIAL
1183+ } else {
1184+ DECODE_MAX_RETRIES_STEADY
1185+ } ,
11701186 "First frame decode failed after retries - cannot continue"
11711187 ) ;
11721188 continue ;
@@ -1194,7 +1210,8 @@ pub async fn render_video_to_channel_nv12(
11941210 Ok ( ( ) )
11951211}
11961212
1197- const DECODE_MAX_RETRIES : u32 = 5 ;
1213+ const DECODE_MAX_RETRIES_INITIAL : u32 = 5 ;
1214+ const DECODE_MAX_RETRIES_STEADY : u32 = 2 ;
11981215
11991216async fn decode_segment_frames_with_retry (
12001217 decoders : & RecordingSegmentDecoders ,
@@ -1206,13 +1223,18 @@ async fn decode_segment_frames_with_retry(
12061223) -> Option < DecodedSegmentFrames > {
12071224 let mut result = None ;
12081225 let mut retry_count = 0u32 ;
1226+ let max_retries = if is_initial_frame {
1227+ DECODE_MAX_RETRIES_INITIAL
1228+ } else {
1229+ DECODE_MAX_RETRIES_STEADY
1230+ } ;
12091231
1210- while result. is_none ( ) && retry_count < DECODE_MAX_RETRIES {
1232+ while result. is_none ( ) && retry_count < max_retries {
12111233 if retry_count > 0 {
12121234 let delay = if is_initial_frame {
12131235 500 * ( retry_count as u64 + 1 )
12141236 } else {
1215- 50 * retry_count as u64
1237+ 10
12161238 } ;
12171239 tokio:: time:: sleep ( std:: time:: Duration :: from_millis ( delay) ) . await ;
12181240 }
@@ -1229,7 +1251,7 @@ async fn decode_segment_frames_with_retry(
12291251
12301252 if result. is_none ( ) {
12311253 retry_count += 1 ;
1232- if retry_count < DECODE_MAX_RETRIES {
1254+ if retry_count < max_retries {
12331255 tracing:: warn!(
12341256 frame_number = current_frame_number,
12351257 segment_time = segment_time,
@@ -2972,7 +2994,7 @@ impl<'a> FrameRenderer<'a> {
29722994 }
29732995
29742996 Ok ( Some ( frame_pipeline:: Nv12RenderedFrame {
2975- data : Arc :: new ( nv12_buf) ,
2997+ data : self . nv12_buffer_pool . wrap ( nv12_buf) ,
29762998 width,
29772999 height,
29783000 y_stride : width,
0 commit comments