2121import threading
2222import time
2323from pathlib import Path
24+ from time import perf_counter as perf_counter
2425from typing import Dict , List , Literal , Optional , Tuple , Union
2526
2627import imageio
@@ -912,6 +913,7 @@ def get_max_frame_index() -> int:
912913 return max (1 , int (framerate_number .value * duration_number .value ) - 1 )
913914
914915 preview_camera_handle : Optional [viser .SceneNodeHandle ] = None
916+ _last_send = 0.0
915917
916918 def remove_preview_camera () -> None :
917919 nonlocal preview_camera_handle
@@ -977,18 +979,17 @@ def _(_) -> None:
977979 pose , fov , time = maybe_pose_and_fov_rad
978980 else :
979981 pose , fov = maybe_pose_and_fov_rad
980-
981- preview_camera_handle = server .scene .add_camera_frustum (
982- "/preview_camera" ,
983- fov = fov ,
984- aspect = render_res_vec2 .value [0 ] / render_res_vec2 .value [1 ],
985- scale = 0.35 ,
986- wxyz = pose .rotation ().wxyz ,
987- position = pose .translation (),
988- color = (10 , 200 , 30 ),
989- )
990- if render_tab_state .preview_render :
991- with server .atomic ():
982+ with server .atomic ():
983+ preview_camera_handle = server .scene .add_camera_frustum (
984+ "/preview_camera" ,
985+ fov = fov ,
986+ aspect = render_res_vec2 .value [0 ] / render_res_vec2 .value [1 ],
987+ scale = 0.35 ,
988+ wxyz = pose .rotation ().wxyz ,
989+ position = pose .translation (),
990+ color = (10 , 200 , 30 ),
991+ )
992+ if render_tab_state .preview_render :
992993 for client in server .get_clients ().values ():
993994 # aspect ratio is not assignable, pass args in get_render instead
994995 client .camera .wxyz = pose .rotation ().wxyz
@@ -1114,17 +1115,21 @@ def play() -> None:
11141115 max_frame = int (framerate_number .value * duration_number .value )
11151116 if max_frame > 0 :
11161117 assert preview_frame_slider is not None
1118+ nonlocal _last_send
1119+ now = perf_counter ()
1120+ if now - _last_send < 1.0 / framerate_number .value :
1121+ continue
1122+ _last_send = now
11171123 preview_frame_slider .value = (
11181124 preview_frame_slider .value + 1
11191125 ) % max_frame
1120- time .sleep (1.0 / framerate_number .value )
11211126
11221127 play_thread = threading .Thread (target = play )
11231128 play_thread .start ()
11241129 play_thread .join ()
11251130 dump_video_button .disabled = not preview_save_camera_path_button .visible
11261131
1127- # Play the camera trajectory when the play button is pressed.
1132+ # Pause the camera trajectory when the pause button is pressed.
11281133 @pause_button .on_click
11291134 def _ (_ ) -> None :
11301135 play_button .visible = True
@@ -1366,7 +1371,13 @@ def dump() -> None:
13661371 max_frame = int (framerate_number .value * duration_number .value )
13671372 assert max_frame > 0 and preview_frame_slider is not None
13681373 preview_frame_slider .value = 0
1369- for _ in range (max_frame ):
1374+ render_count = 0
1375+ while True :
1376+ nonlocal _last_send
1377+ now = perf_counter ()
1378+ if now - _last_send < 1.0 / framerate_number .value :
1379+ continue
1380+ _last_send = now
13701381 preview_frame_slider .value = (
13711382 preview_frame_slider .value + 1
13721383 ) % max_frame
@@ -1376,6 +1387,9 @@ def dump() -> None:
13761387 width = render_res_vec2 .value [0 ],
13771388 )
13781389 writer .append_data (image )
1390+ render_count += 1
1391+ if render_count >= max_frame :
1392+ break
13791393 writer .close ()
13801394 print (f"Video saved to { video_outfile } " )
13811395
0 commit comments