/* AndroidCameraRecordManager.java Copyright (C) 2010 Belledonne Communications, Grenoble, France This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.linphone.core; import java.util.List; import org.linphone.core.AndroidCameraRecord.RecorderParams; import android.hardware.Camera; import android.hardware.Camera.Size; import android.os.Build; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.SurfaceHolder.Callback; /** * Manage the video capture, only one for all cameras. * * @author Guillaume Beraudo * */ public class AndroidCameraRecordManager { private static final String tag = "Linphone"; private static AndroidCameraRecordManager instance; /** * @return instance */ public static final synchronized AndroidCameraRecordManager getInstance() { if (instance == null) { instance = new AndroidCameraRecordManager(); } return instance; } private AndroidCameraRecord.RecorderParams parameters; private SurfaceView surfaceView; private boolean muted; private int cameraId; private AndroidCameraRecord recorder; private List supportedVideoSizes; private int phoneOrientation; public int getPhoneOrientation() {return phoneOrientation;} public void setPhoneOrientation(int degrees) {this.phoneOrientation = degrees;} private int frontCameraId; private int rearCameraId; // singleton private AndroidCameraRecordManager() { findFrontAndRearCameraIds(); } private void findFrontAndRearCameraIds() { if (Version.sdkAbove(9)) { findFrontAndRearCameraIds9(); return; } if (Build.DEVICE.startsWith("GT-I9000")) { // Galaxy S has 2 cameras frontCameraId = 2; rearCameraId = 1; cameraId = rearCameraId; return; } // default to 0/0 } private void findFrontAndRearCameraIds9() { for (int id=0; id < getNumberOfCameras9(); id++) { if (isFrontCamera9(id)) { frontCameraId = id; } else { rearCameraId = id; } } } public boolean hasSeveralCameras() { return frontCameraId != rearCameraId; } public void setUseFrontCamera(boolean value) { if (isFrontCamera() == value) return; // already OK toggleUseFrontCamera(); } public boolean isUseFrontCamera() {return isFrontCamera();} public boolean toggleUseFrontCamera() { boolean previousUseFront = isFrontCamera(); cameraId = previousUseFront ? rearCameraId : frontCameraId; if (parameters != null) { parameters.cameraId = cameraId; if (isRecording()) { stopVideoRecording(); tryToStartVideoRecording(); } } return !previousUseFront; } public void setParametersFromFilter(long filterDataPtr, int height, int width, float fps) { stopVideoRecording(); RecorderParams p = new RecorderParams(filterDataPtr); p.fps = fps; p.width = width; p.height = height; p.cameraId = cameraId; parameters = p; tryToStartVideoRecording(); } public final void setSurfaceView(final SurfaceView sv, final int phoneOrientation) { this.phoneOrientation = phoneOrientation; SurfaceHolder holder = sv.getHolder(); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); holder.addCallback(new Callback() { public void surfaceDestroyed(SurfaceHolder holder) { surfaceView = null; Log.d(tag , "Video capture surface destroyed"); stopVideoRecording(); } public void surfaceCreated(SurfaceHolder holder) { surfaceView = sv; Log.d(tag , "Video capture surface created"); tryToStartVideoRecording(); } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.d(tag , "Video capture surface changed"); } }); } public void setMuted(boolean muteState) { if (muteState == muted) return; muted = muteState; if (muted) { stopVideoRecording(); } else { tryToStartVideoRecording(); } } public boolean toggleMute() { setMuted(!muted); return muted; } public boolean isMuted() { return muted; } public void tryResumingVideoRecording() { if (isRecording()) return; tryToStartVideoRecording(); } private void tryToStartVideoRecording() { if (muted || surfaceView == null || parameters == null) return; parameters.rotation = bufferRotationForCorrectImageOrientation(); parameters.surfaceView = surfaceView; if (Version.sdkAbove(9)) { recorder = new AndroidCameraRecord9Impl(parameters); } else if (Version.sdkAbove(8)) { recorder = new AndroidCameraRecord8Impl(parameters); } else if (Version.sdkAbove(5)) { recorder = new AndroidCameraRecord5Impl(parameters); } else { recorder = new AndroidCameraRecordImpl(parameters); } recorder.startPreview(); } public void stopVideoRecording() { if (recorder != null) { recorder.stopPreview(); recorder = null; } } // FIXME select right camera /** * Eventually null if API < 5. * */ public List supportedVideoSizes() { if (supportedVideoSizes != null) { return supportedVideoSizes; } if (recorder != null) { supportedVideoSizes = recorder.getSupportedVideoSizes(); if (supportedVideoSizes != null) return supportedVideoSizes; } if (Version.sdkAbove(5)) { supportedVideoSizes = AndroidCameraRecord5Impl.oneShotSupportedVideoSizes(); } // eventually null return supportedVideoSizes; } public boolean isRecording() { if (recorder != null) { return recorder.isStarted(); } return false; } public void invalidateParameters() { stopVideoRecording(); parameters = null; } public boolean outputIsPortrait() { final int rotation = bufferRotationForCorrectImageOrientation(); final boolean isPortrait = (rotation % 180) == 90; Log.d(tag, "Camera sensor in portrait orientation ?" + isPortrait); return isPortrait; } public static int getNumberOfCameras() { if (Version.sdkAbove(9)) return getNumberOfCameras9(); // Use hacks to guess the number of cameras if (Build.DEVICE.startsWith("GT-I9000")) { // Galaxy S has 2 cameras return 2; } else return 1; } private static int getNumberOfCameras9() { return Camera.getNumberOfCameras(); } public boolean isCameraOrientationPortrait() { return (getCameraOrientation() % 180) == 90; } public int getCameraOrientation() { if (Version.sdkAbove(9)) return getCameraOrientation9(); // Use hacks to guess orientation of the camera if (cameraId == 2 && Build.DEVICE.startsWith("GT-I9000")) { // Galaxy S rear camera // mounted in landscape for a portrait phone orientation return 90; } return 0; } private int getCameraOrientation9() { android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo(); Camera.getCameraInfo(cameraId, info); return info.orientation; } public boolean isFrontCamera() { if (Version.sdkAbove(9)) return isFrontCamera9(); // Use hacks to guess facing of the camera if (cameraId == 2 && Build.DEVICE.startsWith("GT-I9000")) { return true; } return false; } private boolean isFrontCamera9() { return isFrontCamera9(cameraId); } private boolean isFrontCamera9(int cameraId) { android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo(); Camera.getCameraInfo(cameraId, info); return info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT ? true : false; } private int bufferRotationForCorrectImageOrientation() { final int cameraOrientation = getCameraOrientation(); final int rotation = Version.sdkAbove(8) ? (360 - cameraOrientation + 90 - phoneOrientation) % 360 : 0; Log.d(tag, "Capture video buffer will need a rotation of " + rotation + " degrees : camera " + cameraOrientation + ", phone " + phoneOrientation); return rotation; } }