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