1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 
17 package android.hardware.camera2.legacy;
18 
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.CaptureResult;
25 import android.hardware.camera2.impl.CameraMetadataNative;
26 import android.hardware.camera2.legacy.ParameterUtils.WeightedRectangle;
27 import android.hardware.camera2.legacy.ParameterUtils.ZoomData;
28 import android.hardware.camera2.params.MeteringRectangle;
29 import android.hardware.camera2.utils.ListUtils;
30 import android.hardware.camera2.utils.ParamsUtils;
31 import android.util.Log;
32 import android.util.Size;
33 
34 import java.util.ArrayList;
35 import java.util.List;
36 
37 import static android.hardware.camera2.CaptureResult.*;
38 
39 /**
40  * Provide legacy-specific implementations of camera2 CaptureResult for legacy devices.
41  */
42 @SuppressWarnings("deprecation")
43 public class LegacyResultMapper {
44     private static final String TAG = "LegacyResultMapper";
45     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
46 
47     private LegacyRequest mCachedRequest = null;
48     private CameraMetadataNative mCachedResult = null;
49 
50     /**
51      * Generate capture result metadata from the legacy camera request.
52      *
53      * <p>This method caches and reuses the result from the previous call to this method if
54      * the {@code parameters} of the subsequent {@link LegacyRequest} passed to this method
55      * have not changed.</p>
56      *
57      * @param legacyRequest a non-{@code null} legacy request containing the latest parameters
58      * @param timestamp the timestamp to use for this result in nanoseconds.
59      *
60      * @return {@link CameraMetadataNative} object containing result metadata.
61      */
cachedConvertResultMetadata( LegacyRequest legacyRequest, long timestamp)62     public CameraMetadataNative cachedConvertResultMetadata(
63             LegacyRequest legacyRequest, long timestamp) {
64         CameraMetadataNative result;
65         boolean cached;
66 
67         /*
68          * Attempt to look up the result from the cache if the parameters haven't changed
69          */
70         if (mCachedRequest != null && legacyRequest.parameters.same(mCachedRequest.parameters)) {
71             result = new CameraMetadataNative(mCachedResult);
72             cached = true;
73         } else {
74             result = convertResultMetadata(legacyRequest);
75             cached = false;
76 
77             // Always cache a *copy* of the metadata result,
78             // since api2's client side takes ownership of it after it receives a result
79             mCachedRequest = legacyRequest;
80             mCachedResult = new CameraMetadataNative(result);
81         }
82 
83         /*
84          * Unconditionally set fields that change in every single frame
85          */
86         {
87             // sensor.timestamp
88             result.set(SENSOR_TIMESTAMP, timestamp);
89         }
90 
91         if (VERBOSE) {
92             Log.v(TAG, "cachedConvertResultMetadata - cached? " + cached +
93                     " timestamp = " + timestamp);
94 
95             Log.v(TAG, "----- beginning of result dump ------");
96             result.dumpToLog();
97             Log.v(TAG, "----- end of result dump ------");
98         }
99 
100         return result;
101     }
102 
103     /**
104      * Generate capture result metadata from the legacy camera request.
105      *
106      * @param legacyRequest a non-{@code null} legacy request containing the latest parameters
107      * @return a {@link CameraMetadataNative} object containing result metadata.
108      */
convertResultMetadata(LegacyRequest legacyRequest)109     private static CameraMetadataNative convertResultMetadata(LegacyRequest legacyRequest) {
110         CameraCharacteristics characteristics = legacyRequest.characteristics;
111         CaptureRequest request = legacyRequest.captureRequest;
112         Size previewSize = legacyRequest.previewSize;
113         Camera.Parameters params = legacyRequest.parameters;
114 
115         CameraMetadataNative result = new CameraMetadataNative();
116 
117         Rect activeArraySize = characteristics.get(
118                 CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
119         ZoomData zoomData = ParameterUtils.convertScalerCropRegion(activeArraySize,
120                 request.get(CaptureRequest.SCALER_CROP_REGION), previewSize, params);
121 
122         /*
123          * colorCorrection
124          */
125         // colorCorrection.aberrationMode
126         {
127             // Always hardcoded to FAST
128             result.set(COLOR_CORRECTION_ABERRATION_MODE, COLOR_CORRECTION_ABERRATION_MODE_FAST);
129         }
130 
131         /*
132          * control
133          */
134 
135         /*
136          * control.ae*
137          */
138         mapAe(result, characteristics, request, activeArraySize, zoomData, /*out*/params);
139 
140         /*
141          * control.af*
142          */
143         mapAf(result, activeArraySize, zoomData, /*out*/params);
144 
145         /*
146          * control.awb*
147          */
148         mapAwb(result, /*out*/params);
149 
150         /*
151          * control.captureIntent
152          */
153         {
154             int captureIntent = ParamsUtils.getOrDefault(request,
155                     CaptureRequest.CONTROL_CAPTURE_INTENT,
156                     /*defaultValue*/CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW);
157 
158             captureIntent = LegacyRequestMapper.filterSupportedCaptureIntent(captureIntent);
159 
160             result.set(CONTROL_CAPTURE_INTENT, captureIntent);
161         }
162 
163         /*
164          * control.mode
165          */
166         {
167             int controlMode = ParamsUtils.getOrDefault(request, CaptureRequest.CONTROL_MODE,
168                     CONTROL_MODE_AUTO);
169             if (controlMode == CaptureResult.CONTROL_MODE_USE_SCENE_MODE) {
170                 result.set(CONTROL_MODE, CONTROL_MODE_USE_SCENE_MODE);
171             } else {
172                 result.set(CONTROL_MODE, CONTROL_MODE_AUTO);
173             }
174         }
175 
176         /*
177          * control.sceneMode
178          */
179         {
180             String legacySceneMode = params.getSceneMode();
181             int mode = LegacyMetadataMapper.convertSceneModeFromLegacy(legacySceneMode);
182             if (mode != LegacyMetadataMapper.UNKNOWN_MODE) {
183                 result.set(CaptureResult.CONTROL_SCENE_MODE, mode);
184                 // In case of SCENE_MODE == FACE_PRIORITY, LegacyFaceDetectMapper will override
185                 // the result to say SCENE_MODE == FACE_PRIORITY.
186             }  else {
187                 Log.w(TAG, "Unknown scene mode " + legacySceneMode +
188                         " returned by camera HAL, setting to disabled.");
189                 result.set(CaptureResult.CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_DISABLED);
190             }
191         }
192 
193         /*
194          * control.effectMode
195          */
196         {
197             String legacyEffectMode = params.getColorEffect();
198             int mode = LegacyMetadataMapper.convertEffectModeFromLegacy(legacyEffectMode);
199             if (mode != LegacyMetadataMapper.UNKNOWN_MODE) {
200                 result.set(CaptureResult.CONTROL_EFFECT_MODE, mode);
201             } else {
202                 Log.w(TAG, "Unknown effect mode " + legacyEffectMode +
203                         " returned by camera HAL, setting to off.");
204                 result.set(CaptureResult.CONTROL_EFFECT_MODE, CONTROL_EFFECT_MODE_OFF);
205             }
206         }
207 
208         // control.videoStabilizationMode
209         {
210             int stabMode =
211                     (params.isVideoStabilizationSupported() && params.getVideoStabilization()) ?
212                         CONTROL_VIDEO_STABILIZATION_MODE_ON :
213                         CONTROL_VIDEO_STABILIZATION_MODE_OFF;
214             result.set(CONTROL_VIDEO_STABILIZATION_MODE, stabMode);
215         }
216 
217         /*
218          * flash
219          */
220         {
221             // flash.mode, flash.state mapped in mapAeAndFlashMode
222         }
223 
224         /*
225          * lens
226          */
227         // lens.focusDistance
228         {
229             if (Parameters.FOCUS_MODE_INFINITY.equals(params.getFocusMode())) {
230                 result.set(CaptureResult.LENS_FOCUS_DISTANCE, 0.0f);
231             }
232         }
233 
234         // lens.focalLength
235         result.set(CaptureResult.LENS_FOCAL_LENGTH, params.getFocalLength());
236 
237         /*
238          * request
239          */
240         // request.pipelineDepth
241         result.set(REQUEST_PIPELINE_DEPTH,
242                 characteristics.get(CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH));
243 
244         /*
245          * scaler
246          */
247         mapScaler(result, zoomData, /*out*/params);
248 
249         /*
250          * sensor
251          */
252         // sensor.timestamp varies every frame; mapping is done in #cachedConvertResultMetadata
253         {
254             // Unconditionally no test patterns
255             result.set(SENSOR_TEST_PATTERN_MODE, SENSOR_TEST_PATTERN_MODE_OFF);
256         }
257 
258         /*
259          * jpeg
260          */
261         // jpeg.gpsLocation
262         result.set(JPEG_GPS_LOCATION, request.get(CaptureRequest.JPEG_GPS_LOCATION));
263 
264         // jpeg.orientation
265         result.set(JPEG_ORIENTATION, request.get(CaptureRequest.JPEG_ORIENTATION));
266 
267         // jpeg.quality
268         result.set(JPEG_QUALITY, (byte) params.getJpegQuality());
269 
270         // jpeg.thumbnailQuality
271         result.set(JPEG_THUMBNAIL_QUALITY, (byte) params.getJpegThumbnailQuality());
272 
273         // jpeg.thumbnailSize
274         Camera.Size s = params.getJpegThumbnailSize();
275         if (s != null) {
276             result.set(JPEG_THUMBNAIL_SIZE, ParameterUtils.convertSize(s));
277         } else {
278             Log.w(TAG, "Null thumbnail size received from parameters.");
279         }
280 
281         /*
282          * noiseReduction.*
283          */
284         // noiseReduction.mode
285         result.set(NOISE_REDUCTION_MODE, NOISE_REDUCTION_MODE_FAST);
286 
287         return result;
288     }
289 
mapAe(CameraMetadataNative m, CameraCharacteristics characteristics, CaptureRequest request, Rect activeArray, ZoomData zoomData, Parameters p)290     private static void mapAe(CameraMetadataNative m,
291             CameraCharacteristics characteristics,
292             CaptureRequest request, Rect activeArray, ZoomData zoomData, /*out*/Parameters p) {
293         // control.aeAntiBandingMode
294         {
295             int antiBandingMode = LegacyMetadataMapper.convertAntiBandingModeOrDefault(
296                     p.getAntibanding());
297             m.set(CONTROL_AE_ANTIBANDING_MODE, antiBandingMode);
298         }
299 
300         // control.aeExposureCompensation
301         {
302             m.set(CONTROL_AE_EXPOSURE_COMPENSATION, p.getExposureCompensation());
303         }
304 
305         // control.aeLock
306         {
307             boolean lock = p.isAutoExposureLockSupported() ? p.getAutoExposureLock() : false;
308             m.set(CONTROL_AE_LOCK, lock);
309             if (VERBOSE) {
310                 Log.v(TAG,
311                         "mapAe - android.control.aeLock = " + lock +
312                         ", supported = " + p.isAutoExposureLockSupported());
313             }
314 
315             Boolean requestLock = request.get(CaptureRequest.CONTROL_AE_LOCK);
316             if (requestLock != null && requestLock != lock) {
317                 Log.w(TAG,
318                         "mapAe - android.control.aeLock was requested to " + requestLock +
319                         " but resulted in " + lock);
320             }
321         }
322 
323         // control.aeMode, flash.mode, flash.state
324         mapAeAndFlashMode(m, characteristics, p);
325 
326         // control.aeState
327         if (LegacyMetadataMapper.LIE_ABOUT_AE_STATE) {
328             // Lie to pass CTS temporarily.
329             // TODO: Implement precapture trigger, after which we can report CONVERGED ourselves
330             m.set(CONTROL_AE_STATE, CONTROL_AE_STATE_CONVERGED);
331         }
332 
333         // control.aeRegions
334         if (p.getMaxNumMeteringAreas() > 0) {
335             if (VERBOSE) {
336                 String meteringAreas = p.get("metering-areas");
337                 Log.v(TAG, "mapAe - parameter dump; metering-areas: " + meteringAreas);
338             }
339 
340             MeteringRectangle[] meteringRectArray = getMeteringRectangles(activeArray,
341                     zoomData, p.getMeteringAreas(), "AE");
342 
343             m.set(CONTROL_AE_REGIONS, meteringRectArray);
344         }
345 
346     }
347 
mapAf(CameraMetadataNative m, Rect activeArray, ZoomData zoomData, Camera.Parameters p)348     private static void mapAf(CameraMetadataNative m,
349             Rect activeArray, ZoomData zoomData, Camera.Parameters p) {
350         // control.afMode
351         m.set(CaptureResult.CONTROL_AF_MODE, convertLegacyAfMode(p.getFocusMode()));
352 
353         // control.afRegions
354         if (p.getMaxNumFocusAreas() > 0) {
355             if (VERBOSE) {
356                 String focusAreas = p.get("focus-areas");
357                 Log.v(TAG, "mapAe - parameter dump; focus-areas: " + focusAreas);
358             }
359 
360             MeteringRectangle[] meteringRectArray = getMeteringRectangles(activeArray,
361                     zoomData, p.getFocusAreas(), "AF");
362 
363             m.set(CONTROL_AF_REGIONS, meteringRectArray);
364         }
365     }
366 
mapAwb(CameraMetadataNative m, Camera.Parameters p)367     private static void mapAwb(CameraMetadataNative m, Camera.Parameters p) {
368         // control.awbLock
369         {
370             boolean lock = p.isAutoWhiteBalanceLockSupported() ?
371                     p.getAutoWhiteBalanceLock() : false;
372             m.set(CONTROL_AWB_LOCK, lock);
373         }
374 
375         // control.awbMode
376         {
377             int awbMode = convertLegacyAwbMode(p.getWhiteBalance());
378             m.set(CONTROL_AWB_MODE, awbMode);
379         }
380     }
381 
getMeteringRectangles(Rect activeArray, ZoomData zoomData, List<Camera.Area> meteringAreaList, String regionName)382     private static MeteringRectangle[] getMeteringRectangles(Rect activeArray, ZoomData zoomData,
383             List<Camera.Area> meteringAreaList, String regionName) {
384         List<MeteringRectangle> meteringRectList = new ArrayList<>();
385         if (meteringAreaList != null) {
386             for (Camera.Area area : meteringAreaList) {
387                 WeightedRectangle rect =
388                         ParameterUtils.convertCameraAreaToActiveArrayRectangle(
389                                 activeArray, zoomData, area);
390 
391                 meteringRectList.add(rect.toMetering());
392             }
393         }
394 
395         if (VERBOSE) {
396             Log.v(TAG,
397                     "Metering rectangles for " + regionName + ": "
398                      + ListUtils.listToString(meteringRectList));
399         }
400 
401         return meteringRectList.toArray(new MeteringRectangle[0]);
402     }
403 
404     /** Map results for control.aeMode, flash.mode, flash.state */
mapAeAndFlashMode(CameraMetadataNative m, CameraCharacteristics characteristics, Parameters p)405     private static void mapAeAndFlashMode(CameraMetadataNative m,
406             CameraCharacteristics characteristics, Parameters p) {
407         // Default: AE mode on but flash never fires
408         int flashMode = FLASH_MODE_OFF;
409         // If there is no flash on this camera, the state is always unavailable
410         // , otherwise it's only known for TORCH/SINGLE modes
411         Integer flashState = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE)
412                 ? null : FLASH_STATE_UNAVAILABLE;
413         int aeMode = CONTROL_AE_MODE_ON;
414 
415         String flashModeSetting = p.getFlashMode();
416 
417         if (flashModeSetting != null) {
418             switch (flashModeSetting) {
419                 case Parameters.FLASH_MODE_OFF:
420                     break; // ok, using default
421                 case Parameters.FLASH_MODE_AUTO:
422                     aeMode = CONTROL_AE_MODE_ON_AUTO_FLASH;
423                     break;
424                 case Parameters.FLASH_MODE_ON:
425                     // flashMode = SINGLE + aeMode = ON is indistinguishable from ON_ALWAYS_FLASH
426                     flashMode = FLASH_MODE_SINGLE;
427                     aeMode = CONTROL_AE_MODE_ON_ALWAYS_FLASH;
428                     flashState = FLASH_STATE_FIRED;
429                     break;
430                 case Parameters.FLASH_MODE_RED_EYE:
431                     aeMode = CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE;
432                     break;
433                 case Parameters.FLASH_MODE_TORCH:
434                     flashMode = FLASH_MODE_TORCH;
435                     flashState = FLASH_STATE_FIRED;
436                     break;
437                 default:
438                     Log.w(TAG,
439                             "mapAeAndFlashMode - Ignoring unknown flash mode " + p.getFlashMode());
440             }
441         }
442 
443         // flash.state
444         m.set(FLASH_STATE, flashState);
445         // flash.mode
446         m.set(FLASH_MODE, flashMode);
447         // control.aeMode
448         m.set(CONTROL_AE_MODE, aeMode);
449     }
450 
convertLegacyAfMode(String mode)451     private static int convertLegacyAfMode(String mode) {
452         if (mode == null) {
453             Log.w(TAG, "convertLegacyAfMode - no AF mode, default to OFF");
454             return CONTROL_AF_MODE_OFF;
455         }
456 
457         switch (mode) {
458             case Parameters.FOCUS_MODE_AUTO:
459                 return CONTROL_AF_MODE_AUTO;
460             case Parameters.FOCUS_MODE_CONTINUOUS_PICTURE:
461                 return CONTROL_AF_MODE_CONTINUOUS_PICTURE;
462             case Parameters.FOCUS_MODE_CONTINUOUS_VIDEO:
463                 return CONTROL_AF_MODE_CONTINUOUS_VIDEO;
464             case Parameters.FOCUS_MODE_EDOF:
465                 return CONTROL_AF_MODE_EDOF;
466             case Parameters.FOCUS_MODE_MACRO:
467                 return CONTROL_AF_MODE_MACRO;
468             case Parameters.FOCUS_MODE_FIXED:
469                 return CONTROL_AF_MODE_OFF;
470             case Parameters.FOCUS_MODE_INFINITY:
471                 return CONTROL_AF_MODE_OFF;
472             default:
473                 Log.w(TAG, "convertLegacyAfMode - unknown mode " + mode + " , ignoring");
474                 return CONTROL_AF_MODE_OFF;
475         }
476     }
477 
convertLegacyAwbMode(String mode)478     private static int convertLegacyAwbMode(String mode) {
479         if (mode == null) {
480             // OK: camera1 api may not support changing WB modes; assume AUTO
481             return CONTROL_AWB_MODE_AUTO;
482         }
483 
484         switch (mode) {
485             case Camera.Parameters.WHITE_BALANCE_AUTO:
486                 return CONTROL_AWB_MODE_AUTO;
487             case Camera.Parameters.WHITE_BALANCE_INCANDESCENT:
488                 return CONTROL_AWB_MODE_INCANDESCENT;
489             case Camera.Parameters.WHITE_BALANCE_FLUORESCENT:
490                 return CONTROL_AWB_MODE_FLUORESCENT;
491             case Camera.Parameters.WHITE_BALANCE_WARM_FLUORESCENT:
492                 return CONTROL_AWB_MODE_WARM_FLUORESCENT;
493             case Camera.Parameters.WHITE_BALANCE_DAYLIGHT:
494                 return CONTROL_AWB_MODE_DAYLIGHT;
495             case Camera.Parameters.WHITE_BALANCE_CLOUDY_DAYLIGHT:
496                 return CONTROL_AWB_MODE_CLOUDY_DAYLIGHT;
497             case Camera.Parameters.WHITE_BALANCE_TWILIGHT:
498                 return CONTROL_AWB_MODE_TWILIGHT;
499             case Camera.Parameters.WHITE_BALANCE_SHADE:
500                 return CONTROL_AWB_MODE_SHADE;
501             default:
502                 Log.w(TAG, "convertAwbMode - unrecognized WB mode " + mode);
503                 return CONTROL_AWB_MODE_AUTO;
504         }
505     }
506 
507     /** Map results for scaler.* */
mapScaler(CameraMetadataNative m, ZoomData zoomData, Parameters p)508     private static void mapScaler(CameraMetadataNative m,
509             ZoomData zoomData,
510             /*out*/Parameters p) {
511         /*
512          * scaler.cropRegion
513          */
514         {
515             m.set(SCALER_CROP_REGION, zoomData.reportedCrop);
516         }
517     }
518 }
519