blob: b3b4549426f0a06bb5dc421561fe09b2ec5caf68 [file] [log] [blame]
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -07001/*
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
17package android.hardware.camera2.legacy;
18
19import android.graphics.Rect;
20import android.hardware.Camera;
21import android.hardware.Camera.FaceDetectionListener;
22import android.hardware.camera2.impl.CameraMetadataNative;
23import android.hardware.camera2.legacy.ParameterUtils.ZoomData;
24import android.hardware.camera2.CameraCharacteristics;
25import android.hardware.camera2.CaptureRequest;
26import android.hardware.camera2.CaptureResult;
27import android.hardware.camera2.params.Face;
28import android.hardware.camera2.utils.ListUtils;
29import android.hardware.camera2.utils.ParamsUtils;
30import android.util.Log;
31import android.util.Size;
32
33import com.android.internal.util.ArrayUtils;
34
35import java.util.ArrayList;
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -070036import java.util.List;
37
38import static android.hardware.camera2.CaptureRequest.*;
39import static com.android.internal.util.Preconditions.*;
40
41/**
42 * Map legacy face detect callbacks into face detection results.
43 */
44@SuppressWarnings("deprecation")
45public class LegacyFaceDetectMapper {
46 private static String TAG = "LegacyFaceDetectMapper";
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -070047 private static final boolean DEBUG = false;
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -070048
49 private final Camera mCamera;
Igor Murashkin8c4486c12014-08-08 14:02:36 -070050 /** Is the camera capable of face detection? */
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -070051 private final boolean mFaceDetectSupported;
Igor Murashkin8c4486c12014-08-08 14:02:36 -070052 /** Is the camera is running face detection? */
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -070053 private boolean mFaceDetectEnabled = false;
Igor Murashkin8c4486c12014-08-08 14:02:36 -070054 /** 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;
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -070058
Igor Murashkin8c4486c12014-08-08 14:02:36 -070059 /** Synchronize access to all fields */
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -070060 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 */
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
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -0700100 if (DEBUG) {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700101 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 */
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
Igor Murashkin8c4486c12014-08-08 14:02:36 -0700138 /*
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
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700149 // 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
Igor Murashkin8c4486c12014-08-08 14:02:36 -0700165 boolean enableFaceDetect = (fdMode != STATISTICS_FACE_DETECT_MODE_OFF)
166 || (sceneMode == CONTROL_SCENE_MODE_FACE_PRIORITY);
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700167 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
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -0700173 if (DEBUG) {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700174 Log.v(TAG, "processFaceDetectMode - start face detection");
175 }
176 } else {
177 mCamera.stopFaceDetection();
178
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -0700179 if (DEBUG) {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700180 Log.v(TAG, "processFaceDetectMode - stop face detection");
181 }
182
183 mFaces = null;
184 }
185
186 mFaceDetectEnabled = enableFaceDetect;
Igor Murashkin8c4486c12014-08-08 14:02:36 -0700187 mFaceDetectScenePriority = sceneMode == CONTROL_SCENE_MODE_FACE_PRIORITY;
188 mFaceDetectReporting = fdMode != STATISTICS_FACE_DETECT_MODE_OFF;
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700189 }
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 *
Igor Murashkin8c4486c12014-08-08 14:02:36 -0700200 * <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 *
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700204 * @param result a non-{@code null} result
205 * @param legacyRequest a non-{@code null} request (read-only)
206 */
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;
Igor Murashkin8c4486c12014-08-08 14:02:36 -0700213 boolean fdScenePriority;
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700214 synchronized (mLock) {
Igor Murashkin8c4486c12014-08-08 14:02:36 -0700215 fdMode = mFaceDetectReporting ?
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700216 STATISTICS_FACE_DETECT_MODE_SIMPLE : STATISTICS_FACE_DETECT_MODE_OFF;
217
Igor Murashkin8c4486c12014-08-08 14:02:36 -0700218 if (mFaceDetectReporting) {
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700219 faces = mFaces;
220 } else {
221 faces = null;
222 }
223
Igor Murashkin8c4486c12014-08-08 14:02:36 -0700224 fdScenePriority = mFaceDetectScenePriority;
225
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700226 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);
Shuzhen Wangf807a412020-03-27 18:32:27 -0700236 ZoomData zoomData = ParameterUtils.convertToLegacyZoom(activeArray,
237 request.get(CaptureRequest.SCALER_CROP_REGION),
238 request.get(CaptureRequest.CONTROL_ZOOM_RATIO),
239 previewSize, params);
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700240
241 List<Face> convertedFaces = new ArrayList<>();
242 if (faces != null) {
243 for (Camera.Face face : faces) {
244 if (face != null) {
245 convertedFaces.add(
246 ParameterUtils.convertFaceFromLegacy(face, activeArray, zoomData));
247 } else {
248 Log.w(TAG, "mapResultFaces - read NULL face from camera1 device");
249 }
250 }
251 }
252
Eino-Ville Talvalaa78791f2015-06-01 12:39:54 -0700253 if (DEBUG && previousFaces != faces) { // Log only in verbose and IF the faces changed
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700254 Log.v(TAG, "mapResultFaces - changed to " + ListUtils.listToString(convertedFaces));
255 }
256
257 result.set(CaptureResult.STATISTICS_FACES, convertedFaces.toArray(new Face[0]));
258 result.set(CaptureResult.STATISTICS_FACE_DETECT_MODE, fdMode);
Igor Murashkin8c4486c12014-08-08 14:02:36 -0700259
260 // Override scene mode with FACE_PRIORITY if the request was using FACE_PRIORITY
261 if (fdScenePriority) {
262 result.set(CaptureResult.CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_FACE_PRIORITY);
263 }
Igor Murashkin0a1ef4d2014-07-31 15:53:34 -0700264 }
265}