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