Home | History | Annotate | Download | only in legacy
      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      */
     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      */
    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      */
    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