@@ -34,7 +34,8 @@ use cap_project::{
3434} ;
3535use cap_recording:: {
3636 feeds:: {
37- self , CameraFeed , DeviceOrModelID , RawCameraFrame ,
37+ self ,
38+ camera:: { CameraFeed , DeviceOrModelID , RawCameraFrame } ,
3839 microphone:: { self , MicrophoneFeed } ,
3940 } ,
4041 sources:: ScreenCaptureTarget ,
@@ -67,7 +68,6 @@ use std::{
6768 process:: Command ,
6869 str:: FromStr ,
6970 sync:: Arc ,
70- time:: Duration ,
7171} ;
7272use tauri:: { AppHandle , Manager , State , Window , WindowEvent } ;
7373use tauri_plugin_deep_link:: DeepLinkExt ;
@@ -77,10 +77,7 @@ use tauri_plugin_notification::{NotificationExt, PermissionState};
7777use tauri_plugin_opener:: OpenerExt ;
7878use tauri_plugin_shell:: ShellExt ;
7979use tauri_specta:: Event ;
80- use tokio:: {
81- sync:: { Mutex , RwLock , mpsc} ,
82- time:: timeout,
83- } ;
80+ use tokio:: sync:: RwLock ;
8481use tracing:: { error, trace} ;
8582use upload:: { S3UploadMeta , create_or_get_video, upload_image, upload_video} ;
8683use web_api:: ManagerExt as WebManagerExt ;
@@ -105,19 +102,17 @@ pub struct App {
105102 #[ deprecated = "can be removed when native camera preview is ready" ]
106103 camera_ws_port : u16 ,
107104 #[ serde( skip) ]
108- camera_feed : Option < Arc < Mutex < CameraFeed > > > ,
109- #[ serde( skip) ]
110105 camera_preview : CameraPreviewManager ,
111106 #[ serde( skip) ]
112- camera_feed_initialization : Option < mpsc:: Sender < ( ) > > ,
113- #[ serde( skip) ]
114107 handle : AppHandle ,
115108 #[ serde( skip) ]
116109 recording_state : RecordingState ,
117110 #[ serde( skip) ]
118111 recording_logging_handle : LoggingHandle ,
119112 #[ serde( skip) ]
120113 mic_feed : ActorRef < feeds:: microphone:: MicrophoneFeed > ,
114+ #[ serde( skip) ]
115+ camera_feed : ActorRef < feeds:: camera:: CameraFeed > ,
121116 server_url : String ,
122117}
123118
@@ -245,89 +240,29 @@ async fn set_mic_input(state: MutableState<'_, App>, label: Option<String>) -> R
245240#[ tauri:: command]
246241#[ specta:: specta]
247242async fn set_camera_input (
248- app_handle : AppHandle ,
249243 state : MutableState < ' _ , App > ,
250244 id : Option < DeviceOrModelID > ,
251- ) -> Result < bool , String > {
252- let mut app = state. write ( ) . await ;
245+ ) -> Result < ( ) , String > {
246+ let camera_feed = state. read ( ) . await . camera_feed . clone ( ) ;
253247
254- match ( id , app . camera_feed . as_ref ( ) ) {
255- ( Some ( id ) , Some ( camera_feed ) ) => {
248+ match id {
249+ None => {
256250 camera_feed
257- . lock ( )
258- . await
259- . switch_cameras ( id)
251+ . ask ( feeds:: camera:: RemoveInput )
260252 . await
261253 . map_err ( |e| e. to_string ( ) ) ?;
262- Ok ( true )
263- }
264- ( Some ( id) , None ) => {
265- let ( shutdown_tx, mut shutdown_rx) = mpsc:: channel ( 1 ) ;
266- if let Some ( cancel) = app. camera_feed_initialization . as_ref ( ) {
267- // Ask currently running setup to abort
268- cancel. send ( ( ) ) . await . ok ( ) ;
269-
270- // We can assume a window was already initialized.
271- // Stop it so we can recreate it with the correct `camera_tx`
272- if let Some ( win) = CapWindowId :: Camera . get ( & app_handle) {
273- win. close ( ) . unwrap ( ) ; // TODO: Error handling
274- } ;
275- } else {
276- app. camera_feed_initialization = Some ( shutdown_tx) ;
277- }
278-
279- ShowCapWindow :: Camera . show ( & app_handle) . await . unwrap ( ) ;
280- if let Some ( win) = CapWindowId :: Main . get ( & app_handle) {
281- win. set_focus ( ) . ok ( ) ;
282- } ;
283-
284- let camera_tx = if GeneralSettingsStore :: get ( & app_handle)
285- . ok ( )
286- . and_then ( |v| v. map ( |v| v. enable_native_camera_preview ) )
287- . unwrap_or_default ( )
288- {
289- app. camera_preview . attach ( )
290- } else {
291- app. camera_tx . clone ( )
292- } ;
293-
294- drop ( app) ;
295-
296- let fut = CameraFeed :: init ( id) ;
297-
298- tokio:: select! {
299- result = fut => {
300- let feed = result. map_err( |err| err. to_string( ) ) ?;
301- let mut app = state. write( ) . await ;
302-
303- if let Some ( cancel) = app. camera_feed_initialization. take( ) {
304- cancel. send( ( ) ) . await . ok( ) ;
305- }
306-
307- if app. camera_feed. is_none( ) {
308- feed. attach( camera_tx) ;
309- app. camera_feed = Some ( Arc :: new( Mutex :: new( feed) ) ) ;
310- Ok ( true )
311- } else {
312- Ok ( false )
313- }
314- }
315- _ = shutdown_rx. recv( ) => {
316- Ok ( false )
317- }
318- }
319254 }
320- ( None , _) => {
321- if let Some ( cancel) = app. camera_feed_initialization . take ( ) {
322- cancel. send ( ( ) ) . await . ok ( ) ;
323- }
324- app. camera_feed . take ( ) ;
325- if let Some ( win) = CapWindowId :: Camera . get ( & app_handle) {
326- win. close ( ) . ok ( ) ;
327- }
328- Ok ( true )
255+ Some ( id) => {
256+ camera_feed
257+ . ask ( feeds:: camera:: SetInput { id } )
258+ . await
259+ . map_err ( |e| e. to_string ( ) ) ?
260+ . await
261+ . map_err ( |e| e. to_string ( ) ) ?;
329262 }
330263 }
264+
265+ Ok ( ( ) )
331266}
332267
333268#[ derive( specta:: Type , Serialize , tauri_specta:: Event , Clone ) ]
@@ -1994,6 +1929,23 @@ pub async fn run(recording_logging_handle: LoggingHandle) {
19941929
19951930 let ( mic_samples_tx, mic_samples_rx) = flume:: bounded ( 8 ) ;
19961931
1932+ let camera_feed = {
1933+ let ( error_tx, error_rx) = flume:: bounded ( 1 ) ;
1934+
1935+ let mic_feed = CameraFeed :: spawn ( CameraFeed :: new ( error_tx) ) ;
1936+
1937+ // TODO: make this part of a global actor one day
1938+ tokio:: spawn ( async move {
1939+ let Ok ( err) = error_rx. recv_async ( ) . await else {
1940+ return ;
1941+ } ;
1942+
1943+ error ! ( "Camera feed actor error: {err:?}" ) ;
1944+ } ) ;
1945+
1946+ mic_feed
1947+ } ;
1948+
19971949 let mic_feed = {
19981950 let ( error_tx, error_rx) = flume:: bounded ( 1 ) ;
19991951
@@ -2108,12 +2060,11 @@ pub async fn run(recording_logging_handle: LoggingHandle) {
21082060 camera_tx,
21092061 camera_ws_port,
21102062 handle : app. clone ( ) ,
2111- camera_feed : None ,
2112- camera_feed_initialization : None ,
21132063 camera_preview : CameraPreviewManager :: new ( & app) ,
21142064 recording_state : RecordingState :: None ,
21152065 recording_logging_handle,
21162066 mic_feed,
2067+ camera_feed,
21172068 server_url : GeneralSettingsStore :: get ( & app)
21182069 . ok ( )
21192070 . flatten ( )
@@ -2198,7 +2149,10 @@ pub async fn run(recording_logging_handle: LoggingHandle) {
21982149 if !app_state. is_recording_active_or_pending ( ) {
21992150 let _ =
22002151 app_state. mic_feed . ask ( microphone:: RemoveInput ) . await ;
2201- app_state. camera_feed . take ( ) ;
2152+ let _ = app_state
2153+ . camera_feed
2154+ . ask ( feeds:: camera:: RemoveInput )
2155+ . await ;
22022156 }
22032157 } ) ;
22042158 }
0 commit comments