diff --git a/.github/workflows/emulator_script.sh b/.github/workflows/emulator_script.sh index 9e1235bb..1810bbba 100755 --- a/.github/workflows/emulator_script.sh +++ b/.github/workflows/emulator_script.sh @@ -1,4 +1,4 @@ #!/usr/bin/env bash adb logcat -c -adb logcat CameraOrchestrator:I CameraEngine:I CameraView:I CameraCallbacks:I CameraIntegrationTest:I MediaEncoderEngine:I MediaEncoder:I AudioMediaEncoder:I VideoMediaEncoder:I TextureMediaEncoder:I VideoRecorder:I SnapshotVideoRecorder:I *:E -v color & +adb logcat CameraOrchestrator:I CameraEngine:I CameraView:I CameraCallbacks:I CameraIntegrationTest:I MediaEncoderEngine:I MediaEncoder:I AudioMediaEncoder:I VideoMediaEncoder:I TextureMediaEncoder:I VideoRecorder:I SnapshotVideoRecorder:I MessageQueue:W *:E -v color & ./gradlew cameraview:connectedCheck \ No newline at end of file diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java index 9ce706b1..964bca65 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java @@ -275,6 +275,8 @@ public class Camera1Engine extends CameraEngine implements throw new RuntimeException("Unknown CameraPreview output class."); } } catch (IOException e) { + // NOTE: when this happens, the next onStopEngine() call hangs on camera.release(), + // Not sure for how long. This causes the destroy() flow to fail the timeout. LOG.e("onStopBind", "Could not release surface", e); } return Tasks.forResult(null); @@ -289,8 +291,16 @@ public class Camera1Engine extends CameraEngine implements mOrchestrator.remove(JOB_FOCUS_END); if (mCamera != null) { try { - // In certain states, this release() call can take up to various seconds. LOG.i("onStopEngine:", "Clean up.", "Releasing camera."); + // TODO just like Camera1Engine, this call can hang (at least on emulators) and make + // the destroy() cleanup fail the timeout, thus leaving camera in a bad state. + // This is anticipated by the exception in onStopBind() (see above). + // 12:29:32.163 1512 1512 E Camera3-Device: Camera 0: clearStreamingRequest: Device has encountered a serious error + // 12:29:32.163 1512 1512 E Camera2-StreamingProcessor: stopStream: Camera 0: Can't clear stream request: Function not implemented (-38) + // 12:29:32.163 1512 1512 E Camera2Client: stopPreviewL: Camera 0: Can't stop streaming: Function not implemented (-38) + // 12:29:32.273 1512 1512 E Camera2-StreamingProcessor: deletePreviewStream: Unable to delete old preview stream: Device or resource busy (-16) + // 12:29:32.274 1512 1512 E Camera2-CallbackProcessor: deleteStream: Unable to delete callback stream: Device or resource busy (-16) + // 12:29:32.274 1512 1512 E Camera3-Device: Camera 0: disconnect: Shutting down in an error state mCamera.release(); LOG.i("onStopEngine:", "Clean up.", "Released camera."); } catch (Exception e) { diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java index d25e59e7..8d0cd13e 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java @@ -417,6 +417,8 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv @Override public void onError(@NonNull CameraDevice camera, int error) { + // TODO provide a better implementation + LOG.e("CameraDevice.StateCallback reported an error:", error); task.trySetException(createCameraException(error)); } }, null); @@ -671,6 +673,13 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv protected Task onStopEngine() { try { LOG.i("onStopEngine:", "Clean up.", "Releasing camera."); + // TODO just like Camera1Engine, this call can hang (at least on emulators) and make + // the destroy() cleanup fail the timeout, thus leaving camera in a bad state. + // 12:33:28.152 2888 5470 I CameraEngine: onStopEngine: Clean up. Releasing camera. + // 12:33:29.476 1384 1555 E audio_hw_generic: pcm_write failed cannot write stream data: I/O error + // 12:33:33.206 1512 3616 E Camera3-Device: Camera 0: waitUntilDrainedLocked: Error waiting for HAL to drain: Connection timed out (-110) + // 12:33:33.242 1512 3616 E CameraDeviceClient: detachDevice: waitUntilDrained failed with code 0xffffff92 + // 12:33:33.243 1512 3616 E Camera3-Device: Camera 0: disconnect: Shutting down in an error state mCamera.close(); LOG.i("onStopEngine:", "Clean up.", "Released camera."); } catch (Exception e) { diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java index e348ac60..8a129744 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java @@ -273,7 +273,8 @@ public abstract class CameraEngine implements final @NonNull Throwable throwable, final boolean fromExceptionHandler) { // 1. If this comes from the exception handler, the thread has crashed. Replace it. - // I'm not sure if this can even be called now that all actions are wrapped into Tasks. + // Most actions are wrapped into Tasks so don't go here, but some callbacks do + // (at least in Camera1, e.g. onError). if (fromExceptionHandler) { LOG.e("EXCEPTION:", "Handler thread is gone. Replacing."); thread.interrupt();