mrousavy / react-native-vision-camera

📸 A powerful, high-performance React Native Camera library.
https://react-native-vision-camera.com
MIT License
6.72k stars 1k forks source link

🐛 Camera is still active after unmounting #905

Closed Cavallando closed 8 months ago

Cavallando commented 2 years ago

What were you trying to do?

We noticed that the Camera is still streaming frames even after the component is unmounted. This is made apparent by the green dot indicator in iOS. This appears to happen after we capture a video, take a snapshot or navigate away from the screen entirely.

I noticed an existing issue and docs where it was advised to set isActive to false which wouldn't work for us since we're using React navigation modals so the screen becomes unmounted.

I confirmed the screen is actually getting unmounted through a useEffect cleanup function on our Camera component.

I also noticed from Vision Camera's API docs for isActive that unmounting should destroy the camera.

Reproduceable Code

No response

What happened instead?

The camera never appears to stop using the devices camera unless we put the app in the background. After closing the modal and confirming it is no longer mounted, the green dot indicator still appears.

From the logs I see for Vision camera, it does not appear that self.captureSession.stopRunning() ever gets called because when the screen unmounts I do not see the logs:

Stopping Session...
Stopped Session!

Relevant log output

2022-03-14 14:02:39.169338-0600 Lovd[26103:7459565] [native] VisionCamera.didSetProps(_:): Updating 19 prop(s)...
2022-03-14 14:02:39.170900-0600 Lovd[26103:7460293] [native] VisionCamera.configureAudioSession(): Configuring Audio Session...
2022-03-14 14:02:39.171212-0600 Lovd[26103:7460155] [native] VisionCamera.configureCaptureSession(): Configuring Session...
2022-03-14 14:02:39.171240-0600 Lovd[26103:7460155] [native] VisionCamera.configureCaptureSession(): Initializing Camera with device com.apple.avfoundation.avcapturedevice.built-in_video:6...
2022-03-14 14:02:39.171273-0600 Lovd[26103:7460155] [native] VisionCamera.configureCaptureSession(): Adding Video input...
2022-03-14 14:02:39.171353-0600 Lovd[26103:7460293] [native] VisionCamera.configureAudioSession(): Adding Audio input...
2022-03-14 14:02:39.186415-0600 Lovd[26103:7460293] [native] VisionCamera.configureAudioSession(): Adding Audio Data output...
2022-03-14 14:02:39.189959-0600 Lovd[26103:7460155] [native] VisionCamera.configureCaptureSession(): Adding Photo output...
2022-03-14 14:02:39.201760-0600 Lovd[26103:7460155] [native] VisionCamera.configureCaptureSession(): Adding Video Data output...
2022-03-14 14:02:39.204065-0600 Lovd[26103:7460155] [native] VisionCamera.invokeOnInitialized(): Camera initialized!
2022-03-14 14:02:39.204571-0600 Lovd[26103:7460155] [native] VisionCamera.configureCaptureSession(): Session successfully configured!
2022-03-14 14:02:39.205990-0600 Lovd[26103:7460155] [native] VisionCamera.configureFormat(): Configuring Format...
2022-03-14 14:02:39.211181-0600 Lovd[26103:7460155] [native] VisionCamera.configureFormat(): Format successfully configured!
2022-03-14 14:02:39.211238-0600 Lovd[26103:7460155] [native] VisionCamera.configureDevice(): Configuring Device...
2022-03-14 14:02:39.211418-0600 Lovd[26103:7460155] [native] VisionCamera.configureDevice(): Device successfully configured!
2022-03-14 14:02:39.211646-0600 Lovd[26103:7460155] [native] VisionCamera.didSetProps(_:): Starting Session...
2022-03-14 14:02:40.360213-0600 Lovd[26103:7460155] [native] VisionCamera.didSetProps(_:): Started Session!
PID: 26103, TID: 7460155, Thread name: (none), Queue name: mrousavy/VisionCamera.main, QoS: 33
4   Lovd                                0x0000000105893cf4 $s12VisionCamera0B4ViewC26windowInterfaceOrientation33_2A3C5A0B3FDFD6D72264DCD42E2D487BLLSo011UIInterfaceF0Vvg + 96
5   Lovd                                0x0000000105893eec $s12VisionCamera0B4ViewC16inputOrientation33_2A3C5A0B3FDFD6D72264DCD42E2D487BLLSo011UIInterfaceE0Vvg + 24
6   Lovd                                0x0000000105894090 $s12VisionCamera0B4ViewC17outputOrientation33_2A3C5A0B3FDFD6D72264DCD42E2D487BLLSo011UIInterfaceE0Vvg + 408
7   Lovd                                0x0000000105894950 $s12VisionCamera0B4ViewC17updateOrientationyyFyycfU_yycfU_ySo15AVCaptureOutputCXEfU_ySo0F10ConnectionCXEfU_ + 136
11  Lovd                                0x0000000105894898 $s12VisionCamera0B4ViewC17updateOrientationyyFyycfU_yycfU_ySo15AVCaptureOutputCXEfU_ + 228
15  Lovd                                0x0000000105894784 $s12VisionCamera0B4ViewC17updateOrientationyyFyycfU_yycfU_ + 264
PID: 26103, TID: 7460155, Thread name: (none), Queue name: mrousavy/VisionCamera.main, QoS: 33
4   Lovd                                0x0000000105893cf4 $s12VisionCamera0B4ViewC26windowInterfaceOrientation33_2A3C5A0B3FDFD6D72264DCD42E2D487BLLSo011UIInterfaceF0Vvg + 96
5   Lovd                                0x0000000105893eec $s12VisionCamera0B4ViewC16inputOrientation33_2A3C5A0B3FDFD6D72264DCD42E2D487BLLSo011UIInterfaceE0Vvg + 24
6   Lovd                                0x0000000105894090 $s12VisionCamera0B4ViewC17outputOrientation33_2A3C5A0B3FDFD6D72264DCD42E2D487BLLSo011UIInterfaceE0Vvg + 408
7   Lovd                                0x0000000105894950 $s12VisionCamera0B4ViewC17updateOrientationyyFyycfU_yycfU_ySo15AVCaptureOutputCXEfU_ySo0F10ConnectionCXEfU_ + 136
11  Lovd                                0x0000000105894898 $s12VisionCamera0B4ViewC17updateOrientationyyFyycfU_yycfU_ySo15AVCaptureOutputCXEfU_ + 228
15  Lovd                                0x0000000105894784 $s12VisionCamera0B4ViewC17updateOrientationyyFyycfU_yycfU_ + 264
PID: 26103, TID: 7460155, Thread name: (none), Queue name: mrousavy/VisionCamera.main, QoS: 33
4   Lovd                                0x0000000105893da0 $s12VisionCamera0B4ViewC26windowInterfaceOrientation33_2A3C5A0B3FDFD6D72264DCD42E2D487BLLSo011UIInterfaceF0Vvg + 268
5   Lovd                                0x0000000105893eec $s12VisionCamera0B4ViewC16inputOrientation33_2A3C5A0B3FDFD6D72264DCD42E2D487BLLSo011UIInterfaceE0Vvg + 24
6   Lovd                                0x0000000105894090 $s12VisionCamera0B4ViewC17outputOrientation33_2A3C5A0B3FDFD6D72264DCD42E2D487BLLSo011UIInterfaceE0Vvg + 408
7   Lovd                                0x0000000105894950 $s12VisionCamera0B4ViewC17updateOrientationyyFyycfU_yycfU_ySo15AVCaptureOutputCXEfU_ySo0F10ConnectionCXEfU_ + 136
11  Lovd                                0x0000000105894898 $s12VisionCamera0B4ViewC17updateOrientationyyFyycfU_yycfU_ySo15AVCaptureOutputCXEfU_ + 228
15  Lovd                                0x0000000105894784 $s12VisionCamera0B4ViewC17updateOrientationyyFyycfU_yycfU_ + 264
PID: 26103, TID: 7460155, Thread name: (none), Queue name: mrousavy/VisionCamera.main, QoS: 33
0 $s12VisionCamera0B4ViewC26windowInterfaceOrientation33_2A3C5A0B3FDFD6D72264DCD42E2D487BLLSo011UIInterfaceF0Vvg + 268
5   Lovd                                0x0000000105893eec $s12VisionCamera0B4ViewC16inputOrientation33_2A3C5A0B3FDFD6D72264DCD42E2D487BLLSo011UIInterfaceE0Vvg + 24
6   Lovd                                0x0000000105894090 $s12VisionCamera0B4ViewC17outputOrientation33_2A3C5A0B3FDFD6D72264DCD42E2D487BLLSo011UIInterfaceE0Vvg + 408
7   Lovd                                0x0000000105894950 $s12VisionCamera0B4ViewC17updateOrientationyyFyycfU_yycfU_ySo15AVCaptureOutputCXEfU_ySo0F10ConnectionCXEfU_ + 136
105894898 $s12VisionCamera0B4ViewC17updateOrientationyyFyycfU_yycfU_ySo15AVCaptureOutputCXEfU_ + 228
15  Lovd                                0x0000000105894784 $s12VisionCamera0B4ViewC17updateOrientationyyFyycfU_yycfU_ + 264
PID: 26103, TID: 7460155, Thread name: (none), Queue name: mrousavy/VisionCamera.main, QoS: 33
4   Lovd                                0x0000000105893e04 $s12VisionCamera0B4ViewC26windowInterfaceOrientation33_2A3C5A0B3FDFD6D72264DCD42E2D487BLLSo011UIInterfaceF0Vvg + 368
5   Lovd                                0x0000000105893eec $s12VisionCamera0B4ViewC16inputOrientation33_2A3C5A0B3FDFD6D72264DCD42E2D487BLLSo011UIInterfaceE0Vvg + 24
6   Lovd                                0x0000000105894090 $s12VisionCamera0B4ViewC17outputOrientation33_2A3C5A0B3FDFD6D72264DCD42E2D487BLLSo011UIInterfaceE0Vvg + 408
7   Lovd                                0x0000000105894950 $s12VisionCamera0B4ViewC17updateOrientationyyFyycfU_yycfU_ySo15AVCaptureOutputCXEfU_ySo0F10ConnectionCXEfU_ + 136
11  Lovd                                0x0000000105894898 $s12VisionCamera0B4ViewC17updateOrientationyyFyycfU_yycfU_ySo15AVCaptureOutputCXEfU_ + 228
15  Lovd                                0x0000000105894784 $s12VisionCamera0B4ViewC17updateOrientationyyFyycfU_yycfU_ + 264
PID: 26103, TID: 7460155, Thread name: (none), Queue name: mrousavy/VisionCamera.main, QoS: 33
4   Lovd                                0x0000000105893e04 $s12VisionCamera0B4ViewC26windowInterfaceOrientation33_2A3C5A0B3FDFD6D72264DCD42E2D487BLLSo011UIInterfaceF0Vvg + 368
5   Lovd                                0x0000000105893eec $s12VisionCamera0B4ViewC16inputOrientation33_2A3C5A0B3FDFD6D72264DCD42E2D487BLLSo011UIInterfaceE0Vvg + 24
6   Lovd                                0x0000000105894090 $s12VisionCamera0B4ViewC17outputOrientation33_2A3C5A0B3FDFD6D72264DCD42E2D487BLLSo011UIInterfaceE0Vvg + 408
7   Lovd                                0x0000000105894950 $s12VisionCamera0B4ViewC17updateOrientationyyFyycfU_yycfU_ySo15AVCaptureOutputCXEfU_ySo0F10ConnectionCXEfU_ + 136
11  Lovd                                0x0000000105894898 $s12VisionCamera0B4ViewC17updateOrientationyyFyycfU_yycfU_ySo15AVCaptureOutputCXEfU_ + 228
15  Lovd                                0x0000000105894784 $s12VisionCamera0B4ViewC17updateOrientationyyFyycfU_yycfU_ + 264

