blob: 362ddfae67bf83f039338ff65435aa31bbf161f5 [file] [log] [blame]
/*
* 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 android.hardware.camera2.legacy;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.params.MeteringRectangle;
import android.hardware.camera2.params.StreamConfiguration;
import android.hardware.camera2.params.StreamConfigurationDuration;
import android.hardware.camera2.utils.ArrayUtils;
import android.hardware.camera2.utils.ListUtils;
import android.hardware.camera2.utils.ParamsUtils;
import android.util.Log;
import android.util.Range;
import android.util.Size;
import android.util.SizeF;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static com.android.internal.util.Preconditions.*;
import static android.hardware.camera2.CameraCharacteristics.*;
import static android.hardware.camera2.legacy.ParameterUtils.*;
/**
* Provide legacy-specific implementations of camera2 metadata for legacy devices, such as the
* camera characteristics.
*/
@SuppressWarnings("deprecation")
public class LegacyMetadataMapper {
private static final String TAG = "LegacyMetadataMapper";
private static final boolean DEBUG = false;
private static final long NS_PER_MS = 1000000;
// from graphics.h
public static final int HAL_PIXEL_FORMAT_RGBA_8888 = PixelFormat.RGBA_8888;
public static final int HAL_PIXEL_FORMAT_BGRA_8888 = 0x5;
public static final int HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22;
public static final int HAL_PIXEL_FORMAT_BLOB = 0x21;
// for metadata
private static final float LENS_INFO_MINIMUM_FOCUS_DISTANCE_FIXED_FOCUS = 0.0f;
private static final int REQUEST_MAX_NUM_OUTPUT_STREAMS_COUNT_RAW = 0; // no raw support
private static final int REQUEST_MAX_NUM_OUTPUT_STREAMS_COUNT_PROC = 3; // preview, video, cb
private static final int REQUEST_MAX_NUM_OUTPUT_STREAMS_COUNT_PROC_STALL = 1; // 1 jpeg only
private static final int REQUEST_MAX_NUM_INPUT_STREAMS_COUNT = 0; // no reprocessing
/** Assume 3 HAL1 stages: Exposure, Read-out, Post-Processing */
private static final int REQUEST_PIPELINE_MAX_DEPTH_HAL1 = 3;
/** Assume 3 shim stages: Preview input, Split output, Format conversion for output */
private static final int REQUEST_PIPELINE_MAX_DEPTH_OURS = 3;
/* TODO: Update above maxDepth values once we do more performance measurements */
// For approximating JPEG stall durations
private static final long APPROXIMATE_CAPTURE_DELAY_MS = 200; // 200 milliseconds
private static final long APPROXIMATE_SENSOR_AREA_PX = (1 << 23); // 8 megapixels
private static final long APPROXIMATE_JPEG_ENCODE_TIME_MS = 600; // 600 milliseconds
static final int UNKNOWN_MODE = -1;
// Maximum difference between a preview size aspect ratio and a jpeg size aspect ratio
private static final float PREVIEW_ASPECT_RATIO_TOLERANCE = 0.01f;
/*
* Development hijinks: Lie about not supporting certain capabilities
*
* - Unblock some CTS tests from running whose main intent is not the metadata itself
*
* TODO: Remove these constants and strip out any code that previously relied on them
* being set to true.
*/
static final boolean LIE_ABOUT_AE_STATE = false;
static final boolean LIE_ABOUT_AE_MAX_REGIONS = false;
static final boolean LIE_ABOUT_AF = false;
static final boolean LIE_ABOUT_AF_MAX_REGIONS = false;
static final boolean LIE_ABOUT_AWB_STATE = false;
static final boolean LIE_ABOUT_AWB = false;
/**
* Create characteristics for a legacy device by mapping the {@code parameters}
* and {@code info}
*
* @param parameters A non-{@code null} parameters set
* @param info Camera info with camera facing direction and angle of orientation
* @param cameraId Current camera Id
* @param displaySize Device display size
*
* @return static camera characteristics for a camera device
*
* @throws NullPointerException if any of the args were {@code null}
*/
public static CameraCharacteristics createCharacteristics(Camera.Parameters parameters,
CameraInfo info, int cameraId, Size displaySize) {
checkNotNull(parameters, "parameters must not be null");
checkNotNull(info, "info must not be null");
String paramStr = parameters.flatten();
android.hardware.CameraInfo outerInfo = new android.hardware.CameraInfo();
outerInfo.info = info;
return createCharacteristics(paramStr, outerInfo, cameraId, displaySize);
}
/**
* Create characteristics for a legacy device by mapping the {@code parameters}
* and {@code info}
*
* @param parameters A string parseable by {@link Camera.Parameters#unflatten}
* @param info Camera info with camera facing direction and angle of orientation
* @param cameraId Current camera id
* @param displaySize Device display size
* @return static camera characteristics for a camera device
*
* @throws NullPointerException if any of the args were {@code null}
*/
public static CameraCharacteristics createCharacteristics(String parameters,
android.hardware.CameraInfo info, int cameraId, Size displaySize) {
checkNotNull(parameters, "parameters must not be null");
checkNotNull(info, "info must not be null");
checkNotNull(info.info, "info.info must not be null");
CameraMetadataNative m = new CameraMetadataNative();
mapCharacteristicsFromInfo(m, info.info);
Camera.Parameters params = Camera.getEmptyParameters();
params.unflatten(parameters);
mapCharacteristicsFromParameters(m, params);
if (DEBUG) {
Log.v(TAG, "createCharacteristics metadata:");
Log.v(TAG, "--------------------------------------------------- (start)");
m.dumpToLog();
Log.v(TAG, "--------------------------------------------------- (end)");
}
m.setCameraId(cameraId);
m.setDisplaySize(displaySize);
return new CameraCharacteristics(m);
}
private static void mapCharacteristicsFromInfo(CameraMetadataNative m, CameraInfo i) {
m.set(LENS_FACING, i.facing == CameraInfo.CAMERA_FACING_BACK ?
LENS_FACING_BACK : LENS_FACING_FRONT);
m.set(SENSOR_ORIENTATION, i.orientation);
}
private static void mapCharacteristicsFromParameters(CameraMetadataNative m,
Camera.Parameters p) {
/*
* colorCorrection.*
*/
m.set(COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
new int[] { COLOR_CORRECTION_ABERRATION_MODE_FAST,
COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY });
/*
* control.ae*
*/
mapControlAe(m, p);
/*
* control.af*
*/
mapControlAf(m, p);
/*
* control.awb*
*/
mapControlAwb(m, p);
/*
* control.*
* - Anything that doesn't have a set of related fields
*/
mapControlOther(m, p);
/*
* lens.*
*/
mapLens(m, p);
/*
* flash.*
*/
mapFlash(m, p);
/*
* jpeg.*
*/
mapJpeg(m, p);
/*
* noiseReduction.*
*/
m.set(NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
new int[] { NOISE_REDUCTION_MODE_FAST,
NOISE_REDUCTION_MODE_HIGH_QUALITY});
/*
* scaler.*
*/
mapScaler(m, p);
/*
* sensor.*
*/
mapSensor(m, p);
/*
* statistics.*
*/
mapStatistics(m, p);
/*
* sync.*
*/
mapSync(m, p);
/*
* info.supportedHardwareLevel
*/
m.set(INFO_SUPPORTED_HARDWARE_LEVEL, INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY);
/*
* scaler.availableStream*, scaler.available*Durations, sensor.info.maxFrameDuration
*/
mapScalerStreamConfigs(m, p);
// Order matters below: Put this last so that we can read the metadata set previously
/*
* request.*
*/
mapRequest(m, p);
}
private static void mapScalerStreamConfigs(CameraMetadataNative m, Camera.Parameters p) {
ArrayList<StreamConfiguration> availableStreamConfigs = new ArrayList<>();
/*
* Implementation-defined (preview, recording, etc) -> use camera1 preview sizes
* YUV_420_888 cpu callbacks -> use camera1 preview sizes
* Other preview callbacks (CPU) -> use camera1 preview sizes
* JPEG still capture -> use camera1 still capture sizes
*
* Use platform-internal format constants here, since StreamConfigurationMap does the
* remapping to public format constants.
*/
List<Camera.Size> previewSizes = p.getSupportedPreviewSizes();
List<Camera.Size> jpegSizes = p.getSupportedPictureSizes();
/*
* Work-around for b/17589233:
* - Some HALs's largest preview size aspect ratio does not match the largest JPEG size AR
* - This causes a large amount of problems with focus/metering because it's relative to
* preview, making the difference between the JPEG and preview viewport inaccessible
* - This boils down to metering or focusing areas being "arbitrarily" cropped
* in the capture result.
* - Work-around the HAL limitations by removing all of the largest preview sizes
* until we get one with the same aspect ratio as the jpeg size.
*/
{
SizeAreaComparator areaComparator = new SizeAreaComparator();
// Sort preview to min->max
Collections.sort(previewSizes, areaComparator);
Camera.Size maxJpegSize = SizeAreaComparator.findLargestByArea(jpegSizes);
float jpegAspectRatio = maxJpegSize.width * 1.0f / maxJpegSize.height;
if (DEBUG) {
Log.v(TAG, String.format("mapScalerStreamConfigs - largest JPEG area %dx%d, AR=%f",
maxJpegSize.width, maxJpegSize.height, jpegAspectRatio));
}
// Now remove preview sizes from the end (largest->smallest) until aspect ratio matches
while (!previewSizes.isEmpty()) {
int index = previewSizes.size() - 1; // max is always at the end
Camera.Size size = previewSizes.get(index);
float previewAspectRatio = size.width * 1.0f / size.height;
if (Math.abs(jpegAspectRatio - previewAspectRatio) >=
PREVIEW_ASPECT_RATIO_TOLERANCE) {
previewSizes.remove(index); // Assume removing from end is O(1)
if (DEBUG) {
Log.v(TAG, String.format(
"mapScalerStreamConfigs - removed preview size %dx%d, AR=%f "
+ "was not the same",
size.width, size.height, previewAspectRatio));
}
} else {
break;
}
}
if (previewSizes.isEmpty()) {
// Fall-back to the original faulty behavior, but at least work
Log.w(TAG, "mapScalerStreamConfigs - failed to find any preview size matching " +
"JPEG aspect ratio " + jpegAspectRatio);
previewSizes = p.getSupportedPreviewSizes();
}
// Sort again, this time in descending order max->min
Collections.sort(previewSizes, Collections.reverseOrder(areaComparator));
}
appendStreamConfig(availableStreamConfigs,
HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, previewSizes);
appendStreamConfig(availableStreamConfigs,
ImageFormat.YUV_420_888, previewSizes);
for (int format : p.getSupportedPreviewFormats()) {
if (ImageFormat.isPublicFormat(format) && format != ImageFormat.NV21) {
appendStreamConfig(availableStreamConfigs, format, previewSizes);
} else if (DEBUG) {
/*
* Do not add any formats unknown to us
* (since it would fail runtime checks in StreamConfigurationMap)
*/
Log.v(TAG,
String.format("mapStreamConfigs - Skipping format %x", format));
}
}
appendStreamConfig(availableStreamConfigs,
HAL_PIXEL_FORMAT_BLOB, p.getSupportedPictureSizes());
/*
* scaler.availableStreamConfigurations
*/
m.set(SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
availableStreamConfigs.toArray(new StreamConfiguration[0]));
/*
* scaler.availableMinFrameDurations
*/
// No frame durations available
m.set(SCALER_AVAILABLE_MIN_FRAME_DURATIONS, new StreamConfigurationDuration[0]);
StreamConfigurationDuration[] jpegStalls =
new StreamConfigurationDuration[jpegSizes.size()];
int i = 0;
long longestStallDuration = -1;
for (Camera.Size s : jpegSizes) {
long stallDuration = calculateJpegStallDuration(s);
jpegStalls[i++] = new StreamConfigurationDuration(HAL_PIXEL_FORMAT_BLOB, s.width,
s.height, stallDuration);
if (longestStallDuration < stallDuration) {
longestStallDuration = stallDuration;
}
}
/*
* scaler.availableStallDurations
*/
// Set stall durations for jpeg, other formats use default stall duration
m.set(SCALER_AVAILABLE_STALL_DURATIONS, jpegStalls);
/*
* sensor.info.maxFrameDuration
*/
m.set(SENSOR_INFO_MAX_FRAME_DURATION, longestStallDuration);
}
@SuppressWarnings({"unchecked"})
private static void mapControlAe(CameraMetadataNative m, Camera.Parameters p) {
/*
* control.aeAvailableAntiBandingModes
*/
List<String> antiBandingModes = p.getSupportedAntibanding();
if (antiBandingModes != null && antiBandingModes.size() > 0) { // antibanding is optional
int[] modes = new int[antiBandingModes.size()];
int j = 0;
for (String mode : antiBandingModes) {
int convertedMode = convertAntiBandingMode(mode);
if (DEBUG && convertedMode == -1) {
Log.v(TAG, "Antibanding mode " + ((mode == null) ? "NULL" : mode) +
" not supported, skipping...");
} else {
modes[j++] = convertedMode;
}
}
m.set(CONTROL_AE_AVAILABLE_ANTIBANDING_MODES, Arrays.copyOf(modes, j));
} else {
m.set(CONTROL_AE_AVAILABLE_ANTIBANDING_MODES, new int[0]);
}
/*
* control.aeAvailableTargetFpsRanges
*/
{
List<int[]> fpsRanges = p.getSupportedPreviewFpsRange();
if (fpsRanges == null) {
throw new AssertionError("Supported FPS ranges cannot be null.");
}
int rangesSize = fpsRanges.size();
if (rangesSize <= 0) {
throw new AssertionError("At least one FPS range must be supported.");
}
Range<Integer>[] ranges = new Range[rangesSize];
int i = 0;
for (int[] r : fpsRanges) {
ranges[i++] = Range.create(
(int) Math.floor(r[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] / 1000.0),
(int) Math.ceil(r[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] / 1000.0));
}
m.set(CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, ranges);
}
/*
* control.aeAvailableModes
*/
{
List<String> flashModes = p.getSupportedFlashModes();
String[] flashModeStrings = new String[] {
Camera.Parameters.FLASH_MODE_OFF,
Camera.Parameters.FLASH_MODE_AUTO,
Camera.Parameters.FLASH_MODE_ON,
Camera.Parameters.FLASH_MODE_RED_EYE,
// Map these manually
Camera.Parameters.FLASH_MODE_TORCH,
};
int[] flashModeInts = new int[] {
CONTROL_AE_MODE_ON,
CONTROL_AE_MODE_ON_AUTO_FLASH,
CONTROL_AE_MODE_ON_ALWAYS_FLASH,
CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE
};
int[] aeAvail = ArrayUtils.convertStringListToIntArray(
flashModes, flashModeStrings, flashModeInts);
// No flash control -> AE is always on
if (aeAvail == null || aeAvail.length == 0) {
aeAvail = new int[] {
CONTROL_AE_MODE_ON
};
}
// Note that AE_MODE_OFF is never available.
m.set(CONTROL_AE_AVAILABLE_MODES, aeAvail);
}
/*
* control.aeCompensationRanges
*/
{
int min = p.getMinExposureCompensation();
int max = p.getMaxExposureCompensation();
m.set(CONTROL_AE_COMPENSATION_RANGE, Range.create(min, max));
}
/*
* control.aeCompensationStep
*/
{
float step = p.getExposureCompensationStep();
m.set(CONTROL_AE_COMPENSATION_STEP, ParamsUtils.createRational(step));
}
/*
* control.aeLockAvailable
*/
{
boolean aeLockAvailable = p.isAutoExposureLockSupported();
m.set(CONTROL_AE_LOCK_AVAILABLE, aeLockAvailable);
}
}
@SuppressWarnings({"unchecked"})
private static void mapControlAf(CameraMetadataNative m, Camera.Parameters p) {
/*
* control.afAvailableModes
*/
{
List<String> focusModes = p.getSupportedFocusModes();
String[] focusModeStrings = new String[] {
Camera.Parameters.FOCUS_MODE_AUTO,
Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE,
Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO,
Camera.Parameters.FOCUS_MODE_EDOF,
Camera.Parameters.FOCUS_MODE_INFINITY,
Camera.Parameters.FOCUS_MODE_MACRO,
Camera.Parameters.FOCUS_MODE_FIXED,
};
int[] focusModeInts = new int[] {
CONTROL_AF_MODE_AUTO,
CONTROL_AF_MODE_CONTINUOUS_PICTURE,
CONTROL_AF_MODE_CONTINUOUS_VIDEO,
CONTROL_AF_MODE_EDOF,
CONTROL_AF_MODE_OFF,
CONTROL_AF_MODE_MACRO,
CONTROL_AF_MODE_OFF
};
List<Integer> afAvail = ArrayUtils.convertStringListToIntList(
focusModes, focusModeStrings, focusModeInts);
// No AF modes supported? That's unpossible!
if (afAvail == null || afAvail.size() == 0) {
Log.w(TAG, "No AF modes supported (HAL bug); defaulting to AF_MODE_OFF only");
afAvail = new ArrayList<Integer>(/*capacity*/1);
afAvail.add(CONTROL_AF_MODE_OFF);
}
m.set(CONTROL_AF_AVAILABLE_MODES, ArrayUtils.toIntArray(afAvail));
if (DEBUG) {
Log.v(TAG, "mapControlAf - control.afAvailableModes set to " +
ListUtils.listToString(afAvail));
}
}
}
private static void mapControlAwb(CameraMetadataNative m, Camera.Parameters p) {
/*
* control.awbAvailableModes
*/
{
List<String> wbModes = p.getSupportedWhiteBalance();
String[] wbModeStrings = new String[] {
Camera.Parameters.WHITE_BALANCE_AUTO ,
Camera.Parameters.WHITE_BALANCE_INCANDESCENT ,
Camera.Parameters.WHITE_BALANCE_FLUORESCENT ,
Camera.Parameters.WHITE_BALANCE_WARM_FLUORESCENT ,
Camera.Parameters.WHITE_BALANCE_DAYLIGHT ,
Camera.Parameters.WHITE_BALANCE_CLOUDY_DAYLIGHT ,
Camera.Parameters.WHITE_BALANCE_TWILIGHT ,
Camera.Parameters.WHITE_BALANCE_SHADE ,
};
int[] wbModeInts = new int[] {
CONTROL_AWB_MODE_AUTO,
CONTROL_AWB_MODE_INCANDESCENT ,
CONTROL_AWB_MODE_FLUORESCENT ,
CONTROL_AWB_MODE_WARM_FLUORESCENT ,
CONTROL_AWB_MODE_DAYLIGHT ,
CONTROL_AWB_MODE_CLOUDY_DAYLIGHT ,
CONTROL_AWB_MODE_TWILIGHT ,
CONTROL_AWB_MODE_SHADE ,
// Note that CONTROL_AWB_MODE_OFF is unsupported
};
List<Integer> awbAvail = ArrayUtils.convertStringListToIntList(
wbModes, wbModeStrings, wbModeInts);
// No AWB modes supported? That's unpossible!
if (awbAvail == null || awbAvail.size() == 0) {
Log.w(TAG, "No AWB modes supported (HAL bug); defaulting to AWB_MODE_AUTO only");
awbAvail = new ArrayList<Integer>(/*capacity*/1);
awbAvail.add(CONTROL_AWB_MODE_AUTO);
}
m.set(CONTROL_AWB_AVAILABLE_MODES, ArrayUtils.toIntArray(awbAvail));
if (DEBUG) {
Log.v(TAG, "mapControlAwb - control.awbAvailableModes set to " +
ListUtils.listToString(awbAvail));
}
/*
* control.awbLockAvailable
*/
{
boolean awbLockAvailable = p.isAutoWhiteBalanceLockSupported();
m.set(CONTROL_AWB_LOCK_AVAILABLE, awbLockAvailable);
}
}
}
private static void mapControlOther(CameraMetadataNative m, Camera.Parameters p) {
/*
* android.control.availableVideoStabilizationModes
*/
{
int stabModes[] = p.isVideoStabilizationSupported() ?
new int[] { CONTROL_VIDEO_STABILIZATION_MODE_OFF,
CONTROL_VIDEO_STABILIZATION_MODE_ON } :
new int[] { CONTROL_VIDEO_STABILIZATION_MODE_OFF };
m.set(CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES, stabModes);
}
/*
* android.control.maxRegions
*/
final int AE = 0, AWB = 1, AF = 2;
int[] maxRegions = new int[3];
maxRegions[AE] = p.getMaxNumMeteringAreas();
maxRegions[AWB] = 0; // AWB regions not supported in API1
maxRegions[AF] = p.getMaxNumFocusAreas();
if (LIE_ABOUT_AE_MAX_REGIONS) {
maxRegions[AE] = 0;
}
if (LIE_ABOUT_AF_MAX_REGIONS) {
maxRegions[AF] = 0;
}
m.set(CONTROL_MAX_REGIONS, maxRegions);
/*
* android.control.availableEffects
*/
List<String> effectModes = p.getSupportedColorEffects();
int[] supportedEffectModes = (effectModes == null) ? new int[0] :
ArrayUtils.convertStringListToIntArray(effectModes, sLegacyEffectMode,
sEffectModes);
m.set(CONTROL_AVAILABLE_EFFECTS, supportedEffectModes);
/*
* android.control.availableSceneModes
*/
int maxNumDetectedFaces = p.getMaxNumDetectedFaces();
List<String> sceneModes = p.getSupportedSceneModes();
List<Integer> supportedSceneModes =
ArrayUtils.convertStringListToIntList(sceneModes, sLegacySceneModes, sSceneModes);
// Special case where the only scene mode listed is AUTO => no scene mode
if (sceneModes != null && sceneModes.size() == 1 &&
sceneModes.get(0).equals(Parameters.SCENE_MODE_AUTO)) {
supportedSceneModes = null;
}
boolean sceneModeSupported = true;
if (supportedSceneModes == null && maxNumDetectedFaces == 0) {
sceneModeSupported = false;
}
if (sceneModeSupported) {
if (supportedSceneModes == null) {
supportedSceneModes = new ArrayList<Integer>();
}
if (maxNumDetectedFaces > 0) { // always supports FACE_PRIORITY when face detecting
supportedSceneModes.add(CONTROL_SCENE_MODE_FACE_PRIORITY);
}
// Remove all DISABLED occurrences
if (supportedSceneModes.contains(CONTROL_SCENE_MODE_DISABLED)) {
while(supportedSceneModes.remove(new Integer(CONTROL_SCENE_MODE_DISABLED))) {}
}
m.set(CONTROL_AVAILABLE_SCENE_MODES, ArrayUtils.toIntArray(supportedSceneModes));
} else {
m.set(CONTROL_AVAILABLE_SCENE_MODES, new int[] {CONTROL_SCENE_MODE_DISABLED});
}
/*
* android.control.availableModes
*/
m.set(CONTROL_AVAILABLE_MODES, sceneModeSupported ?
new int[] { CONTROL_MODE_AUTO, CONTROL_MODE_USE_SCENE_MODE } :
new int[] { CONTROL_MODE_AUTO });
}
private static void mapLens(CameraMetadataNative m, Camera.Parameters p) {
/*
* We can tell if the lens is fixed focus;
* but if it's not, we can't tell the minimum focus distance, so leave it null then.
*/
if (DEBUG) {
Log.v(TAG, "mapLens - focus-mode='" + p.getFocusMode() + "'");
}
if (Camera.Parameters.FOCUS_MODE_FIXED.equals(p.getFocusMode())) {
/*
* lens.info.minimumFocusDistance
*/
m.set(LENS_INFO_MINIMUM_FOCUS_DISTANCE, LENS_INFO_MINIMUM_FOCUS_DISTANCE_FIXED_FOCUS);
if (DEBUG) {
Log.v(TAG, "mapLens - lens.info.minimumFocusDistance = 0");
}
} else {
if (DEBUG) {
Log.v(TAG, "mapLens - lens.info.minimumFocusDistance is unknown");
}
}
float[] focalLengths = new float[] { p.getFocalLength() };
m.set(LENS_INFO_AVAILABLE_FOCAL_LENGTHS, focalLengths);
}
private static void mapFlash(CameraMetadataNative m, Camera.Parameters p) {
boolean flashAvailable = false;
List<String> supportedFlashModes = p.getSupportedFlashModes();
if (supportedFlashModes != null) {
// If only 'OFF' is available, we don't really have flash support
flashAvailable = !ListUtils.listElementsEqualTo(
supportedFlashModes, Camera.Parameters.FLASH_MODE_OFF);
}
/*
* flash.info.available
*/
m.set(FLASH_INFO_AVAILABLE, flashAvailable);
}
private static void mapJpeg(CameraMetadataNative m, Camera.Parameters p) {
List<Camera.Size> thumbnailSizes = p.getSupportedJpegThumbnailSizes();
if (thumbnailSizes != null) {
Size[] sizes = convertSizeListToArray(thumbnailSizes);
Arrays.sort(sizes, new android.hardware.camera2.utils.SizeAreaComparator());
m.set(JPEG_AVAILABLE_THUMBNAIL_SIZES, sizes);
}
}
private static void mapRequest(CameraMetadataNative m, Parameters p) {
/*
* request.availableCapabilities
*/
int[] capabilities = { REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE };
m.set(REQUEST_AVAILABLE_CAPABILITIES, capabilities);
/*
* request.availableCharacteristicsKeys
*/
{
// TODO: check if the underlying key is supported before listing a key as available
// Note: We only list public keys. Native HALs should list ALL keys regardless of visibility.
Key<?> availableKeys[] = new Key<?>[] {
CameraCharacteristics.COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES ,
CameraCharacteristics.CONTROL_AE_AVAILABLE_ANTIBANDING_MODES ,
CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES ,
CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES ,
CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE ,
CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP ,
CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE ,
CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES ,
CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS ,
CameraCharacteristics.CONTROL_AVAILABLE_MODES ,
CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES ,
CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES ,
CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES ,
CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE ,
CameraCharacteristics.CONTROL_MAX_REGIONS ,
CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE ,
CameraCharacteristics.FLASH_INFO_AVAILABLE ,
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL ,
CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES ,
CameraCharacteristics.LENS_FACING ,
CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS ,
CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES ,
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES ,
CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_STREAMS ,
CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT ,
CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH ,
CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM ,
// CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP ,
CameraCharacteristics.SCALER_CROPPING_TYPE ,
CameraCharacteristics.SENSOR_AVAILABLE_TEST_PATTERN_MODES ,
CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE ,
CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE ,
CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE ,
CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE ,
CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE ,
CameraCharacteristics.SENSOR_ORIENTATION ,
CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES ,
CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT ,
CameraCharacteristics.SYNC_MAX_LATENCY ,
};
List<Key<?>> characteristicsKeys = new ArrayList<>(Arrays.asList(availableKeys));
/*
* Add the conditional keys
*/
if (m.get(LENS_INFO_MINIMUM_FOCUS_DISTANCE) != null) {
characteristicsKeys.add(LENS_INFO_MINIMUM_FOCUS_DISTANCE);
}
m.set(REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
getTagsForKeys(characteristicsKeys.toArray(new Key<?>[0])));
}
/*
* request.availableRequestKeys
*/
{
CaptureRequest.Key<?> defaultAvailableKeys[] = new CaptureRequest.Key<?>[] {
CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE,
CaptureRequest.CONTROL_AE_ANTIBANDING_MODE,
CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION,
CaptureRequest.CONTROL_AE_LOCK,
CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,
CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_TRIGGER,
CaptureRequest.CONTROL_AWB_LOCK,
CaptureRequest.CONTROL_AWB_MODE,
CaptureRequest.CONTROL_CAPTURE_INTENT,
CaptureRequest.CONTROL_EFFECT_MODE,
CaptureRequest.CONTROL_MODE,
CaptureRequest.CONTROL_SCENE_MODE,
CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE,
CaptureRequest.CONTROL_ZOOM_RATIO,
CaptureRequest.FLASH_MODE,
CaptureRequest.JPEG_GPS_COORDINATES,
CaptureRequest.JPEG_GPS_PROCESSING_METHOD,
CaptureRequest.JPEG_GPS_TIMESTAMP,
CaptureRequest.JPEG_ORIENTATION,
CaptureRequest.JPEG_QUALITY,
CaptureRequest.JPEG_THUMBNAIL_QUALITY,
CaptureRequest.JPEG_THUMBNAIL_SIZE,
CaptureRequest.LENS_FOCAL_LENGTH,
CaptureRequest.NOISE_REDUCTION_MODE,
CaptureRequest.SCALER_CROP_REGION,
CaptureRequest.STATISTICS_FACE_DETECT_MODE,
};
ArrayList<CaptureRequest.Key<?>> availableKeys =
new ArrayList<CaptureRequest.Key<?>>(Arrays.asList(defaultAvailableKeys));
if (p.getMaxNumMeteringAreas() > 0) {
availableKeys.add(CaptureRequest.CONTROL_AE_REGIONS);
}
if (p.getMaxNumFocusAreas() > 0) {
availableKeys.add(CaptureRequest.CONTROL_AF_REGIONS);
}
CaptureRequest.Key<?> availableRequestKeys[] =
new CaptureRequest.Key<?>[availableKeys.size()];
availableKeys.toArray(availableRequestKeys);
m.set(REQUEST_AVAILABLE_REQUEST_KEYS, getTagsForKeys(availableRequestKeys));
}
/*
* request.availableResultKeys
*/
{
CaptureResult.Key<?> defaultAvailableKeys[] = new CaptureResult.Key<?>[] {
CaptureResult.COLOR_CORRECTION_ABERRATION_MODE ,
CaptureResult.CONTROL_AE_ANTIBANDING_MODE ,
CaptureResult.CONTROL_AE_EXPOSURE_COMPENSATION ,
CaptureResult.CONTROL_AE_LOCK ,
CaptureResult.CONTROL_AE_MODE ,
CaptureResult.CONTROL_AF_MODE ,
CaptureResult.CONTROL_AF_STATE ,
CaptureResult.CONTROL_AWB_MODE ,
CaptureResult.CONTROL_AWB_LOCK ,
CaptureResult.CONTROL_MODE ,
CaptureResult.CONTROL_ZOOM_RATIO ,
CaptureResult.FLASH_MODE ,
CaptureResult.JPEG_GPS_COORDINATES ,
CaptureResult.JPEG_GPS_PROCESSING_METHOD ,
CaptureResult.JPEG_GPS_TIMESTAMP ,
CaptureResult.JPEG_ORIENTATION ,
CaptureResult.JPEG_QUALITY ,
CaptureResult.JPEG_THUMBNAIL_QUALITY ,
CaptureResult.LENS_FOCAL_LENGTH ,
CaptureResult.NOISE_REDUCTION_MODE ,
CaptureResult.REQUEST_PIPELINE_DEPTH ,
CaptureResult.SCALER_CROP_REGION ,
CaptureResult.SENSOR_TIMESTAMP ,
CaptureResult.STATISTICS_FACE_DETECT_MODE ,
// CaptureResult.STATISTICS_FACES ,
};
List<CaptureResult.Key<?>> availableKeys =
new ArrayList<CaptureResult.Key<?>>(Arrays.asList(defaultAvailableKeys));
if (p.getMaxNumMeteringAreas() > 0) {
availableKeys.add(CaptureResult.CONTROL_AE_REGIONS);
}
if (p.getMaxNumFocusAreas() > 0) {
availableKeys.add(CaptureResult.CONTROL_AF_REGIONS);
}
CaptureResult.Key<?> availableResultKeys[] =
new CaptureResult.Key<?>[availableKeys.size()];
availableKeys.toArray(availableResultKeys);
m.set(REQUEST_AVAILABLE_RESULT_KEYS, getTagsForKeys(availableResultKeys));
}
/*
* request.maxNumOutputStreams
*/
int[] outputStreams = {
/* RAW */
REQUEST_MAX_NUM_OUTPUT_STREAMS_COUNT_RAW,
/* Processed & Not-Stalling */
REQUEST_MAX_NUM_OUTPUT_STREAMS_COUNT_PROC,
/* Processed & Stalling */
REQUEST_MAX_NUM_OUTPUT_STREAMS_COUNT_PROC_STALL,
};
m.set(REQUEST_MAX_NUM_OUTPUT_STREAMS, outputStreams);
/*
* request.maxNumInputStreams
*/
m.set(REQUEST_MAX_NUM_INPUT_STREAMS, REQUEST_MAX_NUM_INPUT_STREAMS_COUNT);
/*
* request.partialResultCount
*/
m.set(REQUEST_PARTIAL_RESULT_COUNT, 1); // No partial results supported
/*
* request.pipelineMaxDepth
*/
m.set(REQUEST_PIPELINE_MAX_DEPTH,
(byte)(REQUEST_PIPELINE_MAX_DEPTH_HAL1 + REQUEST_PIPELINE_MAX_DEPTH_OURS));
}
private static void mapScaler(CameraMetadataNative m, Parameters p) {
/*
* control.zoomRatioRange
*/
Range<Float> zoomRatioRange = new Range<Float>(1.0f, ParameterUtils.getMaxZoomRatio(p));
m.set(CONTROL_ZOOM_RATIO_RANGE, zoomRatioRange);
/*
* scaler.availableMaxDigitalZoom
*/
m.set(SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, ParameterUtils.getMaxZoomRatio(p));
/*
* scaler.croppingType = CENTER_ONLY
*/
m.set(SCALER_CROPPING_TYPE, SCALER_CROPPING_TYPE_CENTER_ONLY);
}
private static void mapSensor(CameraMetadataNative m, Parameters p) {
// Use the largest jpeg size (by area) for both active array and pixel array
Size largestJpegSize = getLargestSupportedJpegSizeByArea(p);
/*
* sensor.info.activeArraySize, and preCorrectionActiveArraySize
*/
{
Rect activeArrayRect = ParamsUtils.createRect(largestJpegSize);
m.set(SENSOR_INFO_ACTIVE_ARRAY_SIZE, activeArrayRect);
m.set(SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, activeArrayRect);
}
/*
* sensor.availableTestPatternModes
*/
{
// Only "OFF" test pattern mode is available
m.set(SENSOR_AVAILABLE_TEST_PATTERN_MODES, new int[] { SENSOR_TEST_PATTERN_MODE_OFF });
}
/*
* sensor.info.pixelArraySize
*/
m.set(SENSOR_INFO_PIXEL_ARRAY_SIZE, largestJpegSize);
/*
* sensor.info.physicalSize
*/
{
/*
* Assume focal length is at infinity focus and that the lens is rectilinear.
*/
float focalLength = p.getFocalLength(); // in mm
double angleHor = p.getHorizontalViewAngle() * Math.PI / 180; // to radians
double angleVer = p.getVerticalViewAngle() * Math.PI / 180; // to radians
float height = (float)Math.abs(2 * focalLength * Math.tan(angleVer / 2));
float width = (float)Math.abs(2 * focalLength * Math.tan(angleHor / 2));
m.set(SENSOR_INFO_PHYSICAL_SIZE, new SizeF(width, height)); // in mm
}
/*
* sensor.info.timestampSource
*/
{
m.set(SENSOR_INFO_TIMESTAMP_SOURCE, SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN);
}
}
private static void mapStatistics(CameraMetadataNative m, Parameters p) {
/*
* statistics.info.availableFaceDetectModes
*/
int[] fdModes;
if (p.getMaxNumDetectedFaces() > 0) {
fdModes = new int[] {
STATISTICS_FACE_DETECT_MODE_OFF,
STATISTICS_FACE_DETECT_MODE_SIMPLE
// FULL is never-listed, since we have no way to query it statically
};
} else {
fdModes = new int[] {
STATISTICS_FACE_DETECT_MODE_OFF
};
}
m.set(STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES, fdModes);
/*
* statistics.info.maxFaceCount
*/
m.set(STATISTICS_INFO_MAX_FACE_COUNT, p.getMaxNumDetectedFaces());
}
private static void mapSync(CameraMetadataNative m, Parameters p) {
/*
* sync.maxLatency
*/
m.set(SYNC_MAX_LATENCY, SYNC_MAX_LATENCY_UNKNOWN);
}
private static void appendStreamConfig(
ArrayList<StreamConfiguration> configs, int format, List<Camera.Size> sizes) {
for (Camera.Size size : sizes) {
StreamConfiguration config =
new StreamConfiguration(format, size.width, size.height, /*input*/false);
configs.add(config);
}
}
private final static String[] sLegacySceneModes = {
Parameters.SCENE_MODE_AUTO,
Parameters.SCENE_MODE_ACTION,
Parameters.SCENE_MODE_PORTRAIT,
Parameters.SCENE_MODE_LANDSCAPE,
Parameters.SCENE_MODE_NIGHT,
Parameters.SCENE_MODE_NIGHT_PORTRAIT,
Parameters.SCENE_MODE_THEATRE,
Parameters.SCENE_MODE_BEACH,
Parameters.SCENE_MODE_SNOW,
Parameters.SCENE_MODE_SUNSET,
Parameters.SCENE_MODE_STEADYPHOTO,
Parameters.SCENE_MODE_FIREWORKS,
Parameters.SCENE_MODE_SPORTS,
Parameters.SCENE_MODE_PARTY,
Parameters.SCENE_MODE_CANDLELIGHT,
Parameters.SCENE_MODE_BARCODE,
Parameters.SCENE_MODE_HDR,
};
private final static int[] sSceneModes = {
CameraCharacteristics.CONTROL_SCENE_MODE_DISABLED,
CameraCharacteristics.CONTROL_SCENE_MODE_ACTION,
CameraCharacteristics.CONTROL_SCENE_MODE_PORTRAIT,
CameraCharacteristics.CONTROL_SCENE_MODE_LANDSCAPE,
CameraCharacteristics.CONTROL_SCENE_MODE_NIGHT,
CameraCharacteristics.CONTROL_SCENE_MODE_NIGHT_PORTRAIT,
CameraCharacteristics.CONTROL_SCENE_MODE_THEATRE,
CameraCharacteristics.CONTROL_SCENE_MODE_BEACH,
CameraCharacteristics.CONTROL_SCENE_MODE_SNOW,
CameraCharacteristics.CONTROL_SCENE_MODE_SUNSET,
CameraCharacteristics.CONTROL_SCENE_MODE_STEADYPHOTO,
CameraCharacteristics.CONTROL_SCENE_MODE_FIREWORKS,
CameraCharacteristics.CONTROL_SCENE_MODE_SPORTS,
CameraCharacteristics.CONTROL_SCENE_MODE_PARTY,
CameraCharacteristics.CONTROL_SCENE_MODE_CANDLELIGHT,
CameraCharacteristics.CONTROL_SCENE_MODE_BARCODE,
CameraCharacteristics.CONTROL_SCENE_MODE_HDR,
};
static int convertSceneModeFromLegacy(String mode) {
if (mode == null) {
return CameraCharacteristics.CONTROL_SCENE_MODE_DISABLED;
}
int index = ArrayUtils.getArrayIndex(sLegacySceneModes, mode);
if (index < 0) {
return UNKNOWN_MODE;
}
return sSceneModes[index];
}
static String convertSceneModeToLegacy(int mode) {
if (mode == CONTROL_SCENE_MODE_FACE_PRIORITY) {
// OK: Let LegacyFaceDetectMapper handle turning face detection on/off
return Parameters.SCENE_MODE_AUTO;
}
int index = ArrayUtils.getArrayIndex(sSceneModes, mode);
if (index < 0) {
return null;
}
return sLegacySceneModes[index];
}
private final static String[] sLegacyEffectMode = {
Parameters.EFFECT_NONE,
Parameters.EFFECT_MONO,
Parameters.EFFECT_NEGATIVE,
Parameters.EFFECT_SOLARIZE,
Parameters.EFFECT_SEPIA,
Parameters.EFFECT_POSTERIZE,
Parameters.EFFECT_WHITEBOARD,
Parameters.EFFECT_BLACKBOARD,
Parameters.EFFECT_AQUA,
};
private final static int[] sEffectModes = {
CameraCharacteristics.CONTROL_EFFECT_MODE_OFF,
CameraCharacteristics.CONTROL_EFFECT_MODE_MONO,
CameraCharacteristics.CONTROL_EFFECT_MODE_NEGATIVE,
CameraCharacteristics.CONTROL_EFFECT_MODE_SOLARIZE,
CameraCharacteristics.CONTROL_EFFECT_MODE_SEPIA,
CameraCharacteristics.CONTROL_EFFECT_MODE_POSTERIZE,
CameraCharacteristics.CONTROL_EFFECT_MODE_WHITEBOARD,
CameraCharacteristics.CONTROL_EFFECT_MODE_BLACKBOARD,
CameraCharacteristics.CONTROL_EFFECT_MODE_AQUA,
};
static int convertEffectModeFromLegacy(String mode) {
if (mode == null) {
return CameraCharacteristics.CONTROL_EFFECT_MODE_OFF;
}
int index = ArrayUtils.getArrayIndex(sLegacyEffectMode, mode);
if (index < 0) {
return UNKNOWN_MODE;
}
return sEffectModes[index];
}
static String convertEffectModeToLegacy(int mode) {
int index = ArrayUtils.getArrayIndex(sEffectModes, mode);
if (index < 0) {
return null;
}
return sLegacyEffectMode[index];
}
/**
* Convert the ae antibanding mode from api1 into api2.
*
* @param mode the api1 mode, {@code null} is allowed and will return {@code -1}.
*
* @return The api2 value, or {@code -1} by default if conversion failed
*/
private static int convertAntiBandingMode(String mode) {
if (mode == null) {
return -1;
}
switch (mode) {
case Camera.Parameters.ANTIBANDING_OFF: {
return CONTROL_AE_ANTIBANDING_MODE_OFF;
}
case Camera.Parameters.ANTIBANDING_50HZ: {
return CONTROL_AE_ANTIBANDING_MODE_50HZ;
}
case Camera.Parameters.ANTIBANDING_60HZ: {
return CONTROL_AE_ANTIBANDING_MODE_60HZ;
}
case Camera.Parameters.ANTIBANDING_AUTO: {
return CONTROL_AE_ANTIBANDING_MODE_AUTO;
}
default: {
Log.w(TAG, "convertAntiBandingMode - Unknown antibanding mode " + mode);
return -1;
}
}
}
/**
* Convert the ae antibanding mode from api1 into api2.
*
* @param mode the api1 mode, {@code null} is allowed and will return {@code MODE_OFF}.
*
* @return The api2 value, or {@code MODE_OFF} by default if conversion failed
*/
static int convertAntiBandingModeOrDefault(String mode) {
int antiBandingMode = convertAntiBandingMode(mode);
if (antiBandingMode == -1) {
return CONTROL_AE_ANTIBANDING_MODE_OFF;
}
return antiBandingMode;
}
private static int[] convertAeFpsRangeToLegacy(Range<Integer> fpsRange) {
int[] legacyFps = new int[2];
legacyFps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] = fpsRange.getLower();
legacyFps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] = fpsRange.getUpper();
return legacyFps;
}
/**
* Return the stall duration for a given output jpeg size in nanoseconds.
*
* <p>An 8mp image is chosen to have a stall duration of 0.8 seconds.</p>
*/
private static long calculateJpegStallDuration(Camera.Size size) {
long baseDuration = APPROXIMATE_CAPTURE_DELAY_MS * NS_PER_MS; // 200ms for capture
long area = size.width * (long) size.height;
long stallPerArea = APPROXIMATE_JPEG_ENCODE_TIME_MS * NS_PER_MS /
APPROXIMATE_SENSOR_AREA_PX; // 600ms stall for 8mp
return baseDuration + area * stallPerArea;
}
/**
* Set the legacy parameters using the {@link LegacyRequest legacy request}.
*
* <p>The legacy request's parameters are changed as a side effect of calling this
* method.</p>
*
* @param request a non-{@code null} legacy request
*/
public static void convertRequestMetadata(LegacyRequest request) {
LegacyRequestMapper.convertRequestMetadata(request);
}
private static final int[] sAllowedTemplates = {
CameraDevice.TEMPLATE_PREVIEW,
CameraDevice.TEMPLATE_STILL_CAPTURE,
CameraDevice.TEMPLATE_RECORD,
// Disallowed templates in legacy mode:
// CameraDevice.TEMPLATE_VIDEO_SNAPSHOT,
// CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG,
// CameraDevice.TEMPLATE_MANUAL
};
/**
* Create a request template
*
* @param c a non-{@code null} camera characteristics for this camera
* @param templateId a non-negative template ID
*
* @return a non-{@code null} request template
*
* @throws IllegalArgumentException if {@code templateId} was invalid
*
* @see android.hardware.camera2.CameraDevice#TEMPLATE_MANUAL
*/
public static CameraMetadataNative createRequestTemplate(
CameraCharacteristics c, int templateId) {
if (!ArrayUtils.contains(sAllowedTemplates, templateId)) {
throw new IllegalArgumentException("templateId out of range");
}
CameraMetadataNative m = new CameraMetadataNative();
/*
* NOTE: If adding new code here and it needs to query the static info,
* query the camera characteristics, so we can reuse this for api2 code later
* to create our own templates in the framework
*/
/*
* control.*
*/
// control.awbMode
m.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_AUTO);
// AWB is always unconditionally available in API1 devices
// control.aeAntibandingMode
m.set(CaptureRequest.CONTROL_AE_ANTIBANDING_MODE, CONTROL_AE_ANTIBANDING_MODE_AUTO);
// control.aeExposureCompensation
m.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, 0);
// control.aeLock
m.set(CaptureRequest.CONTROL_AE_LOCK, false);
// control.aePrecaptureTrigger
m.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
// control.afTrigger
m.set(CaptureRequest.CONTROL_AF_TRIGGER, CONTROL_AF_TRIGGER_IDLE);
// control.awbMode
m.set(CaptureRequest.CONTROL_AWB_MODE, CONTROL_AWB_MODE_AUTO);
// control.awbLock
m.set(CaptureRequest.CONTROL_AWB_LOCK, false);
// control.aeRegions, control.awbRegions, control.afRegions
{
Rect activeArray = c.get(SENSOR_INFO_ACTIVE_ARRAY_SIZE);
MeteringRectangle[] activeRegions = new MeteringRectangle[] {
new MeteringRectangle(/*x*/0, /*y*/0, /*width*/activeArray.width() - 1,
/*height*/activeArray.height() - 1,/*weight*/0)};
m.set(CaptureRequest.CONTROL_AE_REGIONS, activeRegions);
m.set(CaptureRequest.CONTROL_AWB_REGIONS, activeRegions);
m.set(CaptureRequest.CONTROL_AF_REGIONS, activeRegions);
}
// control.captureIntent
{
int captureIntent;
switch (templateId) {
case CameraDevice.TEMPLATE_PREVIEW:
captureIntent = CONTROL_CAPTURE_INTENT_PREVIEW;
break;
case CameraDevice.TEMPLATE_STILL_CAPTURE:
captureIntent = CONTROL_CAPTURE_INTENT_STILL_CAPTURE;
break;
case CameraDevice.TEMPLATE_RECORD:
captureIntent = CONTROL_CAPTURE_INTENT_VIDEO_RECORD;
break;
default:
// Can't get anything else since it's guarded by the IAE check
throw new AssertionError("Impossible; keep in sync with sAllowedTemplates");
}
m.set(CaptureRequest.CONTROL_CAPTURE_INTENT, captureIntent);
}
// control.aeMode
m.set(CaptureRequest.CONTROL_AE_MODE, CameraMetadata.CONTROL_AE_MODE_ON);
// AE is always unconditionally available in API1 devices
// control.mode
m.set(CaptureRequest.CONTROL_MODE, CONTROL_MODE_AUTO);
// control.afMode
{
Float minimumFocusDistance = c.get(LENS_INFO_MINIMUM_FOCUS_DISTANCE);
int afMode;
if (minimumFocusDistance != null &&
minimumFocusDistance == LENS_INFO_MINIMUM_FOCUS_DISTANCE_FIXED_FOCUS) {
// Cannot control auto-focus with fixed-focus cameras
afMode = CameraMetadata.CONTROL_AF_MODE_OFF;
} else {
// If a minimum focus distance is reported; the camera must have AF
afMode = CameraMetadata.CONTROL_AF_MODE_AUTO;
if (templateId == CameraDevice.TEMPLATE_RECORD ||
templateId == CameraDevice.TEMPLATE_VIDEO_SNAPSHOT) {
if (ArrayUtils.contains(c.get(CONTROL_AF_AVAILABLE_MODES),
CONTROL_AF_MODE_CONTINUOUS_VIDEO)) {
afMode = CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO;
}
} else if (templateId == CameraDevice.TEMPLATE_PREVIEW ||
templateId == CameraDevice.TEMPLATE_STILL_CAPTURE) {
if (ArrayUtils.contains(c.get(CONTROL_AF_AVAILABLE_MODES),
CONTROL_AF_MODE_CONTINUOUS_PICTURE)) {
afMode = CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE;
}
}
}
if (DEBUG) {
Log.v(TAG, "createRequestTemplate (templateId=" + templateId + ")," +
" afMode=" + afMode + ", minimumFocusDistance=" + minimumFocusDistance);
}
m.set(CaptureRequest.CONTROL_AF_MODE, afMode);
}
{
// control.aeTargetFpsRange
Range<Integer>[] availableFpsRange = c.
get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
// Pick FPS range with highest max value, tiebreak on higher min value
Range<Integer> bestRange = availableFpsRange[0];
for (Range<Integer> r : availableFpsRange) {
if (bestRange.getUpper() < r.getUpper()) {
bestRange = r;
} else if (bestRange.getUpper() == r.getUpper() &&
bestRange.getLower() < r.getLower()) {
bestRange = r;
}
}
m.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, bestRange);
}
// control.sceneMode -- DISABLED is always available
m.set(CaptureRequest.CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_DISABLED);
// control.zoomRatio -- 1.0
m.set(CaptureRequest.CONTROL_ZOOM_RATIO, 1.0f);
/*
* statistics.*
*/
// statistics.faceDetectMode
m.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE, STATISTICS_FACE_DETECT_MODE_OFF);
/*
* flash.*
*/
// flash.mode
m.set(CaptureRequest.FLASH_MODE, FLASH_MODE_OFF);
/*
* noiseReduction.*
*/
if (templateId == CameraDevice.TEMPLATE_STILL_CAPTURE) {
m.set(CaptureRequest.NOISE_REDUCTION_MODE, NOISE_REDUCTION_MODE_HIGH_QUALITY);
} else {
m.set(CaptureRequest.NOISE_REDUCTION_MODE, NOISE_REDUCTION_MODE_FAST);
}
/*
* colorCorrection.*
*/
if (templateId == CameraDevice.TEMPLATE_STILL_CAPTURE) {
m.set(CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE,
COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY);
} else {
m.set(CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE,
COLOR_CORRECTION_ABERRATION_MODE_FAST);
}
/*
* lens.*
*/
// lens.focalLength
m.set(CaptureRequest.LENS_FOCAL_LENGTH,
c.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)[0]);
/*
* jpeg.*
*/
// jpeg.thumbnailSize - set smallest non-zero size if possible
Size[] sizes = c.get(CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES);
m.set(CaptureRequest.JPEG_THUMBNAIL_SIZE, (sizes.length > 1) ? sizes[1] : sizes[0]);
// TODO: map other request template values
return m;
}
private static int[] getTagsForKeys(Key<?>[] keys) {
int[] tags = new int[keys.length];
for (int i = 0; i < keys.length; ++i) {
tags[i] = keys[i].getNativeKey().getTag();
}
return tags;
}
private static int[] getTagsForKeys(CaptureRequest.Key<?>[] keys) {
int[] tags = new int[keys.length];
for (int i = 0; i < keys.length; ++i) {
tags[i] = keys[i].getNativeKey().getTag();
}
return tags;
}
private static int[] getTagsForKeys(CaptureResult.Key<?>[] keys) {
int[] tags = new int[keys.length];
for (int i = 0; i < keys.length; ++i) {
tags[i] = keys[i].getNativeKey().getTag();
}
return tags;
}
/**
* Convert the requested AF mode into its equivalent supported parameter.
*
* @param mode {@code CONTROL_AF_MODE}
* @param supportedFocusModes list of camera1's supported focus modes
* @return the stringified af mode, or {@code null} if its not supported
*/
static String convertAfModeToLegacy(int mode, List<String> supportedFocusModes) {
if (supportedFocusModes == null || supportedFocusModes.isEmpty()) {
Log.w(TAG, "No focus modes supported; API1 bug");
return null;
}
String param = null;
switch (mode) {
case CONTROL_AF_MODE_AUTO:
param = Parameters.FOCUS_MODE_AUTO;
break;
case CONTROL_AF_MODE_CONTINUOUS_PICTURE:
param = Parameters.FOCUS_MODE_CONTINUOUS_PICTURE;
break;
case CONTROL_AF_MODE_CONTINUOUS_VIDEO:
param = Parameters.FOCUS_MODE_CONTINUOUS_VIDEO;
break;
case CONTROL_AF_MODE_EDOF:
param = Parameters.FOCUS_MODE_EDOF;
break;
case CONTROL_AF_MODE_MACRO:
param = Parameters.FOCUS_MODE_MACRO;
break;
case CONTROL_AF_MODE_OFF:
if (supportedFocusModes.contains(Parameters.FOCUS_MODE_FIXED)) {
param = Parameters.FOCUS_MODE_FIXED;
} else {
param = Parameters.FOCUS_MODE_INFINITY;
}
}
if (!supportedFocusModes.contains(param)) {
// Weed out bad user input by setting to the first arbitrary focus mode
String defaultMode = supportedFocusModes.get(0);
Log.w(TAG,
String.format(
"convertAfModeToLegacy - ignoring unsupported mode %d, " +
"defaulting to %s", mode, defaultMode));
param = defaultMode;
}
return param;
}
}