/*
* 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 static android.hardware.camera2.CameraCharacteristics.*;
import android.graphics.ImageFormat;
import android.graphics.Point;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.MediaRecorder;
import android.util.Range;
import android.util.Rational;
import com.android.ex.camera2.portability.debug.Log;
import java.util.ArrayList;
import java.util.Arrays;
/**
* The subclass of {@link CameraCapabilities} for Android Camera 2 API.
*/
public class AndroidCamera2Capabilities extends CameraCapabilities {
private static Log.Tag TAG = new Log.Tag("AndCam2Capabs");
AndroidCamera2Capabilities(CameraCharacteristics p) {
super(new Stringifier());
StreamConfigurationMap s = p.get(SCALER_STREAM_CONFIGURATION_MAP);
for (Range<Integer> fpsRange : p.get(CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES)) {
mSupportedPreviewFpsRange.add(new int[] { fpsRange.getLower(), fpsRange.getUpper() });
}
// TODO: We only support TextureView preview rendering
mSupportedPreviewSizes.addAll(Size.buildListFromAndroidSizes(Arrays.asList(
s.getOutputSizes(SurfaceTexture.class))));
for (int format : s.getOutputFormats()) {
mSupportedPreviewFormats.add(format);
}
// TODO: We only support MediaRecorder video capture
mSupportedVideoSizes.addAll(Size.buildListFromAndroidSizes(Arrays.asList(
s.getOutputSizes(MediaRecorder.class))));
// TODO: We only support JPEG image capture
mSupportedPhotoSizes.addAll(Size.buildListFromAndroidSizes(Arrays.asList(
s.getOutputSizes(ImageFormat.JPEG))));
mSupportedPhotoFormats.addAll(mSupportedPreviewFormats);
buildSceneModes(p);
buildFlashModes(p);
buildFocusModes(p);
buildWhiteBalances(p);
// TODO: Populate mSupportedFeatures
// TODO: Populate mPreferredPreviewSizeForVideo
Range<Integer> ecRange = p.get(CONTROL_AE_COMPENSATION_RANGE);
mMinExposureCompensation = ecRange.getLower();
mMaxExposureCompensation = ecRange.getUpper();
Rational ecStep = p.get(CONTROL_AE_COMPENSATION_STEP);
mExposureCompensationStep = (float) ecStep.getNumerator() / ecStep.getDenominator();
mMaxNumOfFacesSupported = p.get(STATISTICS_INFO_MAX_FACE_COUNT);
mMaxNumOfMeteringArea = p.get(CONTROL_MAX_REGIONS_AE);
mMaxZoomRatio = p.get(SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
// TODO: Populate mHorizontalViewAngle
// TODO: Populate mVerticalViewAngle
// TODO: Populate mZoomRatioList
// TODO: Populate mMaxZoomIndex
if (supports(FocusMode.AUTO)) {
mMaxNumOfFocusAreas = p.get(CONTROL_MAX_REGIONS_AF);
if (mMaxNumOfFocusAreas > 0) {
mSupportedFeatures.add(Feature.FOCUS_AREA);
}
}
if (mMaxNumOfMeteringArea > 0) {
mSupportedFeatures.add(Feature.METERING_AREA);
}
if (mMaxZoomRatio > CameraCapabilities.ZOOM_RATIO_UNZOOMED) {
mSupportedFeatures.add(Feature.ZOOM);
}
// TODO: Detect other features
}
private void buildSceneModes(CameraCharacteristics p) {
int[] scenes = p.get(CONTROL_AVAILABLE_SCENE_MODES);
if (scenes != null) {
for (int scene : scenes) {
SceneMode equiv = sceneModeFromInt(scene);
if (equiv != null) {
mSupportedSceneModes.add(equiv);
}
}
}
}
private void buildFlashModes(CameraCharacteristics p) {
mSupportedFlashModes.add(FlashMode.OFF);
if (p.get(FLASH_INFO_AVAILABLE)) {
mSupportedFlashModes.add(FlashMode.AUTO);
mSupportedFlashModes.add(FlashMode.ON);
mSupportedFlashModes.add(FlashMode.TORCH);
for (int expose : p.get(CONTROL_AE_AVAILABLE_MODES)) {
if (expose == CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE) {
mSupportedFlashModes.add(FlashMode.RED_EYE);
}
}
}
}
private void buildFocusModes(CameraCharacteristics p) {
int[] focuses = p.get(CONTROL_AF_AVAILABLE_MODES);
if (focuses != null) {
for (int focus : focuses) {
FocusMode equiv = focusModeFromInt(focus);
if (equiv != null) {
mSupportedFocusModes.add(equiv);
}
}
}
}
private void buildWhiteBalances(CameraCharacteristics p) {
int[] bals = p.get(CONTROL_AWB_AVAILABLE_MODES);
if (bals != null) {
for (int bal : bals) {
WhiteBalance equiv = whiteBalanceFromInt(bal);
if (equiv != null) {
mSupportedWhiteBalances.add(equiv);
}
}
}
}
/**
* Converts the API-related integer representation of the focus mode to the
* abstract representation.
*
* @param fm The integral representation.
* @return The mode represented by the input integer, or {@code null} if it
* cannot be converted.
*/
public static FocusMode focusModeFromInt(int fm) {
switch (fm) {
case CONTROL_AF_MODE_AUTO:
return FocusMode.AUTO;
case CONTROL_AF_MODE_CONTINUOUS_PICTURE:
return FocusMode.CONTINUOUS_PICTURE;
case CONTROL_AF_MODE_CONTINUOUS_VIDEO:
return FocusMode.CONTINUOUS_VIDEO;
case CONTROL_AF_MODE_EDOF:
return FocusMode.EXTENDED_DOF;
case CONTROL_AF_MODE_OFF:
return FocusMode.FIXED;
// TODO: We cannot support INFINITY
case CONTROL_AF_MODE_MACRO:
return FocusMode.MACRO;
}
Log.w(TAG, "Unable to convert from API 2 focus mode: " + fm);
return null;
}
/**
* Converts the API-related integer representation of the scene mode to the
* abstract representation.
*
* @param sm The integral representation.
* @return The mode represented by the input integer, or {@code null} if it
* cannot be converted.
*/
public static SceneMode sceneModeFromInt(int sm) {
switch (sm) {
case CONTROL_SCENE_MODE_DISABLED:
return SceneMode.AUTO;
case CONTROL_SCENE_MODE_ACTION:
return SceneMode.ACTION;
case CONTROL_SCENE_MODE_BARCODE:
return SceneMode.BARCODE;
case CONTROL_SCENE_MODE_BEACH:
return SceneMode.BEACH;
case CONTROL_SCENE_MODE_CANDLELIGHT:
return SceneMode.CANDLELIGHT;
case CONTROL_SCENE_MODE_FIREWORKS:
return SceneMode.FIREWORKS;
case CONTROL_SCENE_MODE_LANDSCAPE:
return SceneMode.LANDSCAPE;
case CONTROL_SCENE_MODE_NIGHT:
return SceneMode.NIGHT;
// TODO: We cannot support NIGHT_PORTRAIT
case CONTROL_SCENE_MODE_PARTY:
return SceneMode.PARTY;
case CONTROL_SCENE_MODE_PORTRAIT:
return SceneMode.PORTRAIT;
case CONTROL_SCENE_MODE_SNOW:
return SceneMode.SNOW;
case CONTROL_SCENE_MODE_SPORTS:
return SceneMode.SPORTS;
case CONTROL_SCENE_MODE_STEADYPHOTO:
return SceneMode.STEADYPHOTO;
case CONTROL_SCENE_MODE_SUNSET:
return SceneMode.SUNSET;
case CONTROL_SCENE_MODE_THEATRE:
return SceneMode.THEATRE;
case CONTROL_SCENE_MODE_HDR:
return SceneMode.HDR;
// TODO: We cannot expose FACE_PRIORITY, or HIGH_SPEED_VIDEO
}
Log.w(TAG, "Unable to convert from API 2 scene mode: " + sm);
return null;
}
/**
* Converts the API-related integer representation of the white balance to
* the abstract representation.
*
* @param wb The integral representation.
* @return The balance represented by the input integer, or {@code null} if
* it cannot be converted.
*/
public static WhiteBalance whiteBalanceFromInt(int wb) {
switch (wb) {
case CONTROL_AWB_MODE_AUTO:
return WhiteBalance.AUTO;
case CONTROL_AWB_MODE_CLOUDY_DAYLIGHT:
return WhiteBalance.CLOUDY_DAYLIGHT;
case CONTROL_AWB_MODE_DAYLIGHT:
return WhiteBalance.DAYLIGHT;
case CONTROL_AWB_MODE_FLUORESCENT:
return WhiteBalance.FLUORESCENT;
case CONTROL_AWB_MODE_INCANDESCENT:
return WhiteBalance.INCANDESCENT;
case CONTROL_AWB_MODE_SHADE:
return WhiteBalance.SHADE;
case CONTROL_AWB_MODE_TWILIGHT:
return WhiteBalance.TWILIGHT;
case CONTROL_AWB_MODE_WARM_FLUORESCENT:
return WhiteBalance.WARM_FLUORESCENT;
}
Log.w(TAG, "Unable to convert from API 2 white balance: " + wb);
return null;
}
}