Device

iOS Devices

VisionCamera Version

2.11.2, 2.12.2

Additional information

Cavallando commented 2 years ago

Was just trying some different things out and I was able to expose a stopSession function in CameraViewManager.swift to stop the capture session and this appropriately stops the activity:

  @objc
  final func stopSession(_ node: NSNumber) {
    let component = getCameraView(withTag: node)
    component.captureSession.stopRunning()
  }

This doesn't solve the issue really but just wanted to give some more info.

mrousavy commented 2 years ago

Hey! Hmm, interesting.

Are you maybe using React Freeze? Try doing enableFreeze(false) and see if that (temporarily) fixes the issue

Cavallando commented 2 years ago

Hey @mrousavy thanks for getting back to me! We are not currently using react freeze in our project. To expand on the changes I made above, it would only work when I changed override var bridge: RCTBridge! in CameraViewManager to override weak var bridge: RCTBridge!

EDIT:

For your iOS implementation, would using a weak var for the RCTBridge allow you to call captureSession.stopRunning() in the deinit of CameraView? Right now, it appears none of the deinit's are called when the component unmounts. This is probably me misunderstanding how the react native bridge works, but was thinking that without the weak initialization the bridge is strong so the deinit's never get called, apologies if this is irrelevant, just trying to understand things more!

