Status Update
Comments
le...@google.com <le...@google.com> #2
do...@gmail.com <do...@gmail.com> #3
Great! Thanks a lot, I'll look for the live updates soon!
le...@google.com <le...@google.com> #4
Hi Doudera, Thank you for the detail description. It sounds like some problem in the code. I will investigate it more.
le...@google.com <le...@google.com> #5
From the log, it shows the camera state
-> open -> enable torch -> close -> open
The torch was gone because it restarted the camera.
It needs to investigate more about why it open/close so frequently.
le...@google.com <le...@google.com> #6
In CameraXBasic, it looks like the "cameraProvider.unbindAll()" call in CameraFragment.bindCameraUseCases() method will cause the camera close.
Since camera operation is in another thread, a timing causes the enableTorch() failed because the camera is in closed state at that moment.
It can be detected by checking the returned ListenableFuture result from cameraControl.enableTorch()
020-03-29 12:34:56.139 4946-4946/com.android.example.cameraxbasic E/CameraXBasic: enableTorch
2020-03-29 12:34:56.225 4946-5035/com.android.example.cameraxbasic D/Camera: Transitioning camera internal state: OPENED --> CLOSING
2020-03-29 12:34:56.226 4946-4946/com.android.example.cameraxbasic E/CameraXBasic: enableTorch fail by java.util.concurrent.ExecutionException: androidx.camera.core.CameraControl$OperationCanceledException: Camera is not active.
java.util.concurrent.ExecutionException: androidx.camera.core.CameraControl$OperationCanceledException: Camera is not active.
at androidx.concurrent.futures.AbstractResolvableFuture.getDoneValue(AbstractResolvableFuture.java:518)
at androidx.concurrent.futures.AbstractResolvableFuture.get(AbstractResolvableFuture.java:475)
at androidx.concurrent.futures.CallbackToFutureAdapter$SafeFuture.get(CallbackToFutureAdapter.java:199)
at com.android.example.cameraxbasic.fragments.CameraFragment$bindCameraUseCases$1$2$1.run(CameraFragment.kt:310)
at android.os.Handler.handleCallback(Handler.java:907)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:216)
at android.app.ActivityThread.main(ActivityThread.java:7476)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:549)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:968)
Caused by: androidx.camera.core.CameraControl$OperationCanceledException: Camera is not active.
at androidx.camera.camera2.internal.TorchControl.setActive(TorchControl.java:112)
at androidx.camera.camera2.internal.Camera2CameraControl.setActive(Camera2CameraControl.java:135)
at androidx.camera.camera2.internal.Camera2CameraImpl.tryRemoveOnlineUseCases(Camera2CameraImpl.java:727)
at androidx.camera.camera2.internal.Camera2CameraImpl.lambda$removeOnlineUseCase$13$Camera2CameraImpl(Camera2CameraImpl.java:699)
at androidx.camera.camera2.internal.-$$Lambda$Camera2CameraImpl$5wc8VkOCNW87m5eLEZfzgWUl-nY.run(Unknown Source:4)
at android.os.Handler.handleCallback(Handler.java:907)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:216)
at android.os.HandlerThread.run(HandlerThread.java:67)
It is a defect that CameraX should guarantee the order of the camera request.
Another topic is that why camera will be closed when resuming the CameraFragment.
CameraX is lifecycle aware, if UseCases is bound and pause/resume the page, CameraX will restart those UseCases automatically. So actually it doesn't have to unbind and rebind those UseCases. Unbind and rebind UseCases causes it open camera twice and meet this timing issue. In CameraXBasic, it will unbind and rebind UseCases in onViewCreated().
It is easier to be done in an Activity, just put the bindToLifecycle(...) code in the Activity.onCreate(). For Fragment, maybe Fragment.onAttach() is something like Activity.onCreate, but not pretty sure if it is practical. Or alternatively, it has to unbind all use case in a paired callback such like Fragment.onDestroyView() to prevent Camera from being automatically opened when resuming.
wu...@google.com <wu...@google.com>
ap...@google.com <ap...@google.com> #7
Branch: androidx-master-dev
commit 76ad175d0c681ddc6a1b86ff35b180f97049da0d
Author: leo huang <leohuang@google.com>
Date: Sat Jul 11 11:15:41 2020
Fix CameraControl.enableTorch is not working
The issue is caused by a race condition. Camera2CameraControl#setActive(true) is called on the main thread, but #setActive(false) is called on the camera thread. It will cause the control to be in a wrong state and not work properly.
The solution is to post all operations to camera thread to ensure the API calls are executed in the correct order.
But it must update the "LiveData" in advance on the main thread to maintain an existing behavior, i.e. user can immediately obtain the new LiveData value through continuous API calls on the main thread. However, trying to maintain this behavior will conflict with the above solution. There are 2 issues that need to be corrected to ensure the final LiveData value is correct.
(1) LiveData that was updated earlier on the main thread may reset by "setActive(false)" executed on the camera thread. This issue can be resolved by updating LiveData again in the posted camera operation since the tasks on the camera thread always have correct order.
(2) Before updating LiveData on the main thread, it cannot directly check the "active state" of CameraControl because the "active state" is only correct on the camera thread. The solution is to create a synchronized "use count" that can be queried on all threads. In this way, "use count" represents whether CameraControl accepts new requests, "active state" represents whether the requests are sent to Camera.
"Relnote: Fix the CameraControl unable to work by a race condition"
Bug: 152333890
Bug: 160928870
Bug: 160714166
Test: ./gradlew camera:camera-camera2:connectedAndroidTest; ./gradlew camera:camera-camera2:test
Change-Id: I2279f7bda1a9edf90af0c46b2db749d59821e0cc
M camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/CameraControlDeviceTest.java
M camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraControlTest.java
M camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ZoomControlDeviceTest.java
M camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControl.java
M camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
M camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/FocusMeteringControl.java
M camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/TorchControl.java
M camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ZoomControl.java
M camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/TorchControlTest.java
M camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/ZoomControlTest.java
Description
1.0.0-beta01
CAMERA APPLICATION NAME AND VERSION:
CameraXBasic with modification to have torch always on
ANDROID OS BUILD NUMBER:
QQ2A.200305.002
DEVICE NAME:
Pixel 3a
DESCRIPTION:
Enabling torch after camera binding is not reliable. It always works only for the first Activity launch, but when returning to the same Activity is not working correctly.
LIST ANY EXPERIMENTAL FEATURES:
N/A
STEPS TO REPRODUCE:
1. Launch the example app with modification below, which enables torch after binding.
2. Navigate to Gallery.
3. Go Back to camera preview.
OBSERVED RESULTS:
A torch is switched off.
EXPECTED RESULTS:
A torch is always on.
REPRODUCIBILITY:
5 of 5
ADDITIONAL INFORMATION:
Log from opening app (torch is on) -> Gallery (then Back button) -> camera (torch is off):
CameraXBasic: Screen metrics: 1080 x 2220
CameraXBasic: Preview aspect ratio: 1
CameraRepository: Added camera: 0
UseCaseAttachState: Active and online use case: [] for camera: 0
CameraRepository: Added camera: 1
UseCaseAttachState: Active and online use case: [] for camera: 1
UseCaseAttachState: Active and online use case: [] for camera: 0
Camera: Use cases [ImageCapture:androidx.camera.core.ImageCapture-0adade8a-60ab-4b3e-8b97-370ff318f73b, Preview:androidx.camera.core.Preview-c3e392d8-57ad-40a2-989e-b56ebd134eaf, ImageAnalysis:androidx.camera.core.ImageAnalysis-1671baa6-a5f8-45f0-9bb7-dab949c97b3b] now ONLINE for camera 0
UseCaseAttachState: Active and online use case: [] for camera: 0
Camera: Resetting Capture Session
Camera: releasing session in state INITIALIZED
Camera: Transitioning camera internal state: INITIALIZED --> OPENING
Camera: Opening camera: 0
UseCaseAttachState: All use case: [androidx.camera.core.ImageCapture-0adade8a-60ab-4b3e-8b97-370ff318f73b, androidx.camera.core.Preview-c3e392d8-57ad-40a2-989e-b56ebd134eaf, androidx.camera.core.ImageAnalysis-1671baa6-a5f8-45f0-9bb7-dab949c97b3b] for camera: 0
Camera: Use case ImageCapture:androidx.camera.core.ImageCapture-0adade8a-60ab-4b3e-8b97-370ff318f73b ACTIVE for camera 0
UseCaseAttachState: Active and online use case: [androidx.camera.core.ImageCapture-0adade8a-60ab-4b3e-8b97-370ff318f73b] for camera: 0
Camera: Use case Preview:androidx.camera.core.Preview-c3e392d8-57ad-40a2-989e-b56ebd134eaf ACTIVE for camera 0
UseCaseAttachState: Active and online use case: [androidx.camera.core.ImageCapture-0adade8a-60ab-4b3e-8b97-370ff318f73b, androidx.camera.core.Preview-c3e392d8-57ad-40a2-989e-b56ebd134eaf] for camera: 0
Camera: Use case ImageAnalysis:androidx.camera.core.ImageAnalysis-1671baa6-a5f8-45f0-9bb7-dab949c97b3b ACTIVE for camera 0
UseCaseAttachState: Active and online use case: [androidx.camera.core.ImageCapture-0adade8a-60ab-4b3e-8b97-370ff318f73b, androidx.camera.core.Preview-c3e392d8-57ad-40a2-989e-b56ebd134eaf, androidx.camera.core.ImageAnalysis-1671baa6-a5f8-45f0-9bb7-dab949c97b3b] for camera: 0
UseCaseAttachState: Active and online use case: [androidx.camera.core.ImageCapture-0adade8a-60ab-4b3e-8b97-370ff318f73b, androidx.camera.core.Preview-c3e392d8-57ad-40a2-989e-b56ebd134eaf, androidx.camera.core.ImageAnalysis-1671baa6-a5f8-45f0-9bb7-dab949c97b3b] for camera: 0
Camera: CameraDevice.onOpened(): 0
Camera: Transitioning camera internal state: OPENING --> OPENED
UseCaseAttachState: All use case: [androidx.camera.core.ImageCapture-0adade8a-60ab-4b3e-8b97-370ff318f73b, androidx.camera.core.Preview-c3e392d8-57ad-40a2-989e-b56ebd134eaf, androidx.camera.core.ImageAnalysis-1671baa6-a5f8-45f0-9bb7-dab949c97b3b] for camera: 0
CaptureSession: Opening capture session.
CaptureSession: Attempting to send capture request onConfigured
CaptureSession: Issuing request for session.
CaptureSession: CameraCaptureSession.onConfigured() mState=OPENED
CaptureSession: CameraCaptureSession.onReady() OPENED
Camera: Use cases [ImageCapture:androidx.camera.core.ImageCapture-0adade8a-60ab-4b3e-8b97-370ff318f73b, Preview:androidx.camera.core.Preview-c3e392d8-57ad-40a2-989e-b56ebd134eaf, ImageAnalysis:androidx.camera.core.ImageAnalysis-1671baa6-a5f8-45f0-9bb7-dab949c97b3b] now OFFLINE for camera 0
Camera: Resetting Capture Session
Camera: releasing session in state OPENED
Camera: Closing camera: 0
Camera: Transitioning camera internal state: OPENED --> CLOSING
Camera: Resetting Capture Session
Camera: releasing session in state CLOSING
UseCaseAttachState: Active and online use case: [] for camera: 0
UseCaseAttachState: Active and online use case: [] for camera: 0
Camera: issue capture request for camera 0
UseCaseAttachState: Active and online use case: [] for camera: 0
CaptureSession: CameraCaptureSession.onClosed()
Camera: CameraDevice.onClosed(): 0
Camera: Transitioning camera internal state: CLOSING --> INITIALIZED
Camera: Use cases [ImageCapture:androidx.camera.core.ImageCapture-0adade8a-60ab-4b3e-8b97-370ff318f73b, Preview:androidx.camera.core.Preview-c3e392d8-57ad-40a2-989e-b56ebd134eaf, ImageAnalysis:androidx.camera.core.ImageAnalysis-1671baa6-a5f8-45f0-9bb7-dab949c97b3b] now ONLINE for camera 0
UseCaseAttachState: Active and online use case: [androidx.camera.core.ImageCapture-0adade8a-60ab-4b3e-8b97-370ff318f73b, androidx.camera.core.Preview-c3e392d8-57ad-40a2-989e-b56ebd134eaf, androidx.camera.core.ImageAnalysis-1671baa6-a5f8-45f0-9bb7-dab949c97b3b] for camera: 0
Camera: Resetting Capture Session
Camera: releasing session in state INITIALIZED
Camera: Transitioning camera internal state: INITIALIZED --> OPENING
Camera: Opening camera: 0
UseCaseAttachState: All use case: [androidx.camera.core.ImageCapture-0adade8a-60ab-4b3e-8b97-370ff318f73b, androidx.camera.core.Preview-c3e392d8-57ad-40a2-989e-b56ebd134eaf, androidx.camera.core.ImageAnalysis-1671baa6-a5f8-45f0-9bb7-dab949c97b3b] for camera: 0
Camera: CameraDevice.onOpened(): 0
Camera: Transitioning camera internal state: OPENING --> OPENED
UseCaseAttachState: All use case: [androidx.camera.core.ImageCapture-0adade8a-60ab-4b3e-8b97-370ff318f73b, androidx.camera.core.Preview-c3e392d8-57ad-40a2-989e-b56ebd134eaf, androidx.camera.core.ImageAnalysis-1671baa6-a5f8-45f0-9bb7-dab949c97b3b] for camera: 0
CaptureSession: Opening capture session.
CameraXBasic: Screen metrics: 1080 x 2220
CameraXBasic: Preview aspect ratio: 1
Camera: Use cases [ImageCapture:androidx.camera.core.ImageCapture-0adade8a-60ab-4b3e-8b97-370ff318f73b, Preview:androidx.camera.core.Preview-c3e392d8-57ad-40a2-989e-b56ebd134eaf, ImageAnalysis:androidx.camera.core.ImageAnalysis-1671baa6-a5f8-45f0-9bb7-dab949c97b3b] now OFFLINE for camera 0
Camera: Resetting Capture Session
Camera: releasing session in state OPENED
Camera: Closing camera: 0
Camera: Transitioning camera internal state: OPENED --> CLOSING
Camera: Resetting Capture Session
Camera: releasing session in state CLOSING
UseCaseAttachState: Active and online use case: [] for camera: 0
Camera: Use cases [ImageCapture:androidx.camera.core.ImageCapture-a07cad15-fa28-4e60-a12b-b7b572df394a, ImageAnalysis:androidx.camera.core.ImageAnalysis-6e588f99-bc88-4afd-b906-17a67e514dbc, Preview:androidx.camera.core.Preview-764071db-da2c-42ff-9b85-002d632d1ac0] now ONLINE for camera 0
UseCaseAttachState: Active and online use case: [] for camera: 0
Camera: Resetting Capture Session
Camera: releasing session in state CLOSING
Camera: Transitioning camera internal state: CLOSING --> REOPENING
Camera: Transitioning camera internal state: REOPENING --> OPENED
UseCaseAttachState: All use case: [androidx.camera.core.ImageCapture-a07cad15-fa28-4e60-a12b-b7b572df394a, androidx.camera.core.ImageAnalysis-6e588f99-bc88-4afd-b906-17a67e514dbc, androidx.camera.core.Preview-764071db-da2c-42ff-9b85-002d632d1ac0] for camera: 0
Camera: Use case ImageCapture:androidx.camera.core.ImageCapture-a07cad15-fa28-4e60-a12b-b7b572df394a ACTIVE for camera 0
UseCaseAttachState: Active and online use case: [androidx.camera.core.ImageCapture-a07cad15-fa28-4e60-a12b-b7b572df394a] for camera: 0
Camera: Use case ImageAnalysis:androidx.camera.core.ImageAnalysis-6e588f99-bc88-4afd-b906-17a67e514dbc ACTIVE for camera 0
UseCaseAttachState: Active and online use case: [androidx.camera.core.ImageCapture-a07cad15-fa28-4e60-a12b-b7b572df394a, androidx.camera.core.ImageAnalysis-6e588f99-bc88-4afd-b906-17a67e514dbc] for camera: 0
Camera: Use case Preview:androidx.camera.core.Preview-764071db-da2c-42ff-9b85-002d632d1ac0 ACTIVE for camera 0
UseCaseAttachState: Active and online use case: [androidx.camera.core.ImageCapture-a07cad15-fa28-4e60-a12b-b7b572df394a, androidx.camera.core.ImageAnalysis-6e588f99-bc88-4afd-b906-17a67e514dbc, androidx.camera.core.Preview-764071db-da2c-42ff-9b85-002d632d1ac0] for camera: 0
UseCaseAttachState: Active and online use case: [androidx.camera.core.ImageCapture-a07cad15-fa28-4e60-a12b-b7b572df394a, androidx.camera.core.ImageAnalysis-6e588f99-bc88-4afd-b906-17a67e514dbc, androidx.camera.core.Preview-764071db-da2c-42ff-9b85-002d632d1ac0] for camera: 0
CaptureSession: CameraCaptureSession.onConfigured() mState=RELEASING
UseCaseAttachState: Active and online use case: [androidx.camera.core.ImageCapture-a07cad15-fa28-4e60-a12b-b7b572df394a, androidx.camera.core.ImageAnalysis-6e588f99-bc88-4afd-b906-17a67e514dbc, androidx.camera.core.Preview-764071db-da2c-42ff-9b85-002d632d1ac0] for camera: 0
UseCaseAttachState: Active and online use case: [androidx.camera.core.ImageCapture-a07cad15-fa28-4e60-a12b-b7b572df394a, androidx.camera.core.ImageAnalysis-6e588f99-bc88-4afd-b906-17a67e514dbc, androidx.camera.core.Preview-764071db-da2c-42ff-9b85-002d632d1ac0] for camera: 0
Camera: issue capture request for camera 0
UseCaseAttachState: Active and online use case: [androidx.camera.core.ImageCapture-a07cad15-fa28-4e60-a12b-b7b572df394a, androidx.camera.core.ImageAnalysis-6e588f99-bc88-4afd-b906-17a67e514dbc, androidx.camera.core.Preview-764071db-da2c-42ff-9b85-002d632d1ac0] for camera: 0
CaptureSession: CameraCaptureSession.onReady() RELEASING
CaptureSession: Opening capture session.
CaptureSession: CameraCaptureSession.onClosed()
CaptureSession: Attempting to send capture request onConfigured
CaptureSession: Issuing request for session.
CaptureSession: Issuing capture request.
CaptureSession: CameraCaptureSession.onConfigured() mState=OPENED
CaptureSession: CameraCaptureSession.onReady() OPENED
CODE FRAGMENTS:
Use an example from
and just change binding to this:
camera = cameraProvider.bindToLifecycle(
this, cameraSelector, preview, imageCapture, imageAnalyzer)
.apply { cameraControl.enableTorch(true) }