@@ -173,37 +173,48 @@ export const createDefaultCommonCallingHandlers = memoizeOne(
173173 const onToggleCamera = async ( options ?: VideoStreamOptions ) : Promise < void > => {
174174 const previewOn = _isPreviewOn ( callClient . getState ( ) . deviceManager ) ;
175175
176- if ( previewOn && call && call . state === 'Connecting' ) {
177- // This is to workaround: https://skype.visualstudio.com/SPOOL/_workitems/edit/3030558.
178- // The root cause of the issue is caused by never transitioning the unparented view to the
179- // call object when going from configuration page (disconnected call state) to connecting.
180- //
181- // Currently the only time the local video stream is moved from unparented view to the call
182- // object is when we transition from connecting -> call state. If the camera was on,
183- // inside the MediaGallery we trigger toggleCamera. This triggers onStartLocalVideo which
184- // destroys the unparentedView and creates a new stream in the call - so all looks well.
185- //
186- // However, if someone turns off their camera during the lobbyOrConnecting screen, the
187- // call.localVideoStreams will be empty (as the stream is currently stored in the unparented
188- // views and was never transitioned to the call object) and thus we incorrectly try to create
189- // a new video stream for the call object, instead of only stopping the unparented view.
190- //
191- // The correct fix for this is to ensure that callAgent.onStartCall is called with the
192- // localvideostream as a videoOption. That will mean call.onLocalVideoStreamsUpdated will
193- // be triggered when the call is in connecting state, which we can then transition the
194- // local video stream to the stateful call client and get into a clean state.
195- await onDisposeLocalStreamView ( ) ;
196- return ;
197- }
176+ // the disposal of the unparented views is to workaround: https://skype.visualstudio.com/SPOOL/_workitems/edit/3030558.
177+ // The root cause of the issue is caused by never transitioning the unparented view to the
178+ // call object when going from configuration page (disconnected call state) to connecting.
179+ //
180+ // Currently the only time the local video stream is moved from unparented view to the call
181+ // object is when we transition from connecting -> call state. If the camera was on,
182+ // inside the MediaGallery we trigger toggleCamera. This triggers onStartLocalVideo which
183+ // destroys the unparentedView and creates a new stream in the call - so all looks well.
184+ //
185+ // However, if someone turns off their camera during the lobbyOrConnecting screen, the
186+ // call.localVideoStreams will be empty (as the stream is currently stored in the unparented
187+ // views and was never transitioned to the call object) and thus we incorrectly try to create
188+ // a new video stream for the call object, instead of only stopping the unparented view.
189+ //
190+ // The correct fix for this is to ensure that callAgent.onStartCall is called with the
191+ // localvideostream as a videoOption. That will mean call.onLocalVideoStreamsUpdated will
192+ // be triggered when the call is in connecting state, which we can then transition the
193+ // local video stream to the stateful call client and get into a clean state.
198194
199195 if ( call && ( _isInCall ( call . state ) || _isInLobbyOrConnecting ( call . state ) ) ) {
200196 const stream = call . localVideoStreams . find ( ( stream ) => stream . mediaStreamType === 'Video' ) ;
201- if ( stream ) {
202- await onStopLocalVideo ( stream ) ;
197+ const unparentedViews = callClient . getState ( ) . deviceManager . unparentedViews ;
198+ if ( stream || unparentedViews . length > 0 ) {
199+ unparentedViews &&
200+ ( await unparentedViews . forEach (
201+ ( view ) => view . mediaStreamType === 'Video' && callClient . disposeView ( undefined , undefined , view )
202+ ) ) ;
203+ stream && ( await onStopLocalVideo ( stream ) ) ;
203204 } else {
204205 await onStartLocalVideo ( ) ;
205206 }
206207 } else {
208+ /**
209+ * This will create a unparented view to be used on the configuration page and the connecting screen
210+ *
211+ * If the device that the stream will come from is not on from permissions checks, then it will take time
212+ * to create the stream since device is off. If we are turn the camera on immedietly on the configuration page we see it is
213+ * fast but that is because the device is already primed to return a stream.
214+ *
215+ * On the connecting page the device has already turned off and the connecting window is so small we do not see the resulting
216+ * unparented view from the code below.
217+ */
207218 const selectedCamera = callClient . getState ( ) . deviceManager . selectedCamera ;
208219 if ( selectedCamera ) {
209220 if ( previewOn ) {
@@ -292,7 +303,7 @@ export const createDefaultCommonCallingHandlers = memoizeOne(
292303 } ;
293304
294305 const onToggleMicrophone = async ( ) : Promise < void > => {
295- if ( ! call || ! _isInCall ( call . state ) ) {
306+ if ( ! call || ! ( _isInCall ( call . state ) || _isInLobbyOrConnecting ( call . state ) ) ) {
296307 throw new Error ( `Please invoke onToggleMicrophone after call is started` ) ;
297308 }
298309 return call . isMuted ? await call . unmute ( ) : await call . mute ( ) ;
0 commit comments