diff --git a/AndroidCameraRecord.java b/AndroidCameraRecord.java index 1696efbc7..6feafc322 100644 --- a/AndroidCameraRecord.java +++ b/AndroidCameraRecord.java @@ -39,14 +39,12 @@ public abstract class AndroidCameraRecord { private PreviewCallback storedPreviewCallback; private boolean previewStarted; - protected int displayOrientation; protected static final String tag="Linphone"; private List supportedVideoSizes; private Size currentPreviewSize; public AndroidCameraRecord(RecorderParams parameters) { this.params = parameters; - setDisplayOrientation(parameters.rotation); } protected List getSupportedPreviewSizes(Camera.Parameters parameters) { @@ -67,7 +65,7 @@ public abstract class AndroidCameraRecord { } - camera=Camera.open(); + camera = openCamera(params.cameraId); camera.setErrorCallback(new ErrorCallback() { public void onError(int error, Camera camera) { Log.e(tag, "Camera error : " + error); @@ -84,9 +82,10 @@ public abstract class AndroidCameraRecord { } - if (!params.videoDimensionsInverted) { + if (params.width >= params.height) { parameters.setPreviewSize(params.width, params.height); } else { + // invert height and width parameters.setPreviewSize(params.height, params.width); } parameters.setPreviewFrameRate(Math.round(params.fps)); @@ -126,6 +125,10 @@ public abstract class AndroidCameraRecord { + protected Camera openCamera(int cameraId) { + return Camera.open(); + } + protected void onSettingCameraParameters(Parameters parameters) {} /** @@ -164,24 +167,8 @@ public abstract class AndroidCameraRecord { protected abstract void lowLevelSetPreviewCallback(Camera camera, PreviewCallback cb); - public void setDisplayOrientation(int rotation) { - displayOrientation = rotation; - } - protected int getDisplayOrientation() {return displayOrientation;} - - protected int rotateCapturedFrame() { - if (params.videoDimensionsInverted) { - return 1; // always rotate 90° - } else if (params.cameraId == 2) { - return 0; - } else { - return (4 + 1 - displayOrientation) % 4; - } - } - - public static class RecorderParams { public float fps; public int height; @@ -191,7 +178,6 @@ public abstract class AndroidCameraRecord { public int cameraId; public int rotation; public SurfaceView surfaceView; - public boolean videoDimensionsInverted; public RecorderParams(long ptr) { filterDataNativePtr = ptr; @@ -200,7 +186,6 @@ public abstract class AndroidCameraRecord { - public boolean isStarted() { return previewStarted; } @@ -215,4 +200,5 @@ public abstract class AndroidCameraRecord { return currentPreviewSize.width * currentPreviewSize.height * 3 /2; } + } diff --git a/AndroidCameraRecordImplAPI5.java b/AndroidCameraRecord5Impl.java similarity index 93% rename from AndroidCameraRecordImplAPI5.java rename to AndroidCameraRecord5Impl.java index 7ff307d03..18e487d12 100644 --- a/AndroidCameraRecordImplAPI5.java +++ b/AndroidCameraRecord5Impl.java @@ -26,9 +26,9 @@ import android.hardware.Camera.Size; import android.util.Log; -public class AndroidCameraRecordImplAPI5 extends AndroidCameraRecordImpl { +public class AndroidCameraRecord5Impl extends AndroidCameraRecordImpl { - public AndroidCameraRecordImplAPI5(RecorderParams parameters) { + public AndroidCameraRecord5Impl(RecorderParams parameters) { super(parameters); } diff --git a/AndroidCameraRecordAPI8Impl.java b/AndroidCameraRecord8Impl.java similarity index 86% rename from AndroidCameraRecordAPI8Impl.java rename to AndroidCameraRecord8Impl.java index 9655428db..490d9ad97 100644 --- a/AndroidCameraRecordAPI8Impl.java +++ b/AndroidCameraRecord8Impl.java @@ -30,10 +30,10 @@ import android.util.Log; * @author Guillaume Beraudo * */ -public class AndroidCameraRecordAPI8Impl extends AndroidCameraRecordImplAPI5 { +public class AndroidCameraRecord8Impl extends AndroidCameraRecord5Impl { - public AndroidCameraRecordAPI8Impl(RecorderParams parameters) { + public AndroidCameraRecord8Impl(RecorderParams parameters) { super(parameters); } @@ -66,10 +66,6 @@ public class AndroidCameraRecordAPI8Impl extends AndroidCameraRecordImplAPI5 { protected void onSettingCameraParameters(Parameters parameters) { super.onSettingCameraParameters(parameters); // Only on v8 hardware - camera.setDisplayOrientation(90 * getPreviewCaptureRotation()); - } - - private int getPreviewCaptureRotation() { - return (4 + 1 - displayOrientation) % 4; + camera.setDisplayOrientation(rotation); } } diff --git a/AndroidCameraRecord9Impl.java b/AndroidCameraRecord9Impl.java new file mode 100644 index 000000000..0ddb283d2 --- /dev/null +++ b/AndroidCameraRecord9Impl.java @@ -0,0 +1,40 @@ +/* +AndroidCameraRecord9Impl.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 android.hardware.Camera; + +/** + * + * Android >= 9 (2.3) version. + * @author Guillaume Beraudo + * + */ +public class AndroidCameraRecord9Impl extends AndroidCameraRecord8Impl { + + + public AndroidCameraRecord9Impl(RecorderParams parameters) { + super(parameters); + } + + @Override + protected Camera openCamera(int cameraId) { + return Camera.open(cameraId); + } +} diff --git a/AndroidCameraRecordImpl.java b/AndroidCameraRecordImpl.java index daf26ea4e..f1a4f4844 100644 --- a/AndroidCameraRecordImpl.java +++ b/AndroidCameraRecordImpl.java @@ -34,19 +34,19 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev private double timeElapsedBetweenFrames = 0; private long lastFrameTime = 0; private final double expectedTimeBetweenFrames; - private boolean sizesInverted; + protected final int rotation; public AndroidCameraRecordImpl(RecorderParams parameters) { super(parameters); expectedTimeBetweenFrames = 1d / Math.round(parameters.fps); filterCtxPtr = parameters.filterDataNativePtr; - sizesInverted = parameters.videoDimensionsInverted; + rotation = parameters.rotation; storePreviewCallBack(this); } - private native void putImage(long filterCtxPtr, byte[] buffer, int rotate, boolean sizesInverted); + private native void putImage(long filterCtxPtr, byte[] buffer, int rotate); public void onPreviewFrame(byte[] data, Camera camera) { @@ -69,7 +69,7 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev long curTime = System.currentTimeMillis(); if (lastFrameTime == 0) { lastFrameTime = curTime; - putImage(filterCtxPtr, data, rotateCapturedFrame(), sizesInverted); + putImage(filterCtxPtr, data, rotation); return; } @@ -82,7 +82,7 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev timeElapsedBetweenFrames = currentTimeElapsed; // Log.d("onPreviewFrame: ", Integer.toString(data.length)); - putImage(filterCtxPtr, data, rotateCapturedFrame(), sizesInverted); + putImage(filterCtxPtr, data, rotation); } @@ -92,6 +92,4 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev camera.setPreviewCallback(cb); } - - } diff --git a/AndroidCameraRecordManager.java b/AndroidCameraRecordManager.java index a6c38d76d..5c9f4d7c8 100644 --- a/AndroidCameraRecordManager.java +++ b/AndroidCameraRecordManager.java @@ -22,6 +22,7 @@ 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; @@ -38,14 +39,9 @@ import android.view.SurfaceHolder.Callback; * */ public class AndroidCameraRecordManager { - private static final int version = Integer.parseInt(Build.VERSION.SDK); private static final String tag = "Linphone"; private static AndroidCameraRecordManager instance; - // singleton - private AndroidCameraRecordManager() {} - - /** * @return instance */ @@ -59,31 +55,76 @@ public class AndroidCameraRecordManager { private AndroidCameraRecord.RecorderParams parameters; private SurfaceView surfaceView; private boolean muted; - + private int cameraId; private AndroidCameraRecord recorder; - - private List supportedVideoSizes; - private int rotation; + private int phoneOrientation; + public int getPhoneOrientation() {return phoneOrientation;} + public void setPhoneOrientation(int degrees) {this.phoneOrientation = degrees;} - private boolean useFrontCamera; + 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 (useFrontCamera == value) return; - this.useFrontCamera = 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(); + parameters.cameraId = cameraId; if (isRecording()) { stopVideoRecording(); tryToStartVideoRecording(); } } - } - public boolean isUseFrontCamera() {return useFrontCamera;} - public boolean toggleUseFrontCamera() { - setUseFrontCamera(!useFrontCamera); - return useFrontCamera; + + return !previousUseFront; } @@ -94,16 +135,14 @@ public class AndroidCameraRecordManager { p.fps = fps; p.width = width; p.height = height; - p.cameraId = cameraId(); - p.videoDimensionsInverted = width < height; - // width and height will be inverted in Recorder on startPreview + p.cameraId = cameraId; parameters = p; tryToStartVideoRecording(); } - public final void setSurfaceView(final SurfaceView sv, final int rotation) { - this.rotation = useFrontCamera ? 1 : rotation; + public final void setSurfaceView(final SurfaceView sv, final int phoneOrientation) { + this.phoneOrientation = phoneOrientation; SurfaceHolder holder = sv.getHolder(); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); @@ -152,12 +191,15 @@ public class AndroidCameraRecordManager { private void tryToStartVideoRecording() { if (muted || surfaceView == null || parameters == null) return; - parameters.rotation = rotation; + parameters.rotation = bufferRotationForCorrectImageOrientation(); + parameters.surfaceView = surfaceView; - if (version >= 8) { - recorder = new AndroidCameraRecordAPI8Impl(parameters); - } else if (version >= 5) { - recorder = new AndroidCameraRecordImplAPI5(parameters); + 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); } @@ -188,8 +230,8 @@ public class AndroidCameraRecordManager { if (supportedVideoSizes != null) return supportedVideoSizes; } - if (version >= 5) { - supportedVideoSizes = AndroidCameraRecordImplAPI5.oneShotSupportedVideoSizes(); + if (Version.sdkAbove(5)) { + supportedVideoSizes = AndroidCameraRecord5Impl.oneShotSupportedVideoSizes(); } // eventually null @@ -212,34 +254,86 @@ public class AndroidCameraRecordManager { parameters = null; } - /** - * Naive simple version. - * @param askedSize - * @return - */ - public VideoSize doYouSupportThisVideoSize(VideoSize askedSize) { - Log.d(tag, "Asking camera if it supports size "+askedSize); - if (useFrontCamera && askedSize.isPortrait()) { - return askedSize.createInverted(); // only landscape supported - } else { - return askedSize; - } + public boolean outputIsPortrait() { + final int rotation = bufferRotationForCorrectImageOrientation(); + final boolean isPortrait = (rotation % 180) == 90; + + Log.d(tag, "Camera sensor in portrait orientation ?" + isPortrait); + return isPortrait; } - private VideoSize closestVideoSize(VideoSize vSize, int defaultSizeCode, boolean defaultIsPortrait) { - VideoSize testSize = vSize.isPortrait() ? vSize.createInverted() : vSize; - - for (Size s : AndroidCameraRecordManager.getInstance().supportedVideoSizes()) { - if (s.height == testSize.height && s.width == testSize.width) { - return vSize; - } - } - - return VideoSize.createStandard(defaultSizeCode, defaultIsPortrait); + + + 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 final int rearCamId() {return 1;} - private static final int frontCamId() {return 2;} - private final int cameraId() {return useFrontCamera? frontCamId() : rearCamId(); } + 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; + } } diff --git a/Version.java b/Version.java new file mode 100644 index 000000000..0ec07fc25 --- /dev/null +++ b/Version.java @@ -0,0 +1,41 @@ +/* +Version.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 android.os.Build; + +/** + * Centralize version access and allow simulation of lower versions. + * @author Guillaume Beraudo + */ +public class Version { + + private static final int buildVersion = +// Integer.parseInt(Build.VERSION.SDK); + 7; // 2.1 + + public static final boolean sdkAbove(int value) { + return buildVersion >= value; + } + + public static final boolean sdkBelow(int value) { + return buildVersion < value; + } + +}