/* * 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; } }