diff --git a/mediapipe/java/com/google/mediapipe/components/CameraXPreviewHelper.java b/mediapipe/java/com/google/mediapipe/components/CameraXPreviewHelper.java index 70234170d..0279e5038 100644 --- a/mediapipe/java/com/google/mediapipe/components/CameraXPreviewHelper.java +++ b/mediapipe/java/com/google/mediapipe/components/CameraXPreviewHelper.java @@ -59,6 +59,14 @@ import javax.microedition.khronos.egl.EGLSurface; *
{@link CameraX} connects to the camera and provides video frames. */ public class CameraXPreviewHelper extends CameraHelper { + /** Listener invoked when the camera instance is available. */ + public interface OnCameraBoundListener { + /** + * Called after CameraX has been bound to the lifecycle and the camera instance is available. + */ + public void onCameraBound(Camera camera); + } + /** * Provides an Executor that wraps a single-threaded Handler. * @@ -130,6 +138,10 @@ public class CameraXPreviewHelper extends CameraHelper { // the source is CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN. private int cameraTimestampSource = CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN; + @Nullable private OnCameraBoundListener onCameraBoundListener = null; + + private boolean isLandscapeOrientation = false; + /** * Initializes the camera and sets it up for accessing frames, using the default 1280 * 720 * preview size. @@ -211,7 +223,7 @@ public class CameraXPreviewHelper extends CameraHelper { * @param targetSize a predefined constant {@link #TARGET_SIZE}. If set to {@code null}, the * helper will default to 1280 * 720. */ - private void startCamera( + public void startCamera( Context context, LifecycleOwner lifecycleOwner, CameraFacing cameraFacing, @@ -237,9 +249,11 @@ public class CameraXPreviewHelper extends CameraHelper { // (https://developer.android.com/training/camerax/configuration#specify-resolution): // "Express the resolution Size in the coordinate frame after rotating the supported sizes by // the target rotation." - // Since we only support portrait orientation, we unconditionally transpose width and height. + // Transpose width and height if using portrait orientation. Size rotatedSize = - new Size(/* width= */ targetSize.getHeight(), /* height= */ targetSize.getWidth()); + isLandscapeOrientation + ? new Size(/* width= */ targetSize.getWidth(), /* height= */ targetSize.getHeight()) + : new Size(/* width= */ targetSize.getHeight(), /* height= */ targetSize.getWidth()); cameraProviderFuture.addListener( () -> { @@ -325,15 +339,22 @@ public class CameraXPreviewHelper extends CameraHelper { cameraProvider.unbindAll(); // Bind use case(s) to camera. + final Camera boundCamera; if (imageCaptureBuilder != null) { imageCapture = imageCaptureBuilder.build(); - camera = + boundCamera = cameraProvider.bindToLifecycle( lifecycleOwner, cameraSelector, preview, imageCapture); imageCaptureExecutorService = Executors.newSingleThreadExecutor(); isImageCaptureEnabled = true; } else { - camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview); + boundCamera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview); + } + CameraXPreviewHelper.this.camera = boundCamera; + OnCameraBoundListener listener = onCameraBoundListener; + if (listener != null) { + ContextCompat.getMainExecutor(context) + .execute(() -> listener.onCameraBound(boundCamera)); } }, mainThreadExecutor); @@ -462,6 +483,26 @@ public class CameraXPreviewHelper extends CameraHelper { return frameSize; } + /** + * Sets whether the device is in landscape orientation. + * + *
Must be called before {@link #startCamera}. Portrait orientation is assumed by default. + */ + public void setLandscapeOrientation(boolean landscapeOrientation) { + this.isLandscapeOrientation = landscapeOrientation; + } + + /** + * Sets a listener that will be invoked when CameraX is bound. + * + *
The listener will be invoked on the main thread after the next call to {@link #startCamera}. + * The {@link Camera} instance can be used to get camera info and control the camera (e.g. zoom + * level). + */ + public void setOnCameraBoundListener(@Nullable OnCameraBoundListener listener) { + this.onCameraBoundListener = listener; + } + private void updateCameraCharacteristics() { if (cameraCharacteristics != null) { // Queries camera timestamp source. It should be one of REALTIME or UNKNOWN