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