hirbod commented 2 years ago

I am using enableFreeze and I can confirm that the green light indicator will still be on for me occasionally. Most of the times, it will turn off, but sometimes it wont. The consumed memory will reduce but never return back to the value before the camera started as well. Maybe its somehow related

mrousavy commented 2 years ago

UIKit works a bit differently, deinit is not a safe way to stop the camera session here because the view is never actually de-allocated. Just because you don't see it on-screen anymore does not mean the view is gone, it's still in the navigation hierarchy. It's just "moved away" (aka it does not have a parent window nor a parent view), maybe didMoveToWindow helps.

lfmatosm commented 2 years ago

Same here on the latest version. We're not using react-freeze on our project. Component unmounts but the camera stays active.

Device

Android Device

VisionCamera Version

2.13.0

Cavallando commented 2 years ago

Dug in a bit and to help clarify, react-native-screens built react freeze and use it under the hood, but still they specify that it is opt in and one would need to call enableFreeze(true) exported from the library. I can try this but at first read of their docs on it it feels unrelated.

@mrousavy thanks for the feed back on deinit, that does make a ton of sense. that does make me think now that it could possibly relate to react-native-screens/react-navigation and Native Stack which we are using. Ill dig further and also take a look at didMoveToWindow within vision camera development pod and see if I can find anything and report back!

