2 * Copyright (C) 2014 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package android.hardware.camera2.legacy;
19 import android.graphics.Rect;
20 import android.hardware.Camera;
21 import android.hardware.Camera.Parameters;
22 import android.hardware.camera2.CameraCharacteristics;
23 import android.hardware.camera2.CaptureRequest;
24 import android.hardware.camera2.params.MeteringRectangle;
25 import android.hardware.camera2.utils.ListUtils;
26 import android.hardware.camera2.utils.ParamsUtils;
27 import android.location.Location;
28 import android.util.Log;
29 import android.util.Range;
30 import android.util.Size;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.List;
35 import java.util.Objects;
37 import static com.android.internal.util.Preconditions.*;
38 import static android.hardware.camera2.CaptureRequest.*;
41 * Provide legacy-specific implementations of camera2 CaptureRequest for legacy devices.
43 @SuppressWarnings("deprecation")
44 public class LegacyRequestMapper {
45 private static final String TAG = "LegacyRequestMapper";
46 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
48 /** Default quality for android.jpeg.quality, android.jpeg.thumbnailQuality */
49 private static final byte DEFAULT_JPEG_QUALITY = 85;
52 * Set the legacy parameters using the {@link LegacyRequest legacy request}.
54 * <p>The legacy request's parameters are changed as a side effect of calling this
57 * @param legacyRequest a non-{@code null} legacy request
59 public static void convertRequestMetadata(LegacyRequest legacyRequest) {
60 CameraCharacteristics characteristics = legacyRequest.characteristics;
61 CaptureRequest request = legacyRequest.captureRequest;
62 Size previewSize = legacyRequest.previewSize;
63 Camera.Parameters params = legacyRequest.parameters;
65 Rect activeArray = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
70 ParameterUtils.ZoomData zoomData;
72 zoomData = ParameterUtils.convertScalerCropRegion(activeArray,
73 request.get(SCALER_CROP_REGION),
77 if (params.isZoomSupported()) {
78 params.setZoom(zoomData.zoomIndex);
80 Log.v(TAG, "convertRequestToMetadata - zoom is not supported");
87 // colorCorrection.aberrationMode
89 int aberrationMode = ParamsUtils.getOrDefault(request,
90 COLOR_CORRECTION_ABERRATION_MODE,
91 /*defaultValue*/COLOR_CORRECTION_ABERRATION_MODE_FAST);
93 if (aberrationMode != COLOR_CORRECTION_ABERRATION_MODE_FAST) {
94 Log.w(TAG, "convertRequestToMetadata - Ignoring unsupported " +
95 "colorCorrection.aberrationMode = " + aberrationMode);
102 // control.aeAntibandingMode
105 Integer antiBandingMode = request.get(CONTROL_AE_ANTIBANDING_MODE);
106 if (antiBandingMode != null) {
107 legacyMode = convertAeAntiBandingModeToLegacy(antiBandingMode);
109 legacyMode = ListUtils.listSelectFirstFrom(params.getSupportedAntibanding(),
111 Parameters.ANTIBANDING_AUTO,
112 Parameters.ANTIBANDING_OFF,
113 Parameters.ANTIBANDING_50HZ,
114 Parameters.ANTIBANDING_60HZ,
118 if (legacyMode != null) {
119 params.setAntibanding(legacyMode);
124 * control.aeRegions, afRegions
129 // Use aeRegions if available, fall back to using awbRegions if present
130 MeteringRectangle[] aeRegions = request.get(CONTROL_AE_REGIONS);
131 if (request.get(CONTROL_AWB_REGIONS) != null) {
132 Log.w(TAG, "convertRequestMetadata - control.awbRegions setting is not " +
133 "supported, ignoring value");
135 int maxNumMeteringAreas = params.getMaxNumMeteringAreas();
136 List<Camera.Area> meteringAreaList = convertMeteringRegionsToLegacy(
137 activeArray, zoomData, aeRegions, maxNumMeteringAreas,
140 // WAR: for b/17252693, some devices can't handle params.setFocusAreas(null).
141 if (maxNumMeteringAreas > 0) {
142 params.setMeteringAreas(meteringAreaList);
148 MeteringRectangle[] afRegions = request.get(CONTROL_AF_REGIONS);
149 int maxNumFocusAreas = params.getMaxNumFocusAreas();
150 List<Camera.Area> focusAreaList = convertMeteringRegionsToLegacy(
151 activeArray, zoomData, afRegions, maxNumFocusAreas,
154 // WAR: for b/17252693, some devices can't handle params.setFocusAreas(null).
155 if (maxNumFocusAreas > 0) {
156 params.setFocusAreas(focusAreaList);
161 // control.aeTargetFpsRange
162 Range<Integer> aeFpsRange = request.get(CONTROL_AE_TARGET_FPS_RANGE);
163 if (aeFpsRange != null) {
164 int[] legacyFps = convertAeFpsRangeToLegacy(aeFpsRange);
166 // TODO - Should we enforce that all HAL1 devices must include (30, 30) FPS range?
167 boolean supported = false;
168 for(int[] range : params.getSupportedPreviewFpsRange()) {
169 if (legacyFps[0] == range[0] && legacyFps[1] == range[1]) {
175 params.setPreviewFpsRange(legacyFps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
176 legacyFps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
178 Log.w(TAG, "Unsupported FPS range set [" + legacyFps[0] + "," + legacyFps[1] + "]");
186 // control.aeExposureCompensation
188 Range<Integer> compensationRange =
189 characteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE);
190 int compensation = ParamsUtils.getOrDefault(request,
191 CONTROL_AE_EXPOSURE_COMPENSATION,
194 if (!compensationRange.contains(compensation)) {
196 "convertRequestMetadata - control.aeExposureCompensation " +
197 "is out of range, ignoring value");
201 params.setExposureCompensation(compensation);
206 Boolean aeLock = getIfSupported(request, CONTROL_AE_LOCK, /*defaultValue*/false,
207 params.isAutoExposureLockSupported(),
208 /*allowedValue*/false);
210 if (aeLock != null) {
211 params.setAutoExposureLock(aeLock);
215 Log.v(TAG, "convertRequestToMetadata - control.aeLock set to " + aeLock);
218 // TODO: Don't add control.aeLock to availableRequestKeys if it's not supported
221 // control.aeMode, flash.mode
222 mapAeAndFlashMode(request, /*out*/params);
226 int afMode = ParamsUtils.getOrDefault(request, CONTROL_AF_MODE,
227 /*defaultValue*/CONTROL_AF_MODE_OFF);
228 String focusMode = LegacyMetadataMapper.convertAfModeToLegacy(afMode,
229 params.getSupportedFocusModes());
231 if (focusMode != null) {
232 params.setFocusMode(focusMode);
236 Log.v(TAG, "convertRequestToMetadata - control.afMode "
237 + afMode + " mapped to " + focusMode);
243 Integer awbMode = getIfSupported(request, CONTROL_AWB_MODE,
244 /*defaultValue*/CONTROL_AWB_MODE_AUTO,
245 params.getSupportedWhiteBalance() != null,
246 /*allowedValue*/CONTROL_AWB_MODE_AUTO);
248 String whiteBalanceMode = null;
249 if (awbMode != null) { // null iff AWB is not supported by camera1 api
250 whiteBalanceMode = convertAwbModeToLegacy(awbMode);
251 params.setWhiteBalance(whiteBalanceMode);
255 Log.v(TAG, "convertRequestToMetadata - control.awbMode "
256 + awbMode + " mapped to " + whiteBalanceMode);
262 Boolean awbLock = getIfSupported(request, CONTROL_AWB_LOCK, /*defaultValue*/false,
263 params.isAutoWhiteBalanceLockSupported(),
264 /*allowedValue*/false);
266 if (awbLock != null) {
267 params.setAutoWhiteBalanceLock(awbLock);
270 // TODO: Don't add control.awbLock to availableRequestKeys if it's not supported
273 // control.captureIntent
275 int captureIntent = ParamsUtils.getOrDefault(request,
276 CONTROL_CAPTURE_INTENT,
277 /*defaultValue*/CONTROL_CAPTURE_INTENT_PREVIEW);
279 captureIntent = filterSupportedCaptureIntent(captureIntent);
281 params.setRecordingHint(
282 captureIntent == CONTROL_CAPTURE_INTENT_VIDEO_RECORD ||
283 captureIntent == CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT);
286 // control.videoStabilizationMode
288 Integer stabMode = getIfSupported(request, CONTROL_VIDEO_STABILIZATION_MODE,
289 /*defaultValue*/CONTROL_VIDEO_STABILIZATION_MODE_OFF,
290 params.isVideoStabilizationSupported(),
291 /*allowedValue*/CONTROL_VIDEO_STABILIZATION_MODE_OFF);
293 if (stabMode != null) {
294 params.setVideoStabilization(stabMode == CONTROL_VIDEO_STABILIZATION_MODE_ON);
298 // lens.focusDistance
300 boolean infinityFocusSupported =
301 ListUtils.listContains(params.getSupportedFocusModes(),
302 Parameters.FOCUS_MODE_INFINITY);
303 Float focusDistance = getIfSupported(request, LENS_FOCUS_DISTANCE,
304 /*defaultValue*/0f, infinityFocusSupported, /*allowedValue*/0f);
306 if (focusDistance == null || focusDistance != 0f) {
308 "convertRequestToMetadata - Ignoring android.lens.focusDistance "
309 + infinityFocusSupported + ", only 0.0f is supported");
313 // control.sceneMode, control.mode
315 // TODO: Map FACE_PRIORITY scene mode to face detection.
317 if (params.getSupportedSceneModes() != null) {
318 int controlMode = ParamsUtils.getOrDefault(request, CONTROL_MODE,
319 /*defaultValue*/CONTROL_MODE_AUTO);
321 switch (controlMode) {
322 case CONTROL_MODE_USE_SCENE_MODE: {
323 int sceneMode = ParamsUtils.getOrDefault(request, CONTROL_SCENE_MODE,
324 /*defaultValue*/CONTROL_SCENE_MODE_DISABLED);
325 String legacySceneMode = LegacyMetadataMapper.
326 convertSceneModeToLegacy(sceneMode);
327 if (legacySceneMode != null) {
328 modeToSet = legacySceneMode;
330 modeToSet = Parameters.SCENE_MODE_AUTO;
331 Log.w(TAG, "Skipping unknown requested scene mode: " + sceneMode);
335 case CONTROL_MODE_AUTO: {
336 modeToSet = Parameters.SCENE_MODE_AUTO;
340 Log.w(TAG, "Control mode " + controlMode +
341 " is unsupported, defaulting to AUTO");
342 modeToSet = Parameters.SCENE_MODE_AUTO;
345 params.setSceneMode(modeToSet);
349 // control.effectMode
351 if (params.getSupportedColorEffects() != null) {
352 int effectMode = ParamsUtils.getOrDefault(request, CONTROL_EFFECT_MODE,
353 /*defaultValue*/CONTROL_EFFECT_MODE_OFF);
354 String legacyEffectMode = LegacyMetadataMapper.convertEffectModeToLegacy(effectMode);
355 if (legacyEffectMode != null) {
356 params.setColorEffect(legacyEffectMode);
358 params.setColorEffect(Parameters.EFFECT_NONE);
359 Log.w(TAG, "Skipping unknown requested effect mode: " + effectMode);
368 // sensor.testPattern
370 int testPatternMode = ParamsUtils.getOrDefault(request, SENSOR_TEST_PATTERN_MODE,
371 /*defaultValue*/SENSOR_TEST_PATTERN_MODE_OFF);
372 if (testPatternMode != SENSOR_TEST_PATTERN_MODE_OFF) {
373 Log.w(TAG, "convertRequestToMetadata - ignoring sensor.testPatternMode "
374 + testPatternMode + "; only OFF is supported");
384 Location location = request.get(JPEG_GPS_LOCATION);
385 if (location != null) {
386 if (checkForCompleteGpsData(location)) {
387 params.setGpsAltitude(location.getAltitude());
388 params.setGpsLatitude(location.getLatitude());
389 params.setGpsLongitude(location.getLongitude());
390 params.setGpsProcessingMethod(location.getProvider().toUpperCase());
391 params.setGpsTimestamp(location.getTime());
393 Log.w(TAG, "Incomplete GPS parameters provided in location " + location);
396 params.removeGpsData();
402 int orientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
403 params.setRotation(ParamsUtils.getOrDefault(request, JPEG_ORIENTATION, orientation));
408 params.setJpegQuality(0xFF & ParamsUtils.getOrDefault(request, JPEG_QUALITY,
409 DEFAULT_JPEG_QUALITY));
412 // jpeg.thumbnailQuality
414 params.setJpegThumbnailQuality(0xFF & ParamsUtils.getOrDefault(request,
415 JPEG_THUMBNAIL_QUALITY, DEFAULT_JPEG_QUALITY));
418 // jpeg.thumbnailSize
420 List<Camera.Size> sizes = params.getSupportedJpegThumbnailSizes();
422 if (sizes != null && sizes.size() > 0) {
423 Size s = request.get(JPEG_THUMBNAIL_SIZE);
424 boolean invalidSize = (s == null) ? false : !ParameterUtils.containsSize(sizes,
425 s.getWidth(), s.getHeight());
427 Log.w(TAG, "Invalid JPEG thumbnail size set " + s + ", skipping thumbnail...");
429 if (s == null || invalidSize) {
430 // (0,0) = "no thumbnail" in Camera API 1
431 params.setJpegThumbnailSize(/*width*/0, /*height*/0);
433 params.setJpegThumbnailSize(s.getWidth(), s.getHeight());
441 // noiseReduction.mode
443 int mode = ParamsUtils.getOrDefault(request,
444 NOISE_REDUCTION_MODE,
445 /*defaultValue*/NOISE_REDUCTION_MODE_FAST);
447 if (mode != NOISE_REDUCTION_MODE_FAST) {
448 Log.w(TAG, "convertRequestToMetadata - Ignoring unsupported " +
449 "noiseReduction.mode = " + mode);
454 private static boolean checkForCompleteGpsData(Location location) {
455 return location != null && location.getProvider() != null && location.getTime() != 0;
458 static int filterSupportedCaptureIntent(int captureIntent) {
459 switch (captureIntent) {
460 case CONTROL_CAPTURE_INTENT_CUSTOM:
461 case CONTROL_CAPTURE_INTENT_PREVIEW:
462 case CONTROL_CAPTURE_INTENT_STILL_CAPTURE:
463 case CONTROL_CAPTURE_INTENT_VIDEO_RECORD:
464 case CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT:
466 case CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG:
467 case CONTROL_CAPTURE_INTENT_MANUAL:
468 captureIntent = CONTROL_CAPTURE_INTENT_PREVIEW;
469 Log.w(TAG, "Unsupported control.captureIntent value " + captureIntent
470 + "; default to PREVIEW");
472 captureIntent = CONTROL_CAPTURE_INTENT_PREVIEW;
473 Log.w(TAG, "Unknown control.captureIntent value " + captureIntent
474 + "; default to PREVIEW");
477 return captureIntent;
480 private static List<Camera.Area> convertMeteringRegionsToLegacy(
481 Rect activeArray, ParameterUtils.ZoomData zoomData,
482 MeteringRectangle[] meteringRegions, int maxNumMeteringAreas, String regionName) {
483 if (meteringRegions == null || maxNumMeteringAreas <= 0) {
484 if (maxNumMeteringAreas > 0) {
485 return Arrays.asList(ParameterUtils.CAMERA_AREA_DEFAULT);
491 // Add all non-zero weight regions to the list
492 List<MeteringRectangle> meteringRectangleList = new ArrayList<>();
493 for (MeteringRectangle rect : meteringRegions) {
494 if (rect.getMeteringWeight() != MeteringRectangle.METERING_WEIGHT_DONT_CARE) {
495 meteringRectangleList.add(rect);
499 // Ignore any regions beyond our maximum supported count
500 int countMeteringAreas =
501 Math.min(maxNumMeteringAreas, meteringRectangleList.size());
502 List<Camera.Area> meteringAreaList = new ArrayList<>(countMeteringAreas);
504 for (int i = 0; i < countMeteringAreas; ++i) {
505 MeteringRectangle rect = meteringRectangleList.get(i);
507 ParameterUtils.MeteringData meteringData =
508 ParameterUtils.convertMeteringRectangleToLegacy(activeArray, rect, zoomData);
509 meteringAreaList.add(meteringData.meteringArea);
512 if (maxNumMeteringAreas < meteringRectangleList.size()) {
514 "convertMeteringRegionsToLegacy - Too many requested " + regionName +
515 " regions, ignoring all beyond the first " + maxNumMeteringAreas);
519 Log.v(TAG, "convertMeteringRegionsToLegacy - " + regionName + " areas = "
520 + ParameterUtils.stringFromAreaList(meteringAreaList));
523 return meteringAreaList;
526 private static void mapAeAndFlashMode(CaptureRequest r, /*out*/Parameters p) {
527 int flashMode = ParamsUtils.getOrDefault(r, FLASH_MODE, FLASH_MODE_OFF);
528 int aeMode = ParamsUtils.getOrDefault(r, CONTROL_AE_MODE, CONTROL_AE_MODE_ON);
530 List<String> supportedFlashModes = p.getSupportedFlashModes();
532 String flashModeSetting = null;
534 // Flash is OFF by default, on cameras that support flash
535 if (ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_OFF)) {
536 flashModeSetting = Parameters.FLASH_MODE_OFF;
540 * Map all of the control.aeMode* enums, but ignore AE_MODE_OFF since we never support it
543 // Ignore flash.mode controls unless aeMode == ON
544 if (aeMode == CONTROL_AE_MODE_ON) {
545 if (flashMode == FLASH_MODE_TORCH) {
546 if (ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_TORCH)) {
547 flashModeSetting = Parameters.FLASH_MODE_TORCH;
549 Log.w(TAG, "mapAeAndFlashMode - Ignore flash.mode == TORCH;" +
550 "camera does not support it");
552 } else if (flashMode == FLASH_MODE_SINGLE) {
553 if (ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_ON)) {
554 flashModeSetting = Parameters.FLASH_MODE_ON;
556 Log.w(TAG, "mapAeAndFlashMode - Ignore flash.mode == SINGLE;" +
557 "camera does not support it");
560 // Use the default FLASH_MODE_OFF
562 } else if (aeMode == CONTROL_AE_MODE_ON_ALWAYS_FLASH) {
563 if (ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_ON)) {
564 flashModeSetting = Parameters.FLASH_MODE_ON;
566 Log.w(TAG, "mapAeAndFlashMode - Ignore control.aeMode == ON_ALWAYS_FLASH;" +
567 "camera does not support it");
569 } else if (aeMode == CONTROL_AE_MODE_ON_AUTO_FLASH) {
570 if (ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_AUTO)) {
571 flashModeSetting = Parameters.FLASH_MODE_AUTO;
573 Log.w(TAG, "mapAeAndFlashMode - Ignore control.aeMode == ON_AUTO_FLASH;" +
574 "camera does not support it");
576 } else if (aeMode == CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE) {
577 if (ListUtils.listContains(supportedFlashModes, Parameters.FLASH_MODE_RED_EYE)) {
578 flashModeSetting = Parameters.FLASH_MODE_RED_EYE;
580 Log.w(TAG, "mapAeAndFlashMode - Ignore control.aeMode == ON_AUTO_FLASH_REDEYE;"
581 + "camera does not support it");
584 // Default to aeMode == ON, flash = OFF
587 if (flashModeSetting != null) {
588 p.setFlashMode(flashModeSetting);
593 "mapAeAndFlashMode - set flash.mode (api1) to " + flashModeSetting
594 + ", requested (api2) " + flashMode
595 + ", supported (api1) " + ListUtils.listToString(supportedFlashModes));
600 * Returns null if the anti-banding mode enum is not supported.
602 private static String convertAeAntiBandingModeToLegacy(int mode) {
604 case CONTROL_AE_ANTIBANDING_MODE_OFF: {
605 return Parameters.ANTIBANDING_OFF;
607 case CONTROL_AE_ANTIBANDING_MODE_50HZ: {
608 return Parameters.ANTIBANDING_50HZ;
610 case CONTROL_AE_ANTIBANDING_MODE_60HZ: {
611 return Parameters.ANTIBANDING_60HZ;
613 case CONTROL_AE_ANTIBANDING_MODE_AUTO: {
614 return Parameters.ANTIBANDING_AUTO;
622 private static int[] convertAeFpsRangeToLegacy(Range<Integer> fpsRange) {
623 int[] legacyFps = new int[2];
624 legacyFps[Parameters.PREVIEW_FPS_MIN_INDEX] = fpsRange.getLower();
625 legacyFps[Parameters.PREVIEW_FPS_MAX_INDEX] = fpsRange.getUpper();
629 private static String convertAwbModeToLegacy(int mode) {
631 case CONTROL_AWB_MODE_AUTO:
632 return Camera.Parameters.WHITE_BALANCE_AUTO;
633 case CONTROL_AWB_MODE_INCANDESCENT:
634 return Camera.Parameters.WHITE_BALANCE_INCANDESCENT;
635 case CONTROL_AWB_MODE_FLUORESCENT:
636 return Camera.Parameters.WHITE_BALANCE_FLUORESCENT;
637 case CONTROL_AWB_MODE_WARM_FLUORESCENT:
638 return Camera.Parameters.WHITE_BALANCE_WARM_FLUORESCENT;
639 case CONTROL_AWB_MODE_DAYLIGHT:
640 return Camera.Parameters.WHITE_BALANCE_DAYLIGHT;
641 case CONTROL_AWB_MODE_CLOUDY_DAYLIGHT:
642 return Camera.Parameters.WHITE_BALANCE_CLOUDY_DAYLIGHT;
643 case CONTROL_AWB_MODE_TWILIGHT:
644 return Camera.Parameters.WHITE_BALANCE_TWILIGHT;
646 Log.w(TAG, "convertAwbModeToLegacy - unrecognized control.awbMode" + mode);
647 return Camera.Parameters.WHITE_BALANCE_AUTO;
653 * Return {@code null} if the value is not supported, otherwise return the retrieved key's
654 * value from the request (or the default value if it wasn't set).
656 * <p>If the fetched value in the request is equivalent to {@code allowedValue},
657 * then omit the warning (e.g. turning off AF lock on a camera
658 * that always has the AF lock turned off is a silent no-op), but still return {@code null}.</p>
660 * <p>Logs a warning to logcat if the key is not supported by api1 camera device.</p.
662 private static <T> T getIfSupported(
663 CaptureRequest r, CaptureRequest.Key<T> key, T defaultValue, boolean isSupported,
665 T val = ParamsUtils.getOrDefault(r, key, defaultValue);
668 if (!Objects.equals(val, allowedValue)) {
669 Log.w(TAG, key.getName() + " is not supported; ignoring requested value " + val);