// Copyright (c) Philipp Wagner. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. package org.bytefish.videofacedetection; import android.app.Activity; import android.content.Context; import android.hardware.Camera; import android.hardware.Camera.Face; import android.hardware.Camera.FaceDetectionListener; import android.hardware.SensorManager; import android.os.Bundle; import android.util.Log; import android.view.OrientationEventListener; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.ViewGroup.LayoutParams; import java.util.List; public class CameraActivity extends Activity implements SurfaceHolder.Callback { public static final String TAG = CameraActivity.class.getSimpleName(); private Camera mCamera; // We need the phone orientation to correctly draw the overlay: private int mOrientation; private int mOrientationCompensation; private OrientationEventListener mOrientationEventListener; // Let's keep track of the display rotation and orientation also: private int mDisplayRotation; private int mDisplayOrientation; // Holds the Face Detection result: private Camera.Face[] mFaces; // The surface view for the camera data private SurfaceView mView; // Draw rectangles and other fancy stuff: private FaceOverlayView mFaceView; // Log all errors: private final CameraErrorCallback mErrorCallback = new CameraErrorCallback(); /** * Sets the faces for the overlay view, so it can be updated * and the face overlays will be drawn again. */ private FaceDetectionListener faceDetectionListener = new FaceDetectionListener() { @Override public void onFaceDetection(Face[] faces, Camera camera) { Log.d("onFaceDetection", "Number of Faces:" + faces.length); // Update the view now! mFaceView.setFaces(faces); } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mView = new SurfaceView(this); setContentView(mView); // Now create the OverlayView: mFaceView = new FaceOverlayView(this); addContentView(mFaceView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); // Create and Start the OrientationListener: mOrientationEventListener = new SimpleOrientationEventListener(this); mOrientationEventListener.enable(); } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); SurfaceHolder holder = mView.getHolder(); holder.addCallback(this); } @Override protected void onPause() { mOrientationEventListener.disable(); super.onPause(); } @Override protected void onResume() { mOrientationEventListener.enable(); super.onResume(); } @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { mCamera = Camera.open(); mCamera.setFaceDetectionListener(faceDetectionListener); mCamera.startFaceDetection(); try { mCamera.setPreviewDisplay(surfaceHolder); } catch (Exception e) { Log.e(TAG, "Could not preview the image.", e); } } @Override public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) { // We have no surface, return immediately: if (surfaceHolder.getSurface() == null) { return; } // Try to stop the current preview: try { mCamera.stopPreview(); } catch (Exception e) { // Ignore... } configureCamera(width, height); setDisplayOrientation(); setErrorCallback(); // Everything is configured! Finally start the camera preview again: mCamera.startPreview(); } private void setErrorCallback() { mCamera.setErrorCallback(mErrorCallback); } private void setDisplayOrientation() { // Now set the display orientation: mDisplayRotation = Util.getDisplayRotation(CameraActivity.this); mDisplayOrientation = Util.getDisplayOrientation(mDisplayRotation, 0); mCamera.setDisplayOrientation(mDisplayOrientation); if (mFaceView != null) { mFaceView.setDisplayOrientation(mDisplayOrientation); } } private void configureCamera(int width, int height) { Camera.Parameters parameters = mCamera.getParameters(); // Set the PreviewSize and AutoFocus: setOptimalPreviewSize(parameters, width, height); setAutoFocus(parameters); // And set the parameters: mCamera.setParameters(parameters); } private void setOptimalPreviewSize(Camera.Parameters cameraParameters, int width, int height) { List<Camera.Size> previewSizes = cameraParameters.getSupportedPreviewSizes(); float targetRatio = (float) width / height; Camera.Size previewSize = Util.getOptimalPreviewSize(this, previewSizes, targetRatio); cameraParameters.setPreviewSize(previewSize.width, previewSize.height); } private void setAutoFocus(Camera.Parameters cameraParameters) { cameraParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); } @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) { mCamera.setPreviewCallback(null); mCamera.setFaceDetectionListener(null); mCamera.setErrorCallback(null); mCamera.release(); mCamera = null; } /** * We need to react on OrientationEvents to rotate the screen and * update the views. */ private class SimpleOrientationEventListener extends OrientationEventListener { public SimpleOrientationEventListener(Context context) { super(context, SensorManager.SENSOR_DELAY_NORMAL); } @Override public void onOrientationChanged(int orientation) { // We keep the last known orientation. So if the user first orient // the camera then point the camera to floor or sky, we still have // the correct orientation. if (orientation == ORIENTATION_UNKNOWN) return; mOrientation = Util.roundOrientation(orientation, mOrientation); // When the screen is unlocked, display rotation may change. Always // calculate the up-to-date orientationCompensation. int orientationCompensation = mOrientation + Util.getDisplayRotation(CameraActivity.this); if (mOrientationCompensation != orientationCompensation) { mOrientationCompensation = orientationCompensation; mFaceView.setOrientation(mOrientationCompensation); } } } }