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.FaceDetectionListener; 22 import android.hardware.camera2.impl.CameraMetadataNative; 23 import android.hardware.camera2.legacy.ParameterUtils.ZoomData; 24 import android.hardware.camera2.CameraCharacteristics; 25 import android.hardware.camera2.CaptureRequest; 26 import android.hardware.camera2.CaptureResult; 27 import android.hardware.camera2.params.Face; 28 import android.hardware.camera2.utils.ListUtils; 29 import android.hardware.camera2.utils.ParamsUtils; 30 import android.util.Log; 31 import android.util.Size; 32 33 import com.android.internal.util.ArrayUtils; 34 35 import java.util.ArrayList; 36 import java.util.Arrays; 37 import java.util.List; 38 39 import static android.hardware.camera2.CaptureRequest.*; 40 import static com.android.internal.util.Preconditions.*; 41 42 /** 43 * Map legacy face detect callbacks into face detection results. 44 */ 45 @SuppressWarnings("deprecation") 46 public class LegacyFaceDetectMapper { 47 private static String TAG = "LegacyFaceDetectMapper"; 48 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 49 50 private final Camera mCamera; 51 /** Is the camera capable of face detection? */ 52 private final boolean mFaceDetectSupported; 53 /** Is the camera is running face detection? */ 54 private boolean mFaceDetectEnabled = false; 55 /** Did the last request say to use SCENE_MODE = FACE_PRIORITY? */ 56 private boolean mFaceDetectScenePriority = false; 57 /** Did the last request enable the face detect mode to ON? */ 58 private boolean mFaceDetectReporting = false; 59 60 /** Synchronize access to all fields */ 61 private final Object mLock = new Object(); 62 private Camera.Face[] mFaces; 63 private Camera.Face[] mFacesPrev; 64 /** 65 * Instantiate a new face detect mapper. 66 * 67 * @param camera a non-{@code null} camera1 device 68 * @param characteristics a non-{@code null} camera characteristics for that camera1 69 * 70 * @throws NullPointerException if any of the args were {@code null} 71 */ LegacyFaceDetectMapper(Camera camera, CameraCharacteristics characteristics)72 public LegacyFaceDetectMapper(Camera camera, CameraCharacteristics characteristics) { 73 mCamera = checkNotNull(camera, "camera must not be null"); 74 checkNotNull(characteristics, "characteristics must not be null"); 75 76 mFaceDetectSupported = ArrayUtils.contains( 77 characteristics.get( 78 CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES), 79 STATISTICS_FACE_DETECT_MODE_SIMPLE); 80 81 if (!mFaceDetectSupported) { 82 return; 83 } 84 85 mCamera.setFaceDetectionListener(new FaceDetectionListener() { 86 87 @Override 88 public void onFaceDetection(Camera.Face[] faces, Camera camera) { 89 int lengthFaces = faces == null ? 0 : faces.length; 90 synchronized (mLock) { 91 if (mFaceDetectEnabled) { 92 mFaces = faces; 93 } else if (lengthFaces > 0) { 94 // stopFaceDetectMode could race against the requests, print a debug log 95 Log.d(TAG, 96 "onFaceDetection - Ignored some incoming faces since" + 97 "face detection was disabled"); 98 } 99 } 100 101 if (VERBOSE) { 102 Log.v(TAG, "onFaceDetection - read " + lengthFaces + " faces"); 103 } 104 } 105 }); 106 } 107 108 /** 109 * Process the face detect mode from the capture request into an api1 face detect toggle. 110 * 111 * <p>This method should be called after the parameters are {@link LegacyRequestMapper mapped} 112 * with the request.</p> 113 * 114 * <p>Callbacks are processed in the background, and the next call to {@link #mapResultTriggers} 115 * will have the latest faces detected as reflected by the camera1 callbacks.</p> 116 * 117 * <p>None of the arguments will be mutated.</p> 118 * 119 * @param captureRequest a non-{@code null} request 120 * @param parameters a non-{@code null} parameters corresponding to this request (read-only) 121 */ processFaceDetectMode(CaptureRequest captureRequest, Camera.Parameters parameters)122 public void processFaceDetectMode(CaptureRequest captureRequest, 123 Camera.Parameters parameters) { 124 checkNotNull(captureRequest, "captureRequest must not be null"); 125 126 /* 127 * statistics.faceDetectMode 128 */ 129 int fdMode = ParamsUtils.getOrDefault(captureRequest, STATISTICS_FACE_DETECT_MODE, 130 STATISTICS_FACE_DETECT_MODE_OFF); 131 132 if (fdMode != STATISTICS_FACE_DETECT_MODE_OFF && !mFaceDetectSupported) { 133 Log.w(TAG, 134 "processFaceDetectMode - Ignoring statistics.faceDetectMode; " + 135 "face detection is not available"); 136 return; 137 } 138 139 /* 140 * control.sceneMode 141 */ 142 int sceneMode = ParamsUtils.getOrDefault(captureRequest, CONTROL_SCENE_MODE, 143 CONTROL_SCENE_MODE_DISABLED); 144 if (sceneMode == CONTROL_SCENE_MODE_FACE_PRIORITY && !mFaceDetectSupported) { 145 Log.w(TAG, "processFaceDetectMode - ignoring control.sceneMode == FACE_PRIORITY; " + 146 "face detection is not available"); 147 return; 148 } 149 150 // Print some warnings out in case the values were wrong 151 switch (fdMode) { 152 case STATISTICS_FACE_DETECT_MODE_OFF: 153 case STATISTICS_FACE_DETECT_MODE_SIMPLE: 154 break; 155 case STATISTICS_FACE_DETECT_MODE_FULL: 156 Log.w(TAG, 157 "processFaceDetectMode - statistics.faceDetectMode == FULL unsupported, " + 158 "downgrading to SIMPLE"); 159 break; 160 default: 161 Log.w(TAG, "processFaceDetectMode - ignoring unknown statistics.faceDetectMode = " 162 + fdMode); 163 return; 164 } 165 166 boolean enableFaceDetect = (fdMode != STATISTICS_FACE_DETECT_MODE_OFF) 167 || (sceneMode == CONTROL_SCENE_MODE_FACE_PRIORITY); 168 synchronized (mLock) { 169 // Enable/disable face detection if it's changed since last time 170 if (enableFaceDetect != mFaceDetectEnabled) { 171 if (enableFaceDetect) { 172 mCamera.startFaceDetection(); 173 174 if (VERBOSE) { 175 Log.v(TAG, "processFaceDetectMode - start face detection"); 176 } 177 } else { 178 mCamera.stopFaceDetection(); 179 180 if (VERBOSE) { 181 Log.v(TAG, "processFaceDetectMode - stop face detection"); 182 } 183 184 mFaces = null; 185 } 186 187 mFaceDetectEnabled = enableFaceDetect; 188 mFaceDetectScenePriority = sceneMode == CONTROL_SCENE_MODE_FACE_PRIORITY; 189 mFaceDetectReporting = fdMode != STATISTICS_FACE_DETECT_MODE_OFF; 190 } 191 } 192 } 193 194 /** 195 * Update the {@code result} camera metadata map with the new value for the 196 * {@code statistics.faces} and {@code statistics.faceDetectMode}. 197 * 198 * <p>Face detect callbacks are processed in the background, and each call to 199 * {@link #mapResultFaces} will have the latest faces as reflected by the camera1 callbacks.</p> 200 * 201 * <p>If the scene mode was set to {@code FACE_PRIORITY} but face detection is disabled, 202 * the camera will still run face detection in the background, but no faces will be reported 203 * in the capture result.</p> 204 * 205 * @param result a non-{@code null} result 206 * @param legacyRequest a non-{@code null} request (read-only) 207 */ mapResultFaces(CameraMetadataNative result, LegacyRequest legacyRequest)208 public void mapResultFaces(CameraMetadataNative result, LegacyRequest legacyRequest) { 209 checkNotNull(result, "result must not be null"); 210 checkNotNull(legacyRequest, "legacyRequest must not be null"); 211 212 Camera.Face[] faces, previousFaces; 213 int fdMode; 214 boolean fdScenePriority; 215 synchronized (mLock) { 216 fdMode = mFaceDetectReporting ? 217 STATISTICS_FACE_DETECT_MODE_SIMPLE : STATISTICS_FACE_DETECT_MODE_OFF; 218 219 if (mFaceDetectReporting) { 220 faces = mFaces; 221 } else { 222 faces = null; 223 } 224 225 fdScenePriority = mFaceDetectScenePriority; 226 227 previousFaces = mFacesPrev; 228 mFacesPrev = faces; 229 } 230 231 CameraCharacteristics characteristics = legacyRequest.characteristics; 232 CaptureRequest request = legacyRequest.captureRequest; 233 Size previewSize = legacyRequest.previewSize; 234 Camera.Parameters params = legacyRequest.parameters; 235 236 Rect activeArray = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); 237 ZoomData zoomData = ParameterUtils.convertScalerCropRegion(activeArray, 238 request.get(CaptureRequest.SCALER_CROP_REGION), previewSize, params); 239 240 List<Face> convertedFaces = new ArrayList<>(); 241 if (faces != null) { 242 for (Camera.Face face : faces) { 243 if (face != null) { 244 convertedFaces.add( 245 ParameterUtils.convertFaceFromLegacy(face, activeArray, zoomData)); 246 } else { 247 Log.w(TAG, "mapResultFaces - read NULL face from camera1 device"); 248 } 249 } 250 } 251 252 if (VERBOSE && previousFaces != faces) { // Log only in verbose and IF the faces changed 253 Log.v(TAG, "mapResultFaces - changed to " + ListUtils.listToString(convertedFaces)); 254 } 255 256 result.set(CaptureResult.STATISTICS_FACES, convertedFaces.toArray(new Face[0])); 257 result.set(CaptureResult.STATISTICS_FACE_DETECT_MODE, fdMode); 258 259 // Override scene mode with FACE_PRIORITY if the request was using FACE_PRIORITY 260 if (fdScenePriority) { 261 result.set(CaptureResult.CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_FACE_PRIORITY); 262 } 263 } 264 } 265