blob: a028ba0fd0e192782a630bc69132ce28abac9ef2 [file] [log] [blame]
/*
* Copyright (C) 2019 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.testing.fakes;
import static androidx.camera.core.DynamicRange.SDR;
import android.util.Range;
import android.util.Rational;
import android.util.Size;
import android.view.Surface;
import androidx.annotation.FloatRange;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.CameraState;
import androidx.camera.core.DynamicRange;
import androidx.camera.core.ExposureState;
import androidx.camera.core.FocusMeteringAction;
import androidx.camera.core.TorchState;
import androidx.camera.core.ZoomState;
import androidx.camera.core.impl.CameraCaptureCallback;
import androidx.camera.core.impl.CameraInfoInternal;
import androidx.camera.core.impl.EncoderProfilesProvider;
import androidx.camera.core.impl.ImageOutputConfig.RotationValue;
import androidx.camera.core.impl.Quirk;
import androidx.camera.core.impl.Quirks;
import androidx.camera.core.impl.Timebase;
import androidx.camera.core.impl.utils.CameraOrientationUtil;
import androidx.camera.core.internal.ImmutableZoomState;
import androidx.core.util.Preconditions;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
/**
* Information for a fake camera.
*
* <p>This camera info can be constructed with fake values.
*/
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
public final class FakeCameraInfoInternal implements CameraInfoInternal {
private static final Set<Range<Integer>> FAKE_FPS_RANGES = Collections.unmodifiableSet(
new HashSet<>(Arrays.asList(
new Range<>(12, 30),
new Range<>(30, 30),
new Range<>(60, 60))
)
);
private static final Set<DynamicRange> DEFAULT_DYNAMIC_RANGES = Collections.singleton(SDR);
private final String mCameraId;
private final int mSensorRotation;
@CameraSelector.LensFacing
private final int mLensFacing;
private final MutableLiveData<Integer> mTorchState = new MutableLiveData<>(TorchState.OFF);
private final MutableLiveData<ZoomState> mZoomLiveData;
private final Map<Integer, List<Size>> mSupportedResolutionMap = new HashMap<>();
private final Map<Integer, List<Size>> mSupportedHighResolutionMap = new HashMap<>();
private MutableLiveData<CameraState> mCameraStateLiveData;
private final Set<DynamicRange> mSupportedDynamicRanges = new HashSet<>(DEFAULT_DYNAMIC_RANGES);
private String mImplementationType = IMPLEMENTATION_TYPE_FAKE;
// Leave uninitialized to support camera-core:1.0.0 dependencies.
// Can be initialized during class init once there are no more pinned dependencies on
// camera-core:1.0.0
private EncoderProfilesProvider mEncoderProfilesProvider;
private boolean mIsPrivateReprocessingSupported = false;
private float mIntrinsicZoomRatio = 1.0F;
private boolean mIsFocusMeteringSupported = false;
private ExposureState mExposureState = new FakeExposureState();
@NonNull
private final List<Quirk> mCameraQuirks = new ArrayList<>();
private Timebase mTimebase = Timebase.UPTIME;
public FakeCameraInfoInternal() {
this(/*sensorRotation=*/ 0, /*lensFacing=*/ CameraSelector.LENS_FACING_BACK);
}
public FakeCameraInfoInternal(@NonNull String cameraId) {
this(cameraId, 0, CameraSelector.LENS_FACING_BACK);
}
public FakeCameraInfoInternal(@NonNull String cameraId,
@CameraSelector.LensFacing int lensFacing) {
this(cameraId, 0, lensFacing);
}
public FakeCameraInfoInternal(int sensorRotation, @CameraSelector.LensFacing int lensFacing) {
this("0", sensorRotation, lensFacing);
}
public FakeCameraInfoInternal(@NonNull String cameraId, int sensorRotation,
@CameraSelector.LensFacing int lensFacing) {
mCameraId = cameraId;
mSensorRotation = sensorRotation;
mLensFacing = lensFacing;
mZoomLiveData = new MutableLiveData<>(ImmutableZoomState.create(1.0f, 4.0f, 1.0f, 0.0f));
}
/**
* Sets the zoom parameter.
*/
public void setZoom(float zoomRatio, float minZoomRatio, float maxZoomRatio, float linearZoom) {
mZoomLiveData.postValue(ImmutableZoomState.create(
zoomRatio, maxZoomRatio, minZoomRatio, linearZoom
));
}
/**
* Sets the exposure compensation parameters.
*/
public void setExposureState(int index, @NonNull Range<Integer> range,
@NonNull Rational step, boolean isSupported) {
mExposureState = new FakeExposureState(index, range, step, isSupported);
}
/**
* Sets the torch state.
*/
public void setTorch(int torchState) {
mTorchState.postValue(torchState);
}
/**
* Sets the return value for {@link #isFocusMeteringSupported(FocusMeteringAction)}.
*/
public void setIsFocusMeteringSupported(boolean supported) {
mIsFocusMeteringSupported = supported;
}
@Override
public int getLensFacing() {
return mLensFacing;
}
@NonNull
@Override
public String getCameraId() {
return mCameraId;
}
@Override
public int getSensorRotationDegrees(@RotationValue int relativeRotation) {
int relativeRotationDegrees =
CameraOrientationUtil.surfaceRotationToDegrees(relativeRotation);
// Currently this assumes that a back-facing camera is always opposite to the screen.
// This may not be the case for all devices, so in the future we may need to handle that
// scenario.
Integer lensFacing = getLensFacing();
boolean isOppositeFacingScreen =
lensFacing != null && (CameraSelector.LENS_FACING_BACK == getLensFacing());
return CameraOrientationUtil.getRelativeImageRotation(
relativeRotationDegrees,
mSensorRotation,
isOppositeFacingScreen);
}
@Override
public int getSensorRotationDegrees() {
return getSensorRotationDegrees(Surface.ROTATION_0);
}
@Override
public boolean hasFlashUnit() {
return true;
}
@NonNull
@Override
public LiveData<Integer> getTorchState() {
return mTorchState;
}
@NonNull
@Override
public LiveData<ZoomState> getZoomState() {
return mZoomLiveData;
}
@NonNull
@Override
public ExposureState getExposureState() {
return mExposureState;
}
@NonNull
@Override
public LiveData<CameraState> getCameraState() {
if (mCameraStateLiveData == null) {
mCameraStateLiveData = new MutableLiveData<>(
CameraState.create(CameraState.Type.CLOSED));
}
return mCameraStateLiveData;
}
@NonNull
@Override
public String getImplementationType() {
return mImplementationType;
}
@NonNull
@Override
public EncoderProfilesProvider getEncoderProfilesProvider() {
return mEncoderProfilesProvider == null ? EncoderProfilesProvider.EMPTY :
mEncoderProfilesProvider;
}
@NonNull
@Override
public Timebase getTimebase() {
return mTimebase;
}
@NonNull
@Override
public List<Size> getSupportedResolutions(int format) {
List<Size> resolutions = mSupportedResolutionMap.get(format);
return resolutions != null ? resolutions : Collections.emptyList();
}
@NonNull
@Override
public List<Size> getSupportedHighResolutions(int format) {
List<Size> resolutions = mSupportedHighResolutionMap.get(format);
return resolutions != null ? resolutions : Collections.emptyList();
}
@NonNull
@Override
public Set<DynamicRange> getSupportedDynamicRanges() {
return mSupportedDynamicRanges;
}
@Override
public void addSessionCaptureCallback(@NonNull Executor executor,
@NonNull CameraCaptureCallback callback) {
throw new UnsupportedOperationException("Not Implemented");
}
@Override
public void removeSessionCaptureCallback(@NonNull CameraCaptureCallback callback) {
throw new UnsupportedOperationException("Not Implemented");
}
@NonNull
@Override
public Quirks getCameraQuirks() {
return new Quirks(mCameraQuirks);
}
@NonNull
@Override
public Set<Range<Integer>> getSupportedFrameRateRanges() {
return FAKE_FPS_RANGES;
}
@Override
public boolean isFocusMeteringSupported(@NonNull FocusMeteringAction action) {
return mIsFocusMeteringSupported;
}
@Override
public boolean isZslSupported() {
return false;
}
@Override
public boolean isPrivateReprocessingSupported() {
return mIsPrivateReprocessingSupported;
}
@FloatRange(from = 0, fromInclusive = false)
@Override
public float getIntrinsicZoomRatio() {
return mIntrinsicZoomRatio;
}
@Override
public boolean isPreviewStabilizationSupported() {
return false;
}
@Override
public boolean isVideoStabilizationSupported() {
return false;
}
/** Adds a quirk to the list of this camera's quirks. */
@SuppressWarnings("unused")
public void addCameraQuirk(@NonNull final Quirk quirk) {
mCameraQuirks.add(quirk);
}
/**
* Set the implementation type for testing
*/
public void setImplementationType(@NonNull @ImplementationType String implementationType) {
mImplementationType = implementationType;
}
/** Set the EncoderProfilesProvider for testing */
public void setEncoderProfilesProvider(
@NonNull EncoderProfilesProvider encoderProfilesProvider) {
mEncoderProfilesProvider = Preconditions.checkNotNull(encoderProfilesProvider);
}
/** Set the timebase for testing */
public void setTimebase(@NonNull Timebase timebase) {
mTimebase = timebase;
}
/** Set the supported resolutions for testing */
public void setSupportedResolutions(int format, @NonNull List<Size> resolutions) {
mSupportedResolutionMap.put(format, resolutions);
}
/** Set the supported high resolutions for testing */
public void setSupportedHighResolutions(int format, @NonNull List<Size> resolutions) {
mSupportedHighResolutionMap.put(format, resolutions);
}
/** Set the isPrivateReprocessingSupported flag for testing */
public void setPrivateReprocessingSupported(boolean supported) {
mIsPrivateReprocessingSupported = supported;
}
/** Adds a available view angle for testing. */
public void setIntrinsicZoomRatio(float zoomRatio) {
mIntrinsicZoomRatio = zoomRatio;
}
/** Set the supported dynamic ranges for testing */
public void setSupportedDynamicRanges(@NonNull Set<DynamicRange> dynamicRanges) {
mSupportedDynamicRanges.clear();
mSupportedDynamicRanges.addAll(dynamicRanges);
}
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
static final class FakeExposureState implements ExposureState {
private int mIndex = 0;
private Range<Integer> mRange = new Range<>(0, 0);
private Rational mStep = Rational.ZERO;
private boolean mIsSupported = true;
FakeExposureState() {
}
FakeExposureState(int index, Range<Integer> range,
Rational step, boolean isSupported) {
mIndex = index;
mRange = range;
mStep = step;
mIsSupported = isSupported;
}
@Override
public int getExposureCompensationIndex() {
return mIndex;
}
@NonNull
@Override
public Range<Integer> getExposureCompensationRange() {
return mRange;
}
@NonNull
@Override
public Rational getExposureCompensationStep() {
return mStep;
}
@Override
public boolean isExposureCompensationSupported() {
return mIsSupported;
}
}
}