hirbod commented 2 years ago

I have some more insights here. I am pretty sure that the main issue is coming from react native screens (but not enableFreeze).

I have other cases, where popped and unmounted Videos keep playing in the background even when the lifecycle methods report they have been unmounted. RNS is somehow always keeping the last view in the "system". And I think that is exactly what is happening here with the Camera. This is also explaining why memory is never freed again fully after the camera or a video was loaded.

I think what we all have in common is that we are using enableScreens or native stack. Only the js-Stack with enableScreens disabled (extremely bad performance) is not having this issues.

It looks like the detach mechanism is doing something bad.

I wish I had the knowledge to find the issue, but I think that's where the problem is coming from.

I found plenty of issues on the react-native-video repo and I think this is all connected to the same root problem (I am using expo-av though).

The problem only happens with nested navigators. Screens inside a tab stack for example do not have this issue, but a modal or "overlayed" navigator (hierarchy above tabs) does introduce this issue.

hirbod commented 2 years ago

@Cavallando are you using, by any chance, LayoutAnimation by react-native-reanimated or a library, which uses LayoutAnimation?

I found the root cause - at least for me. https://github.com/software-mansion/react-native-reanimated/issues/3124

It was enough for my App to have a single mounted element which has LayoutAnimation on it to break expo-av and react native vision cameras unmounting.

aditya-984 commented 1 year ago

This worked for me (using beforeRemove @react-navigation/native event listener):

  const [isActive, setActive] = useState(true);
  useEffect(() => {
    const unsubscribe = navigation.addListener('beforeRemove', () => {
      setActive(false);
    });
    return unsubscribe;
  }, [navigation]);
    <Camera
      ...
      isActive={isActive}
    />

Hope this helps to you as well!!

Shafran123 commented 1 year ago

any proper solution here!

BdN3504 commented 1 year ago

I'm using version 2.14.1 and also have this issue. Looking at logcat, it seems as if the camera is running the whole time, although the screen is not visible. To my surprise, tapping the hardware button "Recent Apps" twice closes the camera properly. I attached the Log on pastebin.

It would be nice if you could expose a ReactMethod to close the camera on Android side.

Log excerpt:


