Merge "[Multi-Cam] Add setMirrorMode() for Preview" into androidx-main
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
index 8703b53..162e92b 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CaptureRequest
+import android.hardware.camera2.params.OutputConfiguration
import android.hardware.camera2.params.SessionConfiguration.SESSION_HIGH_SPEED
import android.hardware.camera2.params.SessionConfiguration.SESSION_REGULAR
import android.media.MediaCodec
@@ -54,6 +55,7 @@
import androidx.camera.camera2.pipe.integration.interop.Camera2CameraControl
import androidx.camera.camera2.pipe.integration.interop.ExperimentalCamera2Interop
import androidx.camera.core.DynamicRange
+import androidx.camera.core.MirrorMode
import androidx.camera.core.UseCase
import androidx.camera.core.impl.CameraControlInternal
import androidx.camera.core.impl.CameraInfoInternal
@@ -702,6 +704,7 @@
val deferrableSurface = outputConfig.surface
val physicalCameraId =
physicalCameraIdForAllStreams ?: outputConfig.physicalCameraId
+ val mirrorMode = outputConfig.mirrorMode
val outputStreamConfig = OutputStream.Config.create(
size = deferrableSurface.prescribedSize,
format = StreamFormat(deferrableSurface.prescribedStreamFormat),
@@ -710,6 +713,15 @@
} else {
CameraId.fromCamera2Id(physicalCameraId)
},
+ // No need to map MIRROR_MODE_ON_FRONT_ONLY to MIRROR_MODE_AUTO
+ // since its default value in framework
+ mirrorMode = when (mirrorMode) {
+ MirrorMode.MIRROR_MODE_OFF -> OutputStream.MirrorMode(
+ OutputConfiguration.MIRROR_MODE_NONE)
+ MirrorMode.MIRROR_MODE_ON -> OutputStream.MirrorMode(
+ OutputConfiguration.MIRROR_MODE_H)
+ else -> null
+ },
streamUseCase = getStreamUseCase(
deferrableSurface,
sessionConfigAdapter.surfaceToStreamUseCaseMap
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java
index 5be543a..b0e3164 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/CaptureSession.java
@@ -23,6 +23,7 @@
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.DynamicRangeProfiles;
+import android.hardware.camera2.params.OutputConfiguration;
import android.os.Build;
import android.view.Surface;
@@ -44,6 +45,7 @@
import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
import androidx.camera.core.DynamicRange;
import androidx.camera.core.Logger;
+import androidx.camera.core.MirrorMode;
import androidx.camera.core.impl.CameraCaptureCallback;
import androidx.camera.core.impl.CaptureConfig;
import androidx.camera.core.impl.DeferrableSurface;
@@ -391,6 +393,14 @@
outputConfig.getPhysicalCameraId());
}
+ // No need to map MIRROR_MODE_ON_FRONT_ONLY to MIRROR_MODE_AUTO
+ // since its default value in framework
+ if (outputConfig.getMirrorMode() == MirrorMode.MIRROR_MODE_OFF) {
+ outputConfiguration.setMirrorMode(OutputConfiguration.MIRROR_MODE_NONE);
+ } else if (outputConfig.getMirrorMode() == MirrorMode.MIRROR_MODE_ON) {
+ outputConfiguration.setMirrorMode(OutputConfiguration.MIRROR_MODE_H);
+ }
+
if (!outputConfig.getSharedSurfaces().isEmpty()) {
outputConfiguration.enableSurfaceSharing();
for (DeferrableSurface sharedDeferSurface : outputConfig.getSharedSurfaces()) {
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/params/OutputConfigurationCompat.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/params/OutputConfigurationCompat.java
index 9503797..a445d3f 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/params/OutputConfigurationCompat.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/params/OutputConfigurationCompat.java
@@ -200,6 +200,24 @@
}
/**
+ * Returns mirror mode of {@link OutputConfiguration}.
+ * @return {@link OutputConfiguration#getMirrorMode()}
+ * @see OutputConfiguration#getMirrorMode()
+ */
+ public int getMirrorMode() {
+ return mImpl.getMirrorMode();
+ }
+
+ /**
+ * Sets mirror mode of {@link OutputConfiguration}.
+ * @param mirrorMode mirror mode to set for {@link OutputConfiguration}.
+ * @see OutputConfiguration#setMirrorMode(int)
+ */
+ public void setMirrorMode(int mirrorMode) {
+ mImpl.setMirrorMode(mirrorMode);
+ }
+
+ /**
* Retrieve the physical camera ID set by {@link #setPhysicalCameraId(String)}.
*
*/
@@ -488,6 +506,10 @@
interface OutputConfigurationCompatImpl {
void enableSurfaceSharing();
+ int getMirrorMode();
+
+ void setMirrorMode(int mirrorMode);
+
@Nullable
String getPhysicalCameraId();
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/params/OutputConfigurationCompatApi33Impl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/params/OutputConfigurationCompatApi33Impl.java
index a2d905b..bc8829f 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/params/OutputConfigurationCompatApi33Impl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/params/OutputConfigurationCompatApi33Impl.java
@@ -48,6 +48,16 @@
}
@Override
+ public int getMirrorMode() {
+ return ((OutputConfiguration) getOutputConfiguration()).getMirrorMode();
+ }
+
+ @Override
+ public void setMirrorMode(int mirrorMode) {
+ ((OutputConfiguration) getOutputConfiguration()).setMirrorMode(mirrorMode);
+ }
+
+ @Override
public long getDynamicRangeProfile() {
return ((OutputConfiguration) getOutputConfiguration()).getDynamicRangeProfile();
}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/params/OutputConfigurationCompatBaseImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/params/OutputConfigurationCompatBaseImpl.java
index ef70b78..07ee1df 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/params/OutputConfigurationCompatBaseImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/params/OutputConfigurationCompatBaseImpl.java
@@ -19,6 +19,7 @@
import android.annotation.SuppressLint;
import android.graphics.ImageFormat;
import android.hardware.camera2.params.DynamicRangeProfiles;
+import android.hardware.camera2.params.OutputConfiguration;
import android.os.Build;
import android.util.Size;
import android.view.Surface;
@@ -82,6 +83,16 @@
return ((OutputConfigurationParamsApi21) mObject).mPhysicalCameraId;
}
+ @Override
+ public void setMirrorMode(int mirrorMode) {
+ //No-op
+ }
+
+ @Override
+ public int getMirrorMode() {
+ return OutputConfiguration.MIRROR_MODE_AUTO;
+ }
+
/**
* Set stream use case for this OutputConfiguration.
*/
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/params/OutputConfigurationCompatTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/params/OutputConfigurationCompatTest.java
index 7b2ad2a..4dca498 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/params/OutputConfigurationCompatTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/params/OutputConfigurationCompatTest.java
@@ -16,6 +16,9 @@
package androidx.camera.camera2.internal.compat.params;
+import static android.hardware.camera2.params.OutputConfiguration.MIRROR_MODE_H;
+import static android.hardware.camera2.params.OutputConfiguration.MIRROR_MODE_NONE;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
@@ -240,6 +243,22 @@
}
@Test
+ @Config(minSdk = 33)
+ public void canSetMirrorMode() {
+ OutputConfiguration outputConfig = mock(OutputConfiguration.class);
+
+ OutputConfigurationCompat outputConfigCompat = OutputConfigurationCompat.wrap(outputConfig);
+
+ outputConfigCompat.setMirrorMode(MIRROR_MODE_NONE);
+
+ verify(outputConfig, times(1)).setMirrorMode(MIRROR_MODE_NONE);
+
+ outputConfigCompat.setMirrorMode(MIRROR_MODE_H);
+
+ verify(outputConfig, times(1)).setMirrorMode(MIRROR_MODE_H);
+ }
+
+ @Test
public void canSetDynamicRangeProfile() {
OutputConfigurationCompat outputConfigCompat =
new OutputConfigurationCompat(mock(Surface.class));
diff --git a/camera/camera-core/api/1.4.0-beta01.txt b/camera/camera-core/api/1.4.0-beta01.txt
index c84401f..058ffa8 100644
--- a/camera/camera-core/api/1.4.0-beta01.txt
+++ b/camera/camera-core/api/1.4.0-beta01.txt
@@ -212,6 +212,9 @@
@SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalLensFacing {
}
+ @SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalMirrorMode {
+ }
+
@SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalRetryPolicy {
}
@@ -507,6 +510,7 @@
ctor public Preview.Builder();
method public androidx.camera.core.Preview build();
method public androidx.camera.core.Preview.Builder setDynamicRange(androidx.camera.core.DynamicRange);
+ method @SuppressCompatibility @androidx.camera.core.ExperimentalMirrorMode public androidx.camera.core.Preview.Builder setMirrorMode(int);
method public androidx.camera.core.Preview.Builder setPreviewStabilizationEnabled(boolean);
method public androidx.camera.core.Preview.Builder setResolutionSelector(androidx.camera.core.resolutionselector.ResolutionSelector);
method @Deprecated public androidx.camera.core.Preview.Builder setTargetAspectRatio(int);
diff --git a/camera/camera-core/api/current.txt b/camera/camera-core/api/current.txt
index c84401f..058ffa8 100644
--- a/camera/camera-core/api/current.txt
+++ b/camera/camera-core/api/current.txt
@@ -212,6 +212,9 @@
@SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalLensFacing {
}
+ @SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalMirrorMode {
+ }
+
@SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalRetryPolicy {
}
@@ -507,6 +510,7 @@
ctor public Preview.Builder();
method public androidx.camera.core.Preview build();
method public androidx.camera.core.Preview.Builder setDynamicRange(androidx.camera.core.DynamicRange);
+ method @SuppressCompatibility @androidx.camera.core.ExperimentalMirrorMode public androidx.camera.core.Preview.Builder setMirrorMode(int);
method public androidx.camera.core.Preview.Builder setPreviewStabilizationEnabled(boolean);
method public androidx.camera.core.Preview.Builder setResolutionSelector(androidx.camera.core.resolutionselector.ResolutionSelector);
method @Deprecated public androidx.camera.core.Preview.Builder setTargetAspectRatio(int);
diff --git a/camera/camera-core/api/restricted_1.4.0-beta01.txt b/camera/camera-core/api/restricted_1.4.0-beta01.txt
index c84401f..058ffa8 100644
--- a/camera/camera-core/api/restricted_1.4.0-beta01.txt
+++ b/camera/camera-core/api/restricted_1.4.0-beta01.txt
@@ -212,6 +212,9 @@
@SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalLensFacing {
}
+ @SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalMirrorMode {
+ }
+
@SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalRetryPolicy {
}
@@ -507,6 +510,7 @@
ctor public Preview.Builder();
method public androidx.camera.core.Preview build();
method public androidx.camera.core.Preview.Builder setDynamicRange(androidx.camera.core.DynamicRange);
+ method @SuppressCompatibility @androidx.camera.core.ExperimentalMirrorMode public androidx.camera.core.Preview.Builder setMirrorMode(int);
method public androidx.camera.core.Preview.Builder setPreviewStabilizationEnabled(boolean);
method public androidx.camera.core.Preview.Builder setResolutionSelector(androidx.camera.core.resolutionselector.ResolutionSelector);
method @Deprecated public androidx.camera.core.Preview.Builder setTargetAspectRatio(int);
diff --git a/camera/camera-core/api/restricted_current.txt b/camera/camera-core/api/restricted_current.txt
index c84401f..058ffa8 100644
--- a/camera/camera-core/api/restricted_current.txt
+++ b/camera/camera-core/api/restricted_current.txt
@@ -212,6 +212,9 @@
@SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalLensFacing {
}
+ @SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalMirrorMode {
+ }
+
@SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalRetryPolicy {
}
@@ -507,6 +510,7 @@
ctor public Preview.Builder();
method public androidx.camera.core.Preview build();
method public androidx.camera.core.Preview.Builder setDynamicRange(androidx.camera.core.DynamicRange);
+ method @SuppressCompatibility @androidx.camera.core.ExperimentalMirrorMode public androidx.camera.core.Preview.Builder setMirrorMode(int);
method public androidx.camera.core.Preview.Builder setPreviewStabilizationEnabled(boolean);
method public androidx.camera.core.Preview.Builder setResolutionSelector(androidx.camera.core.resolutionselector.ResolutionSelector);
method @Deprecated public androidx.camera.core.Preview.Builder setTargetAspectRatio(int);
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/UseCaseTest.kt b/camera/camera-core/src/androidTest/java/androidx/camera/core/UseCaseTest.kt
index f288885..e8a9867 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/UseCaseTest.kt
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/UseCaseTest.kt
@@ -25,6 +25,7 @@
import androidx.camera.core.MirrorMode.MIRROR_MODE_OFF
import androidx.camera.core.MirrorMode.MIRROR_MODE_ON
import androidx.camera.core.MirrorMode.MIRROR_MODE_ON_FRONT_ONLY
+import androidx.camera.core.MirrorMode.MIRROR_MODE_UNSPECIFIED
import androidx.camera.core.UseCase.snapToSurfaceRotation
import androidx.camera.core.concurrent.CameraCoordinator
import androidx.camera.core.impl.Config
@@ -265,7 +266,7 @@
@Test
fun defaultMirrorModeIsOff() {
val fakeUseCase = createFakeUseCase()
- assertThat(fakeUseCase.mirrorModeInternal).isEqualTo(MIRROR_MODE_OFF)
+ assertThat(fakeUseCase.mirrorModeInternal).isEqualTo(MIRROR_MODE_UNSPECIFIED)
}
@Test
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ExperimentalMirrorMode.java b/camera/camera-core/src/main/java/androidx/camera/core/ExperimentalMirrorMode.java
new file mode 100644
index 0000000..5e841b7
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ExperimentalMirrorMode.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.core;
+
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import androidx.annotation.RequiresOptIn;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Denotes that the annotated API is designed to be experimental for {@link MirrorMode}
+ */
+@Retention(CLASS)
+@RequiresOptIn
+public @interface ExperimentalMirrorMode {
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
index 798ba62..2cf9086 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
@@ -423,7 +423,10 @@
sessionConfigBuilder.setExpectedFrameRateRange(streamSpec.getExpectedFrameRateRange());
- sessionConfigBuilder.addSurface(mDeferrableSurface, streamSpec.getDynamicRange(), null);
+ sessionConfigBuilder.addSurface(mDeferrableSurface,
+ streamSpec.getDynamicRange(),
+ null,
+ MirrorMode.MIRROR_MODE_UNSPECIFIED);
sessionConfigBuilder.addErrorListener((sessionConfig, error) -> {
clearPipeline();
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/MirrorMode.java b/camera/camera-core/src/main/java/androidx/camera/core/MirrorMode.java
index 49af138..cb2e328 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/MirrorMode.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/MirrorMode.java
@@ -42,12 +42,16 @@
*/
public static final int MIRROR_MODE_ON_FRONT_ONLY = 2;
+ /** The mirror mode is not specified by the user **/
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public static final int MIRROR_MODE_UNSPECIFIED = -1;
+
private MirrorMode() {
}
/**
*/
- @IntDef({MIRROR_MODE_OFF, MIRROR_MODE_ON, MIRROR_MODE_ON_FRONT_ONLY})
+ @IntDef({MIRROR_MODE_OFF, MIRROR_MODE_ON, MIRROR_MODE_ON_FRONT_ONLY, MIRROR_MODE_UNSPECIFIED})
@Retention(RetentionPolicy.SOURCE)
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public @interface Mirror {
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
index 2163ecb..04f330d 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
@@ -18,6 +18,7 @@
import static androidx.camera.core.CameraEffect.PREVIEW;
import static androidx.camera.core.MirrorMode.MIRROR_MODE_ON_FRONT_ONLY;
+import static androidx.camera.core.MirrorMode.MIRROR_MODE_UNSPECIFIED;
import static androidx.camera.core.impl.ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE;
import static androidx.camera.core.impl.ImageInputConfig.OPTION_INPUT_DYNAMIC_RANGE;
import static androidx.camera.core.impl.ImageInputConfig.OPTION_INPUT_FORMAT;
@@ -54,6 +55,7 @@
import android.graphics.SurfaceTexture;
import android.media.ImageReader;
import android.media.MediaCodec;
+import android.os.Build;
import android.util.Pair;
import android.util.Range;
import android.util.Size;
@@ -332,7 +334,8 @@
if (mSurfaceProvider != null) {
sessionConfigBuilder.addSurface(mSessionDeferrableSurface,
streamSpec.getDynamicRange(),
- getPhysicalCameraId());
+ getPhysicalCameraId(),
+ getMirrorModeInternal());
}
sessionConfigBuilder.addErrorListener((sessionConfig, error) -> {
@@ -846,7 +849,11 @@
setCaptureType(UseCaseConfigFactory.CaptureType.PREVIEW);
setTargetClass(Preview.class);
- mutableConfig.insertOption(OPTION_MIRROR_MODE, Defaults.DEFAULT_MIRROR_MODE);
+
+ if (mutableConfig.retrieveOption(
+ OPTION_MIRROR_MODE, MIRROR_MODE_UNSPECIFIED) == MIRROR_MODE_UNSPECIFIED) {
+ mutableConfig.insertOption(OPTION_MIRROR_MODE, Defaults.DEFAULT_MIRROR_MODE);
+ }
}
/**
@@ -1024,13 +1031,30 @@
}
/**
- * setMirrorMode is not supported on Preview.
+ * Sets the mirror mode.
+ *
+ * <p>Valid values include: {@link MirrorMode#MIRROR_MODE_OFF},
+ * {@link MirrorMode#MIRROR_MODE_ON} and {@link MirrorMode#MIRROR_MODE_ON_FRONT_ONLY}.
+ * If not set, it defaults to {@link MirrorMode#MIRROR_MODE_ON_FRONT_ONLY}.
+ *
+ * <p>For API 33 and above, it will change the mirroring behavior for Preview use case.
+ * It is calling
+ * {@link android.hardware.camera2.params.OutputConfiguration#setMirrorMode(int)}.
+ *
+ * <p> For API 32 and below, it will be no-op.
+ *
+ * @param mirrorMode The mirror mode of the intended target.
+ * @return The current Builder.
+ * @see android.hardware.camera2.params.OutputConfiguration#setMirrorMode(int)
*/
- @RestrictTo(Scope.LIBRARY_GROUP)
+ @ExperimentalMirrorMode
@NonNull
@Override
public Builder setMirrorMode(@MirrorMode.Mirror int mirrorMode) {
- throw new UnsupportedOperationException("setMirrorMode is not supported.");
+ if (Build.VERSION.SDK_INT >= 33) {
+ getMutableConfig().insertOption(OPTION_MIRROR_MODE, mirrorMode);
+ }
+ return this;
}
/**
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java b/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java
index eb703aa..5918040 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java
@@ -19,6 +19,7 @@
import static androidx.camera.core.MirrorMode.MIRROR_MODE_OFF;
import static androidx.camera.core.MirrorMode.MIRROR_MODE_ON;
import static androidx.camera.core.MirrorMode.MIRROR_MODE_ON_FRONT_ONLY;
+import static androidx.camera.core.MirrorMode.MIRROR_MODE_UNSPECIFIED;
import static androidx.camera.core.impl.ImageOutputConfig.OPTION_MAX_RESOLUTION;
import static androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR;
import static androidx.camera.core.impl.ImageOutputConfig.OPTION_TARGET_ASPECT_RATIO;
@@ -442,7 +443,7 @@
@RestrictTo(Scope.LIBRARY_GROUP)
@MirrorMode.Mirror
protected int getMirrorModeInternal() {
- return ((ImageOutputConfig) mCurrentConfig).getMirrorMode(MIRROR_MODE_OFF);
+ return ((ImageOutputConfig) mCurrentConfig).getMirrorMode(MIRROR_MODE_UNSPECIFIED);
}
/**
@@ -453,6 +454,7 @@
public boolean isMirroringRequired(@NonNull CameraInternal camera) {
int mirrorMode = getMirrorModeInternal();
switch (mirrorMode) {
+ case MIRROR_MODE_UNSPECIFIED:
case MIRROR_MODE_OFF:
return false;
case MIRROR_MODE_ON:
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/SessionConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/SessionConfig.java
index 414ddb0..6ea58a0 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/SessionConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/SessionConfig.java
@@ -30,6 +30,7 @@
import androidx.annotation.RequiresApi;
import androidx.camera.core.DynamicRange;
import androidx.camera.core.Logger;
+import androidx.camera.core.MirrorMode;
import androidx.camera.core.impl.stabilization.StabilizationMode;
import androidx.camera.core.internal.compat.workaround.SurfaceSorter;
@@ -109,6 +110,14 @@
public abstract String getPhysicalCameraId();
/**
+ * Returns the mirror mode.
+ *
+ * @return {@link MirrorMode}
+ */
+ @MirrorMode.Mirror
+ public abstract int getMirrorMode();
+
+ /**
* Returns the surface group ID. Default value is {@link #SURFACE_GROUP_ID_NONE} meaning
* it doesn't belong to any surface group. A surface group ID is used to identify which
* surface group this output surface belongs to. Output streams with the same
@@ -137,6 +146,7 @@
.setSurface(surface)
.setSharedSurfaces(Collections.emptyList())
.setPhysicalCameraId(null)
+ .setMirrorMode(MirrorMode.MIRROR_MODE_UNSPECIFIED)
.setSurfaceGroupId(SURFACE_GROUP_ID_NONE)
.setDynamicRange(DynamicRange.SDR);
}
@@ -168,6 +178,14 @@
public abstract Builder setPhysicalCameraId(@Nullable String cameraId);
/**
+ * Sets the mirror mode. It specifies mirroring mode for
+ * {@link android.hardware.camera2.params.OutputConfiguration}.
+ * @see android.hardware.camera2.params.OutputConfiguration#setMirrorMode(int)
+ */
+ @NonNull
+ public abstract Builder setMirrorMode(@MirrorMode.Mirror int mirrorMode);
+
+ /**
* Sets the surface group ID. A surface group ID is used to identify which surface group
* this output surface belongs to. Output streams with the same non-negative group ID
* won't receive the camera output simultaneously therefore it could be used to reduce
@@ -643,11 +661,13 @@
* Add a surface to the set that the session repeatedly writes data to.
*
* <p>The dynamic range of this surface will default to {@link DynamicRange#SDR}. To
- * manually set the dynamic range, use {@link #addSurface(DeferrableSurface, DynamicRange)}.
+ * manually set the dynamic range, use
+ * {@link #addSurface(DeferrableSurface, DynamicRange, String, int)}.
*/
@NonNull
public Builder addSurface(@NonNull DeferrableSurface surface) {
- return addSurface(surface, DynamicRange.SDR, null);
+ return addSurface(surface, DynamicRange.SDR, null,
+ MirrorMode.MIRROR_MODE_UNSPECIFIED);
}
/**
@@ -657,10 +677,12 @@
@NonNull
public Builder addSurface(@NonNull DeferrableSurface surface,
@NonNull DynamicRange dynamicRange,
- @Nullable String physicalCameraId) {
+ @Nullable String physicalCameraId,
+ @MirrorMode.Mirror int mirrorMode) {
OutputConfig outputConfig = OutputConfig.builder(surface)
.setPhysicalCameraId(physicalCameraId)
.setDynamicRange(dynamicRange)
+ .setMirrorMode(mirrorMode)
.build();
mOutputConfigs.add(outputConfig);
mCaptureConfigBuilder.addSurface(surface);
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharing.java b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharing.java
index d690404..b98833a 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharing.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/streamsharing/StreamSharing.java
@@ -45,6 +45,7 @@
import androidx.annotation.VisibleForTesting;
import androidx.camera.core.CameraEffect;
import androidx.camera.core.ImageCapture;
+import androidx.camera.core.MirrorMode;
import androidx.camera.core.UseCase;
import androidx.camera.core.impl.CameraInfoInternal;
import androidx.camera.core.impl.CameraInternal;
@@ -277,7 +278,10 @@
propagateChildrenCamera2Interop(streamSpec.getResolution(), builder);
- builder.addSurface(mCameraEdge.getDeferrableSurface(), streamSpec.getDynamicRange(), null);
+ builder.addSurface(mCameraEdge.getDeferrableSurface(),
+ streamSpec.getDynamicRange(),
+ null,
+ MirrorMode.MIRROR_MODE_UNSPECIFIED);
builder.addRepeatingCameraCaptureCallback(
mVirtualCameraAdapter.getParentMetadataCallback());
if (streamSpec.getImplementationOptions() != null) {
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java b/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
index 0469f62..380be3b 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
@@ -16,8 +16,8 @@
package androidx.camera.core;
-import static androidx.camera.core.MirrorMode.MIRROR_MODE_OFF;
import static androidx.camera.core.MirrorMode.MIRROR_MODE_ON_FRONT_ONLY;
+import static androidx.camera.core.MirrorMode.MIRROR_MODE_UNSPECIFIED;
import static com.google.common.truth.Truth.assertThat;
@@ -172,7 +172,7 @@
@Test
public void defaultMirrorModeIsOff() {
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder().build();
- assertThat(imageAnalysis.getMirrorModeInternal()).isEqualTo(MIRROR_MODE_OFF);
+ assertThat(imageAnalysis.getMirrorModeInternal()).isEqualTo(MIRROR_MODE_UNSPECIFIED);
}
@Test(expected = UnsupportedOperationException.class)
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt
index bc1b60a..affae0b 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ImageCaptureTest.kt
@@ -33,8 +33,8 @@
import androidx.camera.core.CameraEffect.VIDEO_CAPTURE
import androidx.camera.core.ImageCapture.OUTPUT_FORMAT_JPEG
import androidx.camera.core.ImageCapture.OUTPUT_FORMAT_JPEG_ULTRA_HDR
-import androidx.camera.core.MirrorMode.MIRROR_MODE_OFF
import androidx.camera.core.MirrorMode.MIRROR_MODE_ON_FRONT_ONLY
+import androidx.camera.core.MirrorMode.MIRROR_MODE_UNSPECIFIED
import androidx.camera.core.impl.CameraFactory
import androidx.camera.core.impl.CaptureConfig
import androidx.camera.core.impl.ImageCaptureConfig
@@ -211,7 +211,7 @@
@Test
fun defaultMirrorModeIsOff() {
val imageCapture = ImageCapture.Builder().build()
- assertThat(imageCapture.mirrorModeInternal).isEqualTo(MIRROR_MODE_OFF)
+ assertThat(imageCapture.mirrorModeInternal).isEqualTo(MIRROR_MODE_UNSPECIFIED)
}
@Test(expected = UnsupportedOperationException::class)
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/PreviewTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/PreviewTest.kt
index 7ab1e17..f4e6f6b 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/PreviewTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/PreviewTest.kt
@@ -34,6 +34,8 @@
import androidx.camera.core.CameraEffect.PREVIEW
import androidx.camera.core.CameraEffect.VIDEO_CAPTURE
import androidx.camera.core.CameraSelector.LENS_FACING_FRONT
+import androidx.camera.core.MirrorMode.MIRROR_MODE_OFF
+import androidx.camera.core.MirrorMode.MIRROR_MODE_ON
import androidx.camera.core.MirrorMode.MIRROR_MODE_ON_FRONT_ONLY
import androidx.camera.core.Preview.SurfaceProvider
import androidx.camera.core.SurfaceRequest.TransformationInfo
@@ -279,9 +281,34 @@
assertThat(preview.mirrorModeInternal).isEqualTo(MIRROR_MODE_ON_FRONT_ONLY)
}
- @Test(expected = UnsupportedOperationException::class)
- fun setMirrorMode_throwException() {
- Preview.Builder().setMirrorMode(MIRROR_MODE_ON_FRONT_ONLY)
+ @Config(minSdk = 33)
+ @Test
+ fun setMirrorMode_OnFrontOnly() {
+ val preview = createPreview()
+ assertThat(preview.mirrorModeInternal).isEqualTo(MIRROR_MODE_ON_FRONT_ONLY)
+
+ val sessionConfig = preview.sessionConfig
+ assertThat(sessionConfig.outputConfigs[0].mirrorMode).isEqualTo(MIRROR_MODE_ON_FRONT_ONLY)
+ }
+
+ @Config(minSdk = 33)
+ @Test
+ fun setMirrorMode_On() {
+ val preview = createPreview(mirrorMode = MIRROR_MODE_ON)
+ assertThat(preview.mirrorModeInternal).isEqualTo(MIRROR_MODE_ON)
+
+ val sessionConfig = preview.sessionConfig
+ assertThat(sessionConfig.outputConfigs[0].mirrorMode).isEqualTo(MIRROR_MODE_ON)
+ }
+
+ @Config(minSdk = 33)
+ @Test
+ fun setMirrorMode_Off() {
+ val preview = createPreview(mirrorMode = MIRROR_MODE_OFF)
+ assertThat(preview.mirrorModeInternal).isEqualTo(MIRROR_MODE_OFF)
+
+ val sessionConfig = preview.sessionConfig
+ assertThat(sessionConfig.outputConfigs[0].mirrorMode).isEqualTo(MIRROR_MODE_OFF)
}
@Test
@@ -833,9 +860,11 @@
camera: FakeCamera = backCamera,
targetRotation: Int = ROTATION_90,
surfaceProvider: SurfaceProvider = SurfaceProvider {
- }
+ },
+ mirrorMode: Int = MirrorMode.MIRROR_MODE_UNSPECIFIED
): Preview {
previewToDetach = Preview.Builder()
+ .setMirrorMode(mirrorMode)
.setTargetRotation(targetRotation)
.build()
previewToDetach.effect = effect
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java b/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java
index 95971f2..dcdc96a 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java
@@ -356,7 +356,11 @@
*/
@MirrorMode.Mirror
public int getMirrorMode() {
- return getMirrorModeInternal();
+ int mirrorMode = getMirrorModeInternal();
+ if (mirrorMode == MirrorMode.MIRROR_MODE_UNSPECIFIED) {
+ return MirrorMode.MIRROR_MODE_OFF;
+ }
+ return mirrorMode;
}
@SuppressWarnings("unchecked")
@@ -903,7 +907,10 @@
DynamicRange dynamicRange = streamSpec.getDynamicRange();
if (!isStreamError && mDeferrableSurface != null) {
if (isStreamActive) {
- sessionConfigBuilder.addSurface(mDeferrableSurface, dynamicRange, null);
+ sessionConfigBuilder.addSurface(mDeferrableSurface,
+ dynamicRange,
+ null,
+ MirrorMode.MIRROR_MODE_UNSPECIFIED);
} else {
sessionConfigBuilder.addNonRepeatingSurface(mDeferrableSurface, dynamicRange);
}
diff --git a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/ConcurrentCameraActivity.java b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/ConcurrentCameraActivity.java
index e64e886..5421e2f 100644
--- a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/ConcurrentCameraActivity.java
+++ b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/ConcurrentCameraActivity.java
@@ -46,8 +46,10 @@
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ConcurrentCamera;
import androidx.camera.core.ConcurrentCamera.SingleCameraConfig;
+import androidx.camera.core.ExperimentalMirrorMode;
import androidx.camera.core.FocusMeteringAction;
import androidx.camera.core.MeteringPoint;
+import androidx.camera.core.MirrorMode;
import androidx.camera.core.Preview;
import androidx.camera.core.UseCaseGroup;
import androidx.camera.lifecycle.ExperimentalCameraProviderConfiguration;
@@ -274,7 +276,7 @@
}
@SuppressLint("NullAnnotationGroup")
- @OptIn(markerClass = {ExperimentalCamera2Interop.class,
+ @OptIn(markerClass = {ExperimentalCamera2Interop.class, ExperimentalMirrorMode.class,
androidx.camera.camera2.pipe.integration.interop.ExperimentalCamera2Interop.class})
private void bindToLifecycleForConcurrentCamera(
@NonNull ProcessCameraProvider cameraProvider,
@@ -329,6 +331,7 @@
.build(),
lifecycleOwner);
Preview previewBack = new Preview.Builder()
+ .setMirrorMode(MirrorMode.MIRROR_MODE_OFF)
.build();
previewBack.setSurfaceProvider(backPreviewView.getSurfaceProvider());
SingleCameraConfig secondary = new SingleCameraConfig(