@@ -26,8 +26,10 @@ const ANNEX_B_START_CODE: &[u8] = &[0, 0, 0, 1];
2626const DEFAULT_STUN_URL : & str = "stun:stun.l.google.com:19302" ;
2727const WEBRTC_CONTROL_CHANNEL_LABEL : & str = "simdeck-control" ;
2828const WEBRTC_BOOTSTRAP_KEYFRAME_INTERVAL : Duration = Duration :: from_millis ( 250 ) ;
29- const WEBRTC_BOOTSTRAP_KEYFRAME_REPEATS : u8 = 12 ;
30- const WEBRTC_REFRESH_INTERVAL : Duration = Duration :: from_millis ( 66 ) ;
29+ const WEBRTC_BOOTSTRAP_KEYFRAME_REPEATS : u8 = 4 ;
30+ const WEBRTC_MIN_REFRESH_INTERVAL : Duration = Duration :: from_millis ( 16 ) ;
31+ const WEBRTC_MAX_REFRESH_INTERVAL : Duration = Duration :: from_millis ( 100 ) ;
32+ const WEBRTC_WRITE_TIMEOUT : Duration = Duration :: from_millis ( 120 ) ;
3133
3234#[ derive( Debug , Deserialize ) ]
3335#[ serde( rename_all = "camelCase" ) ]
@@ -61,7 +63,7 @@ pub async fn create_answer(
6163 state. registry . remove ( & udid) ;
6264 return Err ( error) ;
6365 }
64- session. request_refresh ( ) ;
66+ session. request_keyframe ( ) ;
6567 info ! (
6668 "WebRTC offer for {udid}: remote_candidates={} remote_candidate_types={} ice_servers={}" ,
6769 count_sdp_candidates( & payload. sdp) ,
@@ -350,15 +352,19 @@ async fn stream_h264_frames(
350352 let mut last_sequence = 0u64 ;
351353 let mut send_timing = WebRtcSendTiming :: new ( ) ;
352354 let mut bootstrap_interval = time:: interval ( WEBRTC_BOOTSTRAP_KEYFRAME_INTERVAL ) ;
353- let mut refresh_interval = time:: interval ( WEBRTC_REFRESH_INTERVAL ) ;
354- refresh_interval. set_missed_tick_behavior ( time:: MissedTickBehavior :: Skip ) ;
355+ let mut refresh_sleep = Box :: pin ( time:: sleep ( WEBRTC_MIN_REFRESH_INTERVAL ) ) ;
355356 let mut bootstrap_frames_remaining = WEBRTC_BOOTSTRAP_KEYFRAME_REPEATS ;
357+ let mut adaptive_refresh_interval = WEBRTC_MIN_REFRESH_INTERVAL ;
358+ let mut waiting_for_keyframe = false ;
356359 let _guard = WebRtcMetricsGuard :: new ( state. metrics . clone ( ) ) ;
357360
358361 loop {
359362 tokio:: select! {
360- _ = refresh_interval . tick ( ) => {
363+ _ = & mut refresh_sleep => {
361364 session. request_refresh( ) ;
365+ refresh_sleep
366+ . as_mut( )
367+ . reset( time:: Instant :: now( ) + adaptive_refresh_interval) ;
362368 }
363369 _ = bootstrap_interval. tick( ) , if bootstrap_frames_remaining > 0 => {
364370 if let Err ( error) = write_frame_sample(
@@ -380,7 +386,8 @@ async fn stream_h264_frames(
380386 . metrics
381387 . frames_dropped_server
382388 . fetch_add( skipped, Ordering :: Relaxed ) ;
383- session. request_refresh( ) ;
389+ waiting_for_keyframe = true ;
390+ session. request_keyframe( ) ;
384391 continue ;
385392 }
386393 Err ( broadcast:: error:: RecvError :: Closed ) => break ,
@@ -392,7 +399,8 @@ async fn stream_h264_frames(
392399 . frames_dropped_server
393400 . fetch_add( skipped, Ordering :: Relaxed ) ;
394401 if !frame. is_keyframe {
395- session. request_refresh( ) ;
402+ waiting_for_keyframe = true ;
403+ session. request_keyframe( ) ;
396404 continue ;
397405 }
398406 }
@@ -401,14 +409,32 @@ async fn stream_h264_frames(
401409 . metrics
402410 . frames_dropped_server
403411 . fetch_add( frame. frame_sequence - last_sequence - 1 , Ordering :: Relaxed ) ;
404- session. request_refresh( ) ;
412+ waiting_for_keyframe = true ;
413+ session. request_keyframe( ) ;
414+ continue ;
415+ }
416+ if waiting_for_keyframe && !frame. is_keyframe {
417+ state. metrics. frames_dropped_server. fetch_add( 1 , Ordering :: Relaxed ) ;
405418 continue ;
406419 }
407420 if frame. is_keyframe {
408421 latest_keyframe = frame. clone( ) ;
422+ waiting_for_keyframe = false ;
409423 }
410424 let duration = send_timing. duration_for( & frame) ;
411- if let Err ( error) = write_frame_sample( & video_track, & frame, duration) . await {
425+ let started_at = time:: Instant :: now( ) ;
426+ let write_result = time:: timeout(
427+ WEBRTC_WRITE_TIMEOUT ,
428+ write_frame_sample( & video_track, & frame, duration) ,
429+ ) . await ;
430+ adaptive_refresh_interval = adaptive_interval_for_write( started_at. elapsed( ) ) ;
431+ if let Err ( error) = write_result
432+ . map_err( |_| anyhow:: anyhow!(
433+ "timed out writing WebRTC frame after {}ms" ,
434+ WEBRTC_WRITE_TIMEOUT . as_millis( )
435+ ) )
436+ . and_then( |result| result)
437+ {
412438 warn!( "WebRTC frame write failed for {udid}: {error}" ) ;
413439 break ;
414440 }
@@ -421,6 +447,14 @@ async fn stream_h264_frames(
421447 let _ = peer_connection. close ( ) . await ;
422448}
423449
450+ fn adaptive_interval_for_write ( write_elapsed : Duration ) -> Duration {
451+ let target_ms = ( write_elapsed. as_millis ( ) as u64 ) . saturating_mul ( 2 ) . clamp (
452+ WEBRTC_MIN_REFRESH_INTERVAL . as_millis ( ) as u64 ,
453+ WEBRTC_MAX_REFRESH_INTERVAL . as_millis ( ) as u64 ,
454+ ) ;
455+ Duration :: from_millis ( target_ms)
456+ }
457+
424458async fn write_frame_sample (
425459 video_track : & TrackLocalStaticSample ,
426460 frame : & crate :: transport:: packet:: SharedFrame ,
0 commit comments