/*
* Copyright (C) 2014 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 com.android.ex.camera2.portability;
import com.android.ex.camera2.portability.debug.Log;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.TreeSet;
/**
* This class holds all the static information of a camera's capabilities.
*
* The design of this class is thread-safe and can be passed around regardless
* of which thread using it.
*
*/
public class CameraCapabilities {
private static Log.Tag TAG = new Log.Tag("CamCapabs");
/** Zoom ratio used for seeing sensor's full field of view. */
protected static final float ZOOM_RATIO_UNZOOMED = 1.0f;
/* All internal states are declared final and should be thread-safe. */
protected final ArrayList mSupportedPreviewFpsRange = new ArrayList();
protected final ArrayList mSupportedPreviewSizes = new ArrayList();
protected final TreeSet mSupportedPreviewFormats = new TreeSet();
protected final ArrayList mSupportedVideoSizes = new ArrayList();
protected final ArrayList mSupportedPhotoSizes = new ArrayList();
protected final TreeSet mSupportedPhotoFormats = new TreeSet();
protected final EnumSet mSupportedSceneModes = EnumSet.noneOf(SceneMode.class);
protected final EnumSet mSupportedFlashModes = EnumSet.noneOf(FlashMode.class);
protected final EnumSet mSupportedFocusModes = EnumSet.noneOf(FocusMode.class);
protected final EnumSet mSupportedWhiteBalances =
EnumSet.noneOf(WhiteBalance.class);
protected final EnumSet mSupportedFeatures = EnumSet.noneOf(Feature.class);
protected Size mPreferredPreviewSizeForVideo;
protected int mMinExposureCompensation;
protected int mMaxExposureCompensation;
protected float mExposureCompensationStep;
protected int mMaxNumOfFacesSupported;
protected int mMaxNumOfFocusAreas;
protected int mMaxNumOfMeteringArea;
protected float mMaxZoomRatio;
protected float mHorizontalViewAngle;
protected float mVerticalViewAngle;
private final Stringifier mStringifier;
/**
* Focus modes.
*/
public enum FocusMode {
/**
* Continuous auto focus mode intended for taking pictures.
* @see {@link android.hardware.Camera.Parameters#FOCUS_MODE_AUTO}.
*/
AUTO,
/**
* Continuous auto focus mode intended for taking pictures.
* @see {@link android.hardware.Camera.Parameters#FOCUS_MODE_CONTINUOUS_PICTURE}.
*/
CONTINUOUS_PICTURE,
/**
* Continuous auto focus mode intended for video recording.
* @see {@link android.hardware.Camera.Parameters#FOCUS_MODE_CONTINUOUS_VIDEO}.
*/
CONTINUOUS_VIDEO,
/**
* Extended depth of field (EDOF).
* @see {@link android.hardware.Camera.Parameters#FOCUS_MODE_EDOF}.
*/
EXTENDED_DOF,
/**
* Focus is fixed.
* @see {@link android.hardware.Camera.Parameters#FOCUS_MODE_FIXED}.
*/
FIXED,
/**
* Focus is set at infinity.
* @see {@link android.hardware.Camera.Parameters#FOCUS_MODE_INFINITY}.
*/
// TODO: Unsupported on API 2
INFINITY,
/**
* Macro (close-up) focus mode.
* @see {@link android.hardware.Camera.Parameters#FOCUS_MODE_MACRO}.
*/
MACRO,
}
/**
* Flash modes.
*/
public enum FlashMode {
/**
* No flash.
*/
NO_FLASH,
/**
* Flash will be fired automatically when required.
* @see {@link android.hardware.Camera.Parameters#FLASH_MODE_OFF}.
*/
AUTO,
/**
* Flash will not be fired.
* @see {@link android.hardware.Camera.Parameters#FLASH_MODE_OFF}.
*/
OFF,
/**
* Flash will always be fired during snapshot.
* @see {@link android.hardware.Camera.Parameters#FLASH_MODE_ON}.
*/
ON,
/**
* Constant emission of light during preview, auto-focus and snapshot.
* @see {@link android.hardware.Camera.Parameters#FLASH_MODE_TORCH}.
*/
TORCH,
/**
* Flash will be fired in red-eye reduction mode.
* @see {@link android.hardware.Camera.Parameters#FLASH_MODE_RED_EYE}.
*/
RED_EYE,
}
/**
* Scene modes.
*/
public enum SceneMode {
/**
* No supported scene mode.
*/
NO_SCENE_MODE,
/**
* Scene mode is off.
* @see {@link android.hardware.Camera.Parameters#SCENE_MODE_AUTO}.
*/
AUTO,
/**
* Take photos of fast moving objects.
* @see {@link android.hardware.Camera.Parameters#SCENE_MODE_ACTION}.
*/
ACTION,
/**
* Applications are looking for a barcode.
* @see {@link android.hardware.Camera.Parameters#SCENE_MODE_BARCODE}.
*/
BARCODE,
/**
* Take pictures on the beach.
* @see {@link android.hardware.Camera.Parameters#SCENE_MODE_BEACH}.
*/
BEACH,
/**
* Capture the naturally warm color of scenes lit by candles.
* @see {@link android.hardware.Camera.Parameters#SCENE_MODE_CANDLELIGHT}.
*/
CANDLELIGHT,
/**
* For shooting firework displays.
* @see {@link android.hardware.Camera.Parameters#SCENE_MODE_FIREWORKS}.
*/
FIREWORKS,
/**
* Capture a scene using high dynamic range imaging techniques.
* @see {@link android.hardware.Camera.Parameters#SCENE_MODE_HDR}.
*/
// Note: Supported as a vendor tag on the Camera2 API for some LEGACY devices.
HDR,
/**
* Take pictures on distant objects.
* @see {@link android.hardware.Camera.Parameters#SCENE_MODE_LANDSCAPE}.
*/
LANDSCAPE,
/**
* Take photos at night.
* @see {@link android.hardware.Camera.Parameters#SCENE_MODE_NIGHT}.
*/
NIGHT,
/**
* Take people pictures at night.
* @see {@link android.hardware.Camera.Parameters#SCENE_MODE_NIGHT_PORTRAIT}.
*/
// TODO: Unsupported on API 2
NIGHT_PORTRAIT,
/**
* Take indoor low-light shot.
* @see {@link android.hardware.Camera.Parameters#SCENE_MODE_PARTY}.
*/
PARTY,
/**
* Take people pictures.
* @see {@link android.hardware.Camera.Parameters#SCENE_MODE_PORTRAIT}.
*/
PORTRAIT,
/**
* Take pictures on the snow.
* @see {@link android.hardware.Camera.Parameters#SCENE_MODE_SNOW}.
*/
SNOW,
/**
* Take photos of fast moving objects.
* @see {@link android.hardware.Camera.Parameters#SCENE_MODE_SPORTS}.
*/
SPORTS,
/**
* Avoid blurry pictures (for example, due to hand shake).
* @see {@link android.hardware.Camera.Parameters#SCENE_MODE_STEADYPHOTO}.
*/
STEADYPHOTO,
/**
* Take sunset photos.
* @see {@link android.hardware.Camera.Parameters#SCENE_MODE_SUNSET}.
*/
SUNSET,
/**
* Take photos in a theater.
* @see {@link android.hardware.Camera.Parameters#SCENE_MODE_THEATRE}.
*/
THEATRE,
}
/**
* White blances.
*/
public enum WhiteBalance {
/**
* @see {@link android.hardware.Camera.Parameters#WHITE_BALANCE_AUTO}.
*/
AUTO,
/**
* @see {@link android.hardware.Camera.Parameters#WHITE_BALANCE_CLOUDY_DAYLIGHT}.
*/
CLOUDY_DAYLIGHT,
/**
* @see {@link android.hardware.Camera.Parameters#WHITE_BALANCE_DAYLIGHT}.
*/
DAYLIGHT,
/**
* @see {@link android.hardware.Camera.Parameters#WHITE_BALANCE_FLUORESCENT}.
*/
FLUORESCENT,
/**
* @see {@link android.hardware.Camera.Parameters#WHITE_BALANCE_INCANDESCENT}.
*/
INCANDESCENT,
/**
* @see {@link android.hardware.Camera.Parameters#WHITE_BALANCE_SHADE}.
*/
SHADE,
/**
* @see {@link android.hardware.Camera.Parameters#WHITE_BALANCE_TWILIGHT}.
*/
TWILIGHT,
/**
* @see {@link android.hardware.Camera.Parameters#WHITE_BALANCE_WARM_FLUORESCENT}.
*/
WARM_FLUORESCENT,
}
/**
* Features.
*/
public enum Feature {
/**
* Support zoom-related methods.
*/
ZOOM,
/**
* Support for photo capturing during video recording.
*/
VIDEO_SNAPSHOT,
/**
* Support for focus area settings.
*/
FOCUS_AREA,
/**
* Support for metering area settings.
*/
METERING_AREA,
/**
* Support for automatic exposure lock.
*/
AUTO_EXPOSURE_LOCK,
/**
* Support for automatic white balance lock.
*/
AUTO_WHITE_BALANCE_LOCK,
/**
* Support for video stabilization.
*/
VIDEO_STABILIZATION,
}
/**
* A interface stringifier to convert abstract representations to API
* related string representation.
*/
public static class Stringifier {
/**
* Converts the string to hyphen-delimited lowercase for compatibility with multiple APIs.
*
* @param enumCase The name of an enum constant.
* @return The converted string.
*/
private static String toApiCase(String enumCase) {
return enumCase.toLowerCase(Locale.US).replaceAll("_", "-");
}
/**
* Converts the string to underscore-delimited uppercase to match the enum constant names.
*
* @param apiCase An API-related string representation.
* @return The converted string.
*/
private static String toEnumCase(String apiCase) {
return apiCase.toUpperCase(Locale.US).replaceAll("-", "_");
}
/**
* Converts the focus mode to API-related string representation.
*
* @param focus The focus mode to convert.
* @return The string used by the camera framework API to represent the
* focus mode.
*/
public String stringify(FocusMode focus) {
return toApiCase(focus.name());
}
/**
* Converts the API-related string representation of the focus mode to the
* abstract representation.
*
* @param val The string representation.
* @return The focus mode represented by the input string, or the focus
* mode with the lowest ordinal if it cannot be converted.
*/
public FocusMode focusModeFromString(String val) {
if (val == null) {
return FocusMode.values()[0];
}
try {
return FocusMode.valueOf(toEnumCase(val));
} catch (IllegalArgumentException ex) {
return FocusMode.values()[0];
}
}
/**
* Converts the flash mode to API-related string representation.
*
* @param flash The focus mode to convert.
* @return The string used by the camera framework API to represent the
* flash mode.
*/
public String stringify(FlashMode flash) {
return toApiCase(flash.name());
}
/**
* Converts the API-related string representation of the flash mode to the
* abstract representation.
*
* @param val The string representation.
* @return The flash mode represented by the input string, or the flash
* mode with the lowest ordinal if it cannot be converted.
*/
public FlashMode flashModeFromString(String val) {
if (val == null) {
return FlashMode.values()[0];
}
try {
return FlashMode.valueOf(toEnumCase(val));
} catch (IllegalArgumentException ex) {
return FlashMode.values()[0];
}
}
/**
* Converts the scene mode to API-related string representation.
*
* @param scene The focus mode to convert.
* @return The string used by the camera framework API to represent the
* scene mode.
*/
public String stringify(SceneMode scene) {
return toApiCase(scene.name());
}
/**
* Converts the API-related string representation of the scene mode to the
* abstract representation.
*
* @param val The string representation.
* @return The scene mode represented by the input string, or the scene
* mode with the lowest ordinal if it cannot be converted.
*/
public SceneMode sceneModeFromString(String val) {
if (val == null) {
return SceneMode.values()[0];
}
try {
return SceneMode.valueOf(toEnumCase(val));
} catch (IllegalArgumentException ex) {
return SceneMode.values()[0];
}
}
/**
* Converts the white balance to API-related string representation.
*
* @param wb The focus mode to convert.
* @return The string used by the camera framework API to represent the
* white balance.
*/
public String stringify(WhiteBalance wb) {
return toApiCase(wb.name());
}
/**
* Converts the API-related string representation of the white balance to
* the abstract representation.
*
* @param val The string representation.
* @return The white balance represented by the input string, or the
* white balance with the lowest ordinal if it cannot be
* converted.
*/
public WhiteBalance whiteBalanceFromString(String val) {
if (val == null) {
return WhiteBalance.values()[0];
}
try {
return WhiteBalance.valueOf(toEnumCase(val));
} catch (IllegalArgumentException ex) {
return WhiteBalance.values()[0];
}
}
}
/**
* Constructor.
* @param stringifier The API-specific stringifier for this instance.
*/
CameraCapabilities(Stringifier stringifier) {
mStringifier = stringifier;
}
/**
* Copy constructor.
* @param src The source instance.
*/
public CameraCapabilities(CameraCapabilities src) {
mSupportedPreviewFpsRange.addAll(src.mSupportedPreviewFpsRange);
mSupportedPreviewSizes.addAll(src.mSupportedPreviewSizes);
mSupportedPreviewFormats.addAll(src.mSupportedPreviewFormats);
mSupportedVideoSizes.addAll(src.mSupportedVideoSizes);
mSupportedPhotoSizes.addAll(src.mSupportedPhotoSizes);
mSupportedPhotoFormats.addAll(src.mSupportedPhotoFormats);
mSupportedSceneModes.addAll(src.mSupportedSceneModes);
mSupportedFlashModes.addAll(src.mSupportedFlashModes);
mSupportedFocusModes.addAll(src.mSupportedFocusModes);
mSupportedWhiteBalances.addAll(src.mSupportedWhiteBalances);
mSupportedFeatures.addAll(src.mSupportedFeatures);
mPreferredPreviewSizeForVideo = src.mPreferredPreviewSizeForVideo;
mMaxExposureCompensation = src.mMaxExposureCompensation;
mMinExposureCompensation = src.mMinExposureCompensation;
mExposureCompensationStep = src.mExposureCompensationStep;
mMaxNumOfFacesSupported = src.mMaxNumOfFacesSupported;
mMaxNumOfFocusAreas = src.mMaxNumOfFocusAreas;
mMaxNumOfMeteringArea = src.mMaxNumOfMeteringArea;
mMaxZoomRatio = src.mMaxZoomRatio;
mHorizontalViewAngle = src.mHorizontalViewAngle;
mVerticalViewAngle = src.mVerticalViewAngle;
mStringifier = src.mStringifier;
}
public float getHorizontalViewAngle() {
return mHorizontalViewAngle;
}
public float getVerticalViewAngle() {
return mVerticalViewAngle;
}
/**
* @return the supported picture formats. See {@link android.graphics.ImageFormat}.
*/
public Set getSupportedPhotoFormats() {
return new TreeSet(mSupportedPhotoFormats);
}
/**
* Gets the supported preview formats.
* @return The supported preview {@link android.graphics.ImageFormat}s.
*/
public Set getSupportedPreviewFormats() {
return new TreeSet(mSupportedPreviewFormats);
}
/**
* Gets the supported picture sizes.
*/
public List getSupportedPhotoSizes() {
return new ArrayList(mSupportedPhotoSizes);
}
/**
* @return The supported preview fps (frame-per-second) ranges. The returned
* list is sorted by maximum fps then minimum fps in a descending order.
* The values are multiplied by 1000.
*/
public final List getSupportedPreviewFpsRange() {
return new ArrayList(mSupportedPreviewFpsRange);
}
/**
* @return The supported preview sizes. The list is sorted by width then
* height in a descending order.
*/
public final List getSupportedPreviewSizes() {
return new ArrayList(mSupportedPreviewSizes);
}
public final Size getPreferredPreviewSizeForVideo() {
return new Size(mPreferredPreviewSizeForVideo);
}
/**
* @return The supported video frame sizes that can be used by MediaRecorder.
* The list is sorted by width then height in a descending order.
*/
public final List getSupportedVideoSizes() {
return new ArrayList(mSupportedVideoSizes);
}
/**
* @return The supported scene modes.
*/
public final Set getSupportedSceneModes() {
return new HashSet(mSupportedSceneModes);
}
/**
* @return Whether the scene mode is supported.
*/
public final boolean supports(SceneMode scene) {
return (scene != null && mSupportedSceneModes.contains(scene));
}
public boolean supports(final CameraSettings settings) {
if (zoomCheck(settings) && exposureCheck(settings) && focusCheck(settings) &&
flashCheck(settings) && photoSizeCheck(settings) && previewSizeCheck(settings) &&
videoStabilizationCheck(settings)) {
return true;
}
return false;
}
/**
* @return The supported flash modes.
*/
public final Set getSupportedFlashModes() {
return new HashSet(mSupportedFlashModes);
}
/**
* @return Whether the flash mode is supported.
*/
public final boolean supports(FlashMode flash) {
return (flash != null && mSupportedFlashModes.contains(flash));
}
/**
* @return The supported focus modes.
*/
public final Set getSupportedFocusModes() {
return new HashSet(mSupportedFocusModes);
}
/**
* @return Whether the focus mode is supported.
*/
public final boolean supports(FocusMode focus) {
return (focus != null && mSupportedFocusModes.contains(focus));
}
/**
* @return The supported white balanceas.
*/
public final Set getSupportedWhiteBalance() {
return new HashSet(mSupportedWhiteBalances);
}
/**
* @return Whether the white balance is supported.
*/
public boolean supports(WhiteBalance wb) {
return (wb != null && mSupportedWhiteBalances.contains(wb));
}
public final Set getSupportedFeature() {
return new HashSet(mSupportedFeatures);
}
public boolean supports(Feature ft) {
return (ft != null && mSupportedFeatures.contains(ft));
}
/**
* @return The maximal supported zoom ratio.
*/
public float getMaxZoomRatio() {
return mMaxZoomRatio;
}
/**
* @return The min exposure compensation index. The EV is the compensation
* index multiplied by the step value. If unsupported, both this method and
* {@link #getMaxExposureCompensation()} return 0.
*/
public final int getMinExposureCompensation() {
return mMinExposureCompensation;
}
/**
* @return The max exposure compensation index. The EV is the compensation
* index multiplied by the step value. If unsupported, both this method and
* {@link #getMinExposureCompensation()} return 0.
*/
public final int getMaxExposureCompensation() {
return mMaxExposureCompensation;
}
/**
* @return The exposure compensation step. The EV is the compensation index
* multiplied by the step value.
*/
public final float getExposureCompensationStep() {
return mExposureCompensationStep;
}
/**
* @return The max number of faces supported by the face detection. 0 if
* unsupported.
*/
public final int getMaxNumOfFacesSupported() {
return mMaxNumOfFacesSupported;
}
/**
* @return The stringifier used by this instance.
*/
public Stringifier getStringifier() {
return mStringifier;
}
private boolean zoomCheck(final CameraSettings settings) {
final float ratio = settings.getCurrentZoomRatio();
if (!supports(Feature.ZOOM)) {
if (ratio != ZOOM_RATIO_UNZOOMED) {
Log.v(TAG, "Zoom is not supported");
return false;
}
} else {
if (settings.getCurrentZoomRatio() > getMaxZoomRatio()) {
Log.v(TAG, "Zoom ratio is not supported: ratio = " +
settings.getCurrentZoomRatio());
return false;
}
}
return true;
}
private boolean exposureCheck(final CameraSettings settings) {
final int index = settings.getExposureCompensationIndex();
if (index > getMaxExposureCompensation() || index < getMinExposureCompensation()) {
Log.v(TAG, "Exposure compensation index is not supported. Min = " +
getMinExposureCompensation() + ", max = " + getMaxExposureCompensation() + "," +
" setting = " + index);
return false;
}
return true;
}
private boolean focusCheck(final CameraSettings settings) {
FocusMode focusMode = settings.getCurrentFocusMode();
if (!supports(focusMode)) {
if (supports(FocusMode.FIXED)) {
// Workaround for devices whose templates define defaults they don't really support
// TODO: Remove workaround (b/17177436)
Log.w(TAG, "Focus mode not supported... trying FIXED");
settings.setFocusMode(FocusMode.FIXED);
} else {
Log.v(TAG, "Focus mode not supported:" +
(focusMode != null ? focusMode.name() : "null"));
return false;
}
}
return true;
}
private boolean flashCheck(final CameraSettings settings) {
FlashMode flashMode = settings.getCurrentFlashMode();
if (!supports(flashMode)) {
Log.v(TAG,
"Flash mode not supported:" + (flashMode != null ? flashMode.name() : "null"));
return false;
}
return true;
}
private boolean photoSizeCheck(final CameraSettings settings) {
Size photoSize = settings.getCurrentPhotoSize();
if (mSupportedPhotoSizes.contains(photoSize)) {
return true;
}
Log.v(TAG, "Unsupported photo size:" + photoSize);
return false;
}
private boolean previewSizeCheck(final CameraSettings settings) {
final Size previewSize = settings.getCurrentPreviewSize();
if (mSupportedPreviewSizes.contains(previewSize)) {
return true;
}
Log.v(TAG, "Unsupported preview size:" + previewSize);
return false;
}
private boolean videoStabilizationCheck(final CameraSettings settings) {
if (!settings.isVideoStabilizationEnabled() || supports(Feature.VIDEO_STABILIZATION)) {
return true;
}
Log.v(TAG, "Video stabilization is not supported");
return false;
}
}