09-08 16:45:30.872 27667 28850 D CameraStateRegistry: tryOpenCamera(Camera@e49c13a[id=0]) [Available Cameras: 1, Already Open: false (Previous state: CLOSED)] --> SUCCESS
09-08 16:45:30.872 27667 27667 I CameraView: Attaching 1 use-cases...
09-08 16:45:30.873 27667 28850 D CameraStateRegistry: Recalculating open cameras:
09-08 16:45:30.873 27667 28850 D CameraStateRegistry: Camera                                       State                 
09-08 16:45:30.873 27667 28850 D CameraStateRegistry: -------------------------------------------------------------------
09-08 16:45:30.873 27667 28850 D CameraStateRegistry: Camera@e49c13a[id=0]                         OPENING               
09-08 16:45:30.873 27667 28850 D CameraStateRegistry: Camera@ec5f71d[id=1]                         UNKNOWN               
09-08 16:45:30.873 27667 28850 D CameraStateRegistry: -------------------------------------------------------------------
09-08 16:45:30.873 27667 28850 D CameraStateRegistry: Open count: 1 (Max allowed: 1)
09-08 16:45:30.873 27667 28850 D Camera2CameraImpl: {Camera@e49c13a[id=0]} Opening camera.
09-08 16:45:30.873 27667 28850 D Camera2CameraImpl: {Camera@e49c13a[id=0]} Transitioning camera internal state: INITIALIZED --> OPENING
09-08 16:45:30.874 27667 28850 D CameraStateMachine: New public camera state CameraState{type=OPENING, error=null} from OPENING and null
09-08 16:45:30.874 27667 28850 D CameraStateMachine: Publishing new public camera state CameraState{type=OPENING, error=null}
09-08 16:45:30.974  2151  3663 I CameraHardwareInterface: Opening camera 0
09-08 16:45:30.974   548  2538 I CamDev@1.0-impl: Opening camera 0
09-08 16:45:30.974   548  2538 I QCamera : <HAL><INFO> getCameraInfo: 343: Camera id 0 API version 256
09-08 16:45:30.974   548  2538 I QCamera : <HAL><INFO> getCamInfo: 8900: camera 0 resource cost is 100
09-08 16:45:30.974   548  2538 I QCamera : <HAL><INFO> cameraDeviceOpen: 408: Open camera id 0 API version 256
09-08 16:45:30.967 27667 27667 W Binder:27667_7: type=1400 audit(0.0:8644): avc: denied { read } for name="u:object_r:vendor_camera_prop:s0" dev="tmpfs" ino=14720 scontext=u:r:untrusted_app_25:s0:c512,c768 tcontext=u:object_r:vendor_camera_prop:s0 tclass=file permissive=0
09-08 16:45:30.976 27667 31163 E libc    : Access denied finding property "vendor.camera.aux.packagelist"
09-08 16:45:30.976 27667 31163 E libc    : Access denied finding property "vendor.camera.aux.packagelist2"
09-08 16:45:30.967 27667 27667 W Binder:27667_7: type=1400 audit(0.0:8645): avc: denied { read } for name="u:object_r:vendor_camera_prop:s0" dev="tmpfs" ino=14720 scontext=u:r:untrusted_app_25:s0:c512,c768 tcontext=u:object_r:vendor_camera_prop:s0 tclass=file permissive=0
09-08 16:45:31.129   548  2538 I QCamera : <HAL><INFO> openCamera: 1864: [KPI Perf]: E PROFILE_OPEN_CAMERA camera id 0
09-08 16:45:31.131  2151  3663 I CameraProviderManager: Camera device device@1.0/legacy/0 torch status is now NOT_AVAILABLE
09-08 16:45:31.131  2151  3663 I CameraService: onTorchStatusChangedLocked: Torch status changed for cameraId=0, newStatus=0
09-08 16:45:31.132  2217  2217 I mm-camera: < INFO> 395: enable_memleak_trace: start memleak tracking.
09-08 16:45:31.218  2217  2217 I mm-camera: <MCT   >< INFO> 63: mct_controller_new: Creating new mct_controller with session-id 1
09-08 16:45:31.218  2217 31816 I mm-camera: <MCT   >< INFO> 4684: mct_pipeline_start_session_thread: E sensor
09-08 16:45:31.218  2217 31816 I mm-camera: <MCT   >< INFO> 4691: mct_pipeline_start_session_thread: Calling start_session on Module sensor
09-08 16:45:31.218  2217 31817 I mm-camera: <MCT   >< INFO> 4684: mct_pipeline_start_session_thread: E iface
09-08 16:45:31.218  2217 31817 I mm-camera: <MCT   >< INFO> 4691: mct_pipeline_start_session_thread: Calling start_session on Module iface
09-08 16:45:31.219  2217 31819 I mm-camera: <MCT   >< INFO> 4684: mct_pipeline_start_session_thread: E isp
09-08 16:45:31.219  2217 31819 I mm-camera: <MCT   >< INFO> 4691: mct_pipeline_start_session_thread: Calling start_session on Module isp
09-08 16:45:31.219  2217 31819 I mm-camera: <ISP   >< INFO> 205: isp_module_start_session: session id 1
09-08 16:45:31.334 27667 28850 D Camera2CameraImpl: {Camera@e49c13a[id=0]} Transitioning camera internal state: REOPENING --> OPENED
09-08 16:45:31.334 27667 28850 D CameraStateRegistry: Recalculating open cameras:
09-08 16:45:31.334 27667 28850 D CameraStateRegistry: Camera                                       State                 
09-08 16:45:31.334 27667 28850 D CameraStateRegistry: -------------------------------------------------------------------
09-08 16:45:31.334 27667 28850 D CameraStateRegistry: Camera@e49c13a[id=0]                         OPEN                  
09-08 16:45:31.334 27667 28850 D CameraStateRegistry: Camera@ec5f71d[id=1]                         UNKNOWN               
09-08 16:45:31.334 27667 28850 D CameraStateRegistry: -------------------------------------------------------------------
09-08 16:45:31.334 27667 28850 D CameraStateRegistry: Open count: 1 (Max allowed: 1)
09-08 16:45:31.335 27667 28850 D CameraStateMachine: New public camera state CameraState{type=OPEN, error=null} from OPEN and null
09-08 16:45:31.335 27667 28850 D CameraStateMachine: Publishing new public camera state CameraState{type=OPEN, error=null}
09-08 16:45:31.335 27667 28850 D UseCaseAttachState: All use case: [androidx.camera.core.ImageAnalysis-0e3eeab4-1fb3-4dd2-9e2b-39b65fc2da1d84982627, androidx.camera.core.Preview-ce0a7cbb-6321-46da-b928-2483986dc59c211842450] for camera: 0
09-08 16:45:31.337 27667 28850 D UseCaseAttachState: Active and attached use case: [androidx.camera.core.ImageAnalysis-0e3eeab4-1fb3-4dd2-9e2b-39b65fc2da1d84982627, androidx.camera.core.Preview-ce0a7cbb-6321-46da-b928-2483986dc59c211842450] for camera: 0
09-08 16:45:31.493   548 31813 I QCamera : <HAL><INFO> startPreview: 3909: X rc = 0
09-08 16:45:31.493   548  3003 I QCamera : <HAL><INFO> start_preview: 388: [KPI Perf]: X ret = 0
09-08 16:45:31.496  2217 31837 I mm-camera: <MCT   >< INFO> 3850: mct_pipeline_process_set: command=8000016
09-08 16:45:31.496  2217 31837 I mm-camera: <MCT   >< INFO> 4203: mct_pipeline_process_set: store param event: 0xa
09-08 16:45:31.524  2217 31855 I mm-camera: <ISP   >< INFO> 1260: isp_handler_module_handle_reg_update:  Warning! Invalid reg_update state 0
09-08 16:45:31.526  2217 31839 I mm-camera: <MCT   >< INFO> 1021: mct_controller_handle_SOF_proc: (sofdelay, curr_sofdelay) = (0, 0) kptr 1
9-08 16:45:36.789  2217 31839 I mm-camera: <MCT   >< INFO> 1021: mct_controller_handle_SOF_proc: (sofdelay, curr_sofdelay) = (0, 0) kptr 79
09-08 16:45:36.836 27667 27667 D CameraView: Lifecycle went from RESUMED -> CREATED (isActive: true | isAttachedToWindow: false)
09-08 16:45:36.837 27667 28850 D Camera2CameraImpl: {Camera@e49c13a[id=0]} Use cases [androidx.camera.core.Preview-ce0a7cbb-6321-46da-b928-2483986dc59c211842450, androidx.camera.core.ImageAnalysis-0e3eeab4-1fb3-4dd2-9e2b-39b65fc2da1d84982627] now DETACHED for camera
09-08 16:45:36.837 27667 28850 D UseCaseAttachState: All use case: [] for camera: 0
09-08 16:45:36.839 27667 28850 D Camera2CameraImpl: {Camera@e49c13a[id=0]} Resetting Capture Session
09-08 16:45:36.840 27667 28850 D DeferrableSurface: surface closed,  useCount=1 closed=true androidx.camera.core.SurfaceRequest$2@7c9aabf
09-08 16:45:36.840 27667 28850 D DeferrableSurface: surface closed,  useCount=1 closed=true androidx.camera.core.impl.ImmediateSurface@14124ea
09-08 16:45:36.840 27667 28850 D SyncCaptureSessionImpl: [androidx.camera.camera2.internal.SynchronizedCaptureSessionImpl@2a93716] deferrableSurface closed
09-08 16:45:36.840 27667 28850 D SyncCaptureSessionImpl: [androidx.camera.camera2.internal.SynchronizedCaptureSessionImpl@2a93716] Session call close()
09-08 16:45:36.840 27667 28850 D SyncCaptureSessionImpl: [androidx.camera.camera2.internal.SynchronizedCaptureSessionImpl@2a93716] deferrableSurface closed
09-08 16:45:36.841 27667 28850 D Camera2CameraImpl: {Camera@e49c13a[id=0]} Releasing session in state OPENED
09-08 16:45:36.841 27667 28850 D UseCaseAttachState: Active and attached use case: [] for camera: 0
09-08 16:45:36.842 27667 28850 D UseCaseAttachState: Active and attached use case: [] for camera: 0
09-08 16:45:36.844 27667 28850 D Camera2CameraImpl: {Camera@e49c13a[id=0]} Closing camera.
09-08 16:45:36.844 27667 28850 D Camera2CameraImpl: {Camera@e49c13a[id=0]} Transitioning camera internal state: OPENED --> CLOSING
09-08 16:45:36.845 27667 28850 D CameraStateRegistry: Recalculating open cameras:
09-08 16:45:36.845 27667 28850 D CameraStateRegistry: Camera                                       State                 
09-08 16:45:36.845 27667 28850 D CameraStateRegistry: -------------------------------------------------------------------
09-08 16:45:36.845 27667 28850 D CameraStateRegistry: Camera@e49c13a[id=0]                         CLOSING               
09-08 16:45:36.845 27667 28850 D CameraStateRegistry: Camera@ec5f71d[id=1]                         UNKNOWN               
09-08 16:45:36.845 27667 28850 D CameraStateRegistry: -------------------------------------------------------------------
09-08 16:45:36.845 27667 28850 D CameraStateRegistry: Open count: 1 (Max allowed: 1)
09-08 16:45:37.154 27667 28850 D SyncCaptureSessionImpl: [androidx.camera.camera2.internal.SynchronizedCaptureSessionImpl@924ae2] deferrableSurface closed
09-08 16:45:37.154 27667 28850 D SyncCaptureSessionImpl: [androidx.camera.camera2.internal.SynchronizedCaptureSessionImpl@924ae2] onClosed()
09-08 16:45:37.154 27667 28850 D DeferrableSurface: use count-1,  useCount=0 closed=true androidx.camera.core.impl.ImmediateSurface@d209cc4
09-08 16:45:37.154 27667 28850 D DeferrableSurface: Surface no longer in use[total_surfaces=1, used_surfaces=0](androidx.camera.core.impl.ImmediateSurface@d209cc4}
09-08 16:45:37.154 27667 28850 D DeferrableSurface: Surface terminated[total_surfaces=0, used_surfaces=0](androidx.camera.core.impl.ImmediateSurface@d209cc4}
09-08 16:45:37.156 27667 28850 D Camera2CameraImpl: {Camera@e49c13a[id=0]} CameraDevice.onClosed()
09-08 16:45:37.157 27667 28850 D Camera2CameraImpl: {Camera@e49c13a[id=0]} Transitioning camera internal state: CLOSING --> INITIALIZED
09-08 16:45:37.159 27667 28850 D CameraStateRegistry: Recalculating open cameras:
09-08 16:45:37.159 27667 28850 D CameraStateRegistry: Camera                                       State                 
09-08 16:45:37.159 27667 28850 D CameraStateRegistry: -------------------------------------------------------------------
09-08 16:45:37.159 27667 28850 D CameraStateRegistry: Camera@e49c13a[id=0]                         CLOSED                
09-08 16:45:37.159 27667 28850 D CameraStateRegistry: Camera@ec5f71d[id=1]                         UNKNOWN               
09-08 16:45:37.159 27667 28850 D CameraStateRegistry: -------------------------------------------------------------------
09-08 16:45:37.159 27667 28850 D CameraStateRegistry: Open count: 0 (Max allowed: 1)
BdN3504 commented 1 year ago

