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