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.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      */
     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      */
    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      */
    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