I solved this issue with the help of the suggestions in this thread, namely moving the camera component from a modal dialog into its own activity that can be navigated to and adding a 'beforeRemove' listener to @react-navigation/native:

import {useNavigation} from "@react-navigation/native";

    const navigation = useNavigation();

    useEffect(() => {
        navigation.addListener("beforeRemove", () => {
            setIsActive(false)
        });

        return () => navigation.removeListener("beforeRemove", () => {
            setIsActive(false)
        });
    }, [navigation]);
rmyr commented 1 year ago

I think it would be helpful for a workaround if you could disable the camera using the ref.

gustavo867 commented 1 year ago

Hi, i am with the same error, any solutions?

mohd-kasif commented 1 year ago

facing same error, tried @BdN3504 solution but didn't work.

andreyboberskiy commented 1 year ago

If you want off camera on unmounted component (blurred screen in react navigation) you need set isActive to false before unmount (navigating back) you component, very important wait for one render before unmount, so you need define close handler like this

const closeCamera = useCallback(() => { setActive(false) setTimeout(() => { navigation.goBack(); }, 10); }, [navigation]);.

Also I set gestureEnabled={false} in Stack.Screen, I replaced android back button function useEffect(() => { const backHandler = BackHandler.addEventListener( 'hardwareBackPress', closeCamera, ); return () => backHandler.remove(); }, [closeCamera]); and added close button with this handler.

