@@ -22,6 +22,7 @@ class NCCameraController: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate
2222 // State
2323 private var backgroundBlurEnabled = NCUserDefaults . backgroundBlurEnabled ( )
2424 private var usingFrontCamera = true
25+ private var previousOrientationBeforeUpsideDown : UIDeviceOrientation ?
2526 private var deviceOrientation : UIDeviceOrientation = UIDevice . current. orientation
2627 private var videoRotation : RTCVideoRotation = . _0
2728 private var firstLocalViewFrameDrawn = false
@@ -92,26 +93,32 @@ class NCCameraController: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate
9293 }
9394
9495 func switchCamera( ) {
95- var newInput : AVCaptureDeviceInput
96+ DispatchQueue . global ( qos: . userInitiated) . async { [ weak self] in
97+ guard let self = self else { return }
98+ var newInput : AVCaptureDeviceInput
9699
97- if self . usingFrontCamera {
98- newInput = getBackCameraInput ( )
99- } else {
100- newInput = getFrontCameraInput ( )
101- }
100+ if self . usingFrontCamera {
101+ newInput = getBackCameraInput ( )
102+ } else {
103+ newInput = getFrontCameraInput ( )
104+ }
102105
103- if let firstInput = session? . inputs. first {
104- session? . removeInput ( firstInput)
105- }
106+ if let firstInput = session? . inputs. first {
107+ session? . removeInput ( firstInput)
108+ }
106109
107- // Stop and restart the session to prevent a weird glitch when rotating our local view
108- self . session? . stopRunning ( )
109- self . session? . addInput ( newInput)
110+ // Stop and restart the session to prevent a weird glitch when rotating our local view
111+ self . session? . stopRunning ( )
112+ self . session? . addInput ( newInput)
113+
114+ // We need to set the orientation again, because otherweise after switching the video is turned
115+ self . session? . outputs. first? . connections. first? . videoOrientation = . portrait
116+ self . session? . startRunning ( )
110117
111- // We need to set the orientation again, because otherweise after switching the video is turned
112- self . session ? . outputs . first ? . connections . first ? . videoOrientation = . portrait
113- self . session ? . startRunning ( )
114- self . usingFrontCamera = ! self . usingFrontCamera
118+ // Toggle usingFrontCamera flag and update video rotation
119+ self . usingFrontCamera . toggle ( )
120+ self . updateVideoRotationBasedOnDeviceOrientation ( )
121+ }
115122 }
116123
117124 // See ARDCaptureController from the WebRTC project
@@ -326,12 +333,31 @@ class NCCameraController: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate
326333 // Correctly rotate the local image
327334 if videoRotation == . _180 {
328335 ciImage = ciImage. oriented ( . down)
336+
337+ // Rotate the local image on iPhones to match the previous landscape orientation when in UpsideDown.
338+ // Since the localView frame doesn't change when transitioning from landscape to UpsideDown.
339+ if UIDevice . current. userInterfaceIdiom == . phone,
340+ let previousLandscapeOrientation = previousOrientationBeforeUpsideDown {
341+
342+ if previousLandscapeOrientation == . landscapeLeft {
343+ ciImage = usingFrontCamera ? ciImage. oriented ( . left) : ciImage. oriented ( . right)
344+ } else if previousLandscapeOrientation == . landscapeRight {
345+ ciImage = usingFrontCamera ? ciImage. oriented ( . right) : ciImage. oriented ( . left)
346+ }
347+ }
348+
329349 } else if videoRotation == . _90 {
330350 ciImage = ciImage. oriented ( . right)
331351 } else if videoRotation == . _270 {
332352 ciImage = ciImage. oriented ( . left)
333353 }
334354
355+ // Mirror local image when using front camera
356+ if usingFrontCamera {
357+ let mirrorTransform = CGAffineTransform ( translationX: ciImage. extent. width, y: 0 ) . scaledBy ( x: - 1 , y: 1 )
358+ ciImage = ciImage. transformed ( by: mirrorTransform)
359+ }
360+
335361 // make sure the image is full screen
336362 let drawSize = localView. drawableSize
337363 let scaleX = drawSize. width / ciImage. extent. width
@@ -366,8 +392,19 @@ class NCCameraController: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate
366392 // MARK: - Notifications
367393
368394 func deviceOrientationDidChangeNotification( ) {
369- self . deviceOrientation = UIDevice . current. orientation
370- self . updateVideoRotationBasedOnDeviceOrientation ( )
395+ let currentOrientation = UIDevice . current. orientation
396+
397+ // The app doesn't support UpsideDown orientation on iPhones, so the local view stays in landscape when
398+ // transitioning from a landscape orientation to UpsideDown.
399+ // We need to track the last landscape orientation to rotate the local view correctly.
400+ if currentOrientation == . portraitUpsideDown, deviceOrientation. isLandscape {
401+ previousOrientationBeforeUpsideDown = deviceOrientation
402+ } else if currentOrientation != . portraitUpsideDown {
403+ previousOrientationBeforeUpsideDown = nil
404+ }
405+
406+ deviceOrientation = currentOrientation
407+ updateVideoRotationBasedOnDeviceOrientation ( )
371408 }
372409
373410 func updateVideoRotationBasedOnDeviceOrientation( ) {
0 commit comments