@@ -4,6 +4,13 @@ import { useState, useCallback, useEffect, useRef, MutableRefObject } from 'reac
44import { getVirtualBackgroundConfigs } from '../utils/conferenceConfig' ;
55import { VirtualBackgroundTypes } from 'red5pro-conference-sdk' ;
66import log from 'loglevel' ;
7+ import { withTimeout } from '../utils/withTimeout' ;
8+
9+ // Some browsers (notably Safari, which falls back to an OffscreenCanvas/WASM
10+ // segmentation pipeline instead of insertable streams) can hang indefinitely
11+ // when initializing or applying a virtual background. Cap the wait so the UI
12+ // never gets stuck reporting the effect as enabled when it isn't working.
13+ const VIRTUAL_BACKGROUND_TIMEOUT_MS = 15000 ;
714
815// Type definitions
916type VirtualBackgroundType = 'none' | 'blur' | 'slight-blur' | 'color' | 'image' ;
@@ -152,7 +159,11 @@ export const useVirtualBackground = (
152159 const status = conferenceClientRef . current . getVirtualBackgroundStatus ( ) ;
153160
154161 if ( ! status . isInitialized ) {
155- await conferenceClientRef . current . initializeVirtualBackground ( ) ;
162+ await withTimeout (
163+ conferenceClientRef . current . initializeVirtualBackground ( ) ,
164+ VIRTUAL_BACKGROUND_TIMEOUT_MS ,
165+ 'Timed out initializing virtual background' ,
166+ ) ;
156167 log . log ( 'Virtual background initialized' ) ;
157168 setIsVirtualBackgroundInitialized ( true ) ;
158169 }
@@ -204,7 +215,11 @@ export const useVirtualBackground = (
204215
205216 // Handle disable case
206217 if ( type === VirtualBackgroundTypes . NONE ) {
207- await conferenceClientRef . current . disableVirtualBackground ( ) ;
218+ await withTimeout (
219+ conferenceClientRef . current . disableVirtualBackground ( ) ,
220+ VIRTUAL_BACKGROUND_TIMEOUT_MS ,
221+ 'Timed out disabling virtual background' ,
222+ ) ;
208223 setSelectedBackgroundMode ( '' ) ;
209224 return true ;
210225 }
@@ -233,14 +248,22 @@ export const useVirtualBackground = (
233248 }
234249
235250 // Apply the background
236- await conferenceClientRef . current [ action ] ( config . type , config . options ) ;
251+ await withTimeout (
252+ conferenceClientRef . current [ action ] ( config . type , config . options ) ,
253+ VIRTUAL_BACKGROUND_TIMEOUT_MS ,
254+ `Timed out applying virtual background (${ action } )` ,
255+ ) ;
237256 setSelectedBackgroundMode ( type ) ;
238257
239258 log . log ( `Virtual background ${ action } successful: ${ type } ` ) ;
240259 return true ;
241260 } catch ( error ) {
242261 const errorMessage = error instanceof Error ? error . message : 'Unknown error occurred' ;
243262 log . error ( 'Failed to handle background replacement:' , error ) ;
263+ // Ensure the UI never reports the effect as enabled when the apply call
264+ // hung/failed and the SDK never actually produced the processed frame.
265+ setIsVirtualBackgroundEnabled ( false ) ;
266+ setSelectedBackgroundMode ( '' ) ;
244267 if ( showWarningRef . current ) {
245268 showWarningRef . current ( 'Failed to apply virtual background: ' + errorMessage ) ;
246269 }
0 commit comments