All frameProcessor are shut down after that

mrousavy commented 1 year ago

I'll investigate this more in the V3 efforts so that you don't need such a workaround and it unmounts smoothly. :)

bazter commented 1 year ago

Using a tiny timeout after setting isActive to false before navigating away from the camera view fixed the issue in our environment, thanks for the tip @andreyboberskiy

Sid-Turner-Ellis commented 1 year ago

Thanks @andreyboberskiy - that helped in most cases for me.

It doesn't seem to work when an error is thrown in the component that has the camera though, i've tried moving the camera component up the tree into an error boundary component but it still doesn't want to close.

@mrousavy is there any hacky way of killing all camera instances without leaving the app?

Sid-Turner-Ellis commented 1 year ago

I ended up having to set the fallback to a component that renders a new camera component and then kill that one the same way as @andreyboberskiy described above - not a great solution but it will do for now

mrousavy commented 1 year ago

hey! I'm hoping that V3 solves this, if the lifecycle gets killed, so should the Camera. React Freeze is always a culprit for me.

noddy1996 commented 1 year ago

I just simply did this thing

const focused = useIsFocused()

<Camera ... isActive={focused} ... />

mustapha-ghlissi commented 1 year ago

I just simply did this thing

const focused = useIsFocused()

<Camera ... isActive={focused} ... />

I confirm, this the clean solution to have fresh Camera state on each focus/blur event.

mrousavy commented 8 months ago

This is now fixed in V3! 🥳

dFelinger commented 6 months ago

This is now fixed in V3! 🥳

iOS green dot is still active on react navigation page back even with using useIsFocused (useIsFocused is not changes on page back).

react-native-vision-camera: 3.6.4

mrousavy commented 6 months ago

That's weird - I am pretty sure I tested that... I'll try again later

dleavittpmc commented 6 months ago

I'm having a similar problem, I cant get the camera to unmount. None of the above fixes unmounts the camera for me.

Downgrading to 3.2.2 fixes the issue for me with the above fixes.

react-native-vision-camera: 3.6.4

mrousavy commented 6 months ago

Hey! I just found out that I really forgot to close and dispose the locked Camera resources, so I just fixed that in this PR: https://github.com/mrousavy/react-native-vision-camera/pull/2174 Now the Camera fully & synchronously closes all resources (CameraSession, CameraDevice, OpenGL context, Video & Photo outputs, Photo Synchronizer) once the view gets removed from the React view hierarchy ("unmounted"), and things like Flash, Torch, NFC, and other Camera components should be working again.

There is still a small issue that causes once Camera component to turn into a blackscreen when navigating back and forth between two Camera components, that's a pretty rare edge case but I will still try to fix that soon when I have some free time.

If you appreciate my time, expertise and dedication to this project, pleas 💖 consider sponsoring me on GitHub 💖 to support the development of this project.

dFelinger commented 6 months ago

Is this fix (https://github.com/mrousavy/react-native-vision-camera/pull/2174) only for Android? What about iOS?

mrousavy commented 6 months ago

I don't think the issue exists on iOS at the moment. At least for me, the Camera closes when I minimize the app.

dFelinger commented 6 months ago

Finally, because @react-navigation/native-stack can't get the blur event when page is going back, I use this custom hook to get page focus state by transitionEnd event.

import { useEffect, useState } from 'react';

import { useNavigation } from '@react-navigation/native';

const useIsFocused = () => {
  const navigation = useNavigation();
  const [isFocused, setIsFocused] = useState(false);

  useEffect(() => {
    return navigation.addListener('transitionEnd', () => {
      setIsFocused(state => !state);
    });
  }, [navigation]);

  return isFocused;
};

export default useIsFocused;

I don't know how the camera works under the hood, but it seems like all camera instances are keep existing on page unmount. https://github.com/software-mansion/react-native-reanimated/issues/3124

Nice-zrd commented 5 months ago

My project version is "react-native": "0.72.6" , "react-native-vision-camera": "^3.6.17" , I also encountered the same problem. Is there a solution available? After uninstalling the camera in Android, it still remains active. I have set the isActive={false} attribute。

const device = useCameraDevice('back')
const [isActive, setIsActive] = useState(false);
const isFocused = useIsFocused()
const appState = useAppState()

useEffect(() => {
    if (device) {
        // Delaying the camera by one second to solve the black screen problem
        const timeout = setTimeout(() => {
            setIsActive(
                isFocused && appState === "active")
            }, 1000)
        return () => clearTimeout(timeout)
    }
}, [isFocused, appState])

<Camera
    ref={camera}
    style={[StyleSheet.absoluteFill, { backgroundColor: 'black', }]}
    resizeMode='cover'
    isActive={isActive}
    device={device}
    codeScanner={codeScanner}
    torch={isLight ? 'on' : 'off'}
/>