diff --git a/src/org/linphone/BandwidthManager.java b/src/org/linphone/BandwidthManager.java index f313e604b..830823c5b 100644 --- a/src/org/linphone/BandwidthManager.java +++ b/src/org/linphone/BandwidthManager.java @@ -18,8 +18,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.linphone; +import java.util.List; + import org.linphone.core.LinphoneCallParams; import org.linphone.core.LinphoneCore; +import org.linphone.core.Log; import org.linphone.core.VideoSize; import org.linphone.core.video.AndroidCameraRecordManager; @@ -81,7 +84,10 @@ public class BandwidthManager { // Setting Linphone Core Preferred Video Size boolean bandwidthOKForVideo = isVideoPossible(); if (bandwidthOKForVideo) { - VideoSize targetVideoSize = getMaximumVideoSize(); + AndroidCameraRecordManager acrm = AndroidCameraRecordManager.getInstance(); + boolean isPortrait=acrm.isFrameToBeShownPortrait(); + VideoSize targetVideoSize=maxSupportedVideoSize(isPortrait, getMaximumVideoSize(isPortrait), + acrm.supportedVideoSizes()); lc.setPreferredVideoSize(targetVideoSize); VideoSize actualVideoSize = lc.getPreferredVideoSize(); @@ -103,6 +109,26 @@ public class BandwidthManager { } + private VideoSize maxSupportedVideoSize(boolean isPortrait, VideoSize maximumVideoSize, + List supportedVideoSizes) { + Log.d("Searching for maximum video size for ", isPortrait ? "portrait" : "landscape","capture from (",maximumVideoSize); + VideoSize selected = VideoSize.createStandard(VideoSize.QCIF, isPortrait); + for (VideoSize s : supportedVideoSizes) { + int sW = s.width; + int sH = s.height; + if (s.isPortrait() != isPortrait) { + sW=s.height; + sH=s.width; + } + if (sW >maximumVideoSize.width || sH>maximumVideoSize.height) continue; + if (selected.width getSupportedPreviewSizes(int cameraId); + /** * Default: no front; rear=0; default=rear * @author Guillaume Beraudo diff --git a/src/org/linphone/core/video/AndroidCameraConf5.java b/src/org/linphone/core/video/AndroidCameraConf5.java index c9c3a1ade..06c266d7a 100644 --- a/src/org/linphone/core/video/AndroidCameraConf5.java +++ b/src/org/linphone/core/video/AndroidCameraConf5.java @@ -18,8 +18,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.linphone.core.video; +import java.util.List; + import org.linphone.core.Hacks; import org.linphone.core.Log; +import org.linphone.core.VideoSize; + +import android.hardware.Camera; class AndroidCameraConf5 implements AndroidCameraConf { @@ -90,6 +95,18 @@ class AndroidCameraConf5 implements AndroidCameraConf { return false; } + + public List getSupportedPreviewSizes(int cameraId) { + if (getNumberOfCameras() >1) { + Log.w("Hack: on older devices, using video formats supported by default camera"); + } + Log.i("Opening camera to retrieve supported video sizes"); + Camera c = Camera.open(); + List sizes=VideoUtil.createList(c.getParameters().getSupportedPreviewSizes()); + c.release(); + Log.i("Camera opened to retrieve supported video sizes released"); + return sizes; + } diff --git a/src/org/linphone/core/video/AndroidCameraConf9.java b/src/org/linphone/core/video/AndroidCameraConf9.java index d0ef0faa5..02e909895 100644 --- a/src/org/linphone/core/video/AndroidCameraConf9.java +++ b/src/org/linphone/core/video/AndroidCameraConf9.java @@ -18,12 +18,18 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.linphone.core.video; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.linphone.core.Log; +import org.linphone.core.VideoSize; import android.hardware.Camera; class AndroidCameraConf9 implements AndroidCameraConf { private AndroidCameras foundCameras; + private Map> supportedSizes = new HashMap>(); public AndroidCameras getFoundCameras() {return foundCameras;} public AndroidCameraConf9() { @@ -38,9 +44,19 @@ class AndroidCameraConf9 implements AndroidCameraConf { } else { foundCameras.rear = id; } + supportedSizes.put(id, findSupportedVideoSizes(id)); } } + private List findSupportedVideoSizes(int id) { + Log.i("Opening camera ",id," to retrieve supported video sizes"); + Camera c = Camera.open(id); + List sizes=VideoUtil.createList(c.getParameters().getSupportedPreviewSizes()); + c.release(); + Log.i("Camera ",id," opened to retrieve supported video sizes released"); + return sizes; + } + public int getNumberOfCameras() { return Camera.getNumberOfCameras(); } @@ -58,4 +74,8 @@ class AndroidCameraConf9 implements AndroidCameraConf { return info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT ? true : false; } + public List getSupportedPreviewSizes(int cameraId) { + return supportedSizes.get(cameraId); + } + } diff --git a/src/org/linphone/core/video/AndroidCameraRecord.java b/src/org/linphone/core/video/AndroidCameraRecord.java index dec9338b2..bb9fd9786 100644 --- a/src/org/linphone/core/video/AndroidCameraRecord.java +++ b/src/org/linphone/core/video/AndroidCameraRecord.java @@ -18,12 +18,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.linphone.core.video; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.linphone.core.Log; import org.linphone.core.Version; +import org.linphone.core.VideoSize; import android.hardware.Camera; import android.hardware.Camera.AutoFocusCallback; @@ -44,7 +44,7 @@ public abstract class AndroidCameraRecord implements AutoFocusCallback { private PreviewCallback storedPreviewCallback; private boolean previewStarted; - private List supportedVideoSizes; + private List supportedVideoSizes; private Size currentPreviewSize; public AndroidCameraRecord(RecorderParams parameters) { @@ -55,6 +55,23 @@ public abstract class AndroidCameraRecord implements AutoFocusCallback { return Collections.emptyList(); } + private int[] findClosestFpsRange(int expectedFps, List fpsRanges) { + Log.d("Searching for closest fps range from ",expectedFps); + int measure = Integer.MAX_VALUE; + int[] closestRange = {expectedFps,expectedFps}; + for (int[] curRange : fpsRanges) { + if (curRange[0] > expectedFps || curRange[1] < expectedFps) continue; + int curMeasure = Math.abs(curRange[0] - expectedFps) + + Math.abs(curRange[1] - expectedFps); + if (curMeasure < measure) { + closestRange=curRange; + Log.d("a better range has been found: w=",closestRange[0],",h=",closestRange[1]); + } + } + Log.d("The closest fps range is w=",closestRange[0],",h=",closestRange[1]); + return closestRange; + } + public synchronized void startPreview() { // FIXME throws exception? if (previewStarted) { Log.w("Already started"); @@ -83,12 +100,12 @@ public abstract class AndroidCameraRecord implements AutoFocusCallback { Camera.Parameters parameters=camera.getParameters(); - if (Version.sdkStrictlyBelow(9)) { + if (Version.sdkStrictlyBelow(Version.API09_GINGERBREAD_23)) { parameters.set("camera-id",params.cameraId); } if (supportedVideoSizes == null) { - supportedVideoSizes = new ArrayList(getSupportedPreviewSizes(parameters)); + supportedVideoSizes = VideoUtil.createList(getSupportedPreviewSizes(parameters)); } @@ -101,7 +118,13 @@ public abstract class AndroidCameraRecord implements AutoFocusCallback { // should setParameters and get again to have the real one?? currentPreviewSize = parameters.getPreviewSize(); - parameters.setPreviewFrameRate(Math.round(params.fps)); + // Frame rate + if (Version.sdkStrictlyBelow(Version.API09_GINGERBREAD_23)) { + parameters.setPreviewFrameRate(Math.round(params.fps)); + } else { + int[] range=findClosestFpsRange((int)(1000*params.fps), parameters.getSupportedPreviewFpsRange()); + parameters.setPreviewFpsRange(range[0], range[1]); + } onSettingCameraParameters(parameters); @@ -223,8 +246,8 @@ public abstract class AndroidCameraRecord implements AutoFocusCallback { return previewStarted; } - public List getSupportedVideoSizes() { - return new ArrayList(supportedVideoSizes); + public List getSupportedVideoSizes() { + return supportedVideoSizes; } diff --git a/src/org/linphone/core/video/AndroidCameraRecord5.java b/src/org/linphone/core/video/AndroidCameraRecord5.java index 03f9123db..45f94cd29 100644 --- a/src/org/linphone/core/video/AndroidCameraRecord5.java +++ b/src/org/linphone/core/video/AndroidCameraRecord5.java @@ -135,4 +135,5 @@ class AndroidCameraRecord5 extends AndroidCameraRecord implements PreviewCallbac camera.setPreviewCallback(cb); } + } diff --git a/src/org/linphone/core/video/AndroidCameraRecordManager.java b/src/org/linphone/core/video/AndroidCameraRecordManager.java index 86d12fffa..22f437d21 100644 --- a/src/org/linphone/core/video/AndroidCameraRecordManager.java +++ b/src/org/linphone/core/video/AndroidCameraRecordManager.java @@ -20,14 +20,13 @@ package org.linphone.core.video; import java.util.List; - import org.linphone.core.LinphoneCore; import org.linphone.core.Log; import org.linphone.core.Version; +import org.linphone.core.VideoSize; import org.linphone.core.video.AndroidCameraRecord.RecorderParams; import android.content.Context; -import android.hardware.Camera.Size; import android.view.OrientationEventListener; import android.view.SurfaceHolder; import android.view.SurfaceView; @@ -63,7 +62,6 @@ public class AndroidCameraRecordManager { private int cameraId; private AndroidCameraRecord recorder; - private List supportedVideoSizes; private int mAlwaysChangingPhoneOrientation=0; @@ -126,11 +124,14 @@ public class AndroidCameraRecordManager { p.cameraId = cameraId; p.isFrontCamera = isUseFrontCamera(); parameters = p; - + + // Mirror the sent frames in order to make them readable + // (otherwise it is mirrored and thus unreadable) if (p.isFrontCamera) { - if (!isCameraOrientationPortrait()) { - // Code for Nexus S: to be tested - p.mirror = RecorderParams.MirrorType.CENTRAL; + if (!isCameraMountedPortrait()) { + // Code for Nexus S + if (isFrameToBeShownPortrait()) + p.mirror = RecorderParams.MirrorType.CENTRAL; } else { // Code for Galaxy S like: camera mounted landscape when phone hold portrait p.mirror = RecorderParams.MirrorType.HORIZONTAL; @@ -236,20 +237,9 @@ public class AndroidCameraRecordManager { - /** - * FIXME select right camera - */ - public List supportedVideoSizes() { - if (supportedVideoSizes != null) { - return supportedVideoSizes; - } - - if (recorder != null) { - supportedVideoSizes = recorder.getSupportedVideoSizes(); - if (supportedVideoSizes != null) return supportedVideoSizes; - } - - return supportedVideoSizes; + public List supportedVideoSizes() { + Log.d("Using supportedVideoSizes of camera ",cameraId); + return cc.getSupportedPreviewSizes(cameraId); } @@ -267,20 +257,28 @@ public class AndroidCameraRecordManager { parameters = null; } - public boolean isOutputPortraitDependingOnCameraAndPhoneOrientations() { + /** Depends on currently selected camera, camera mounted portrait/landscape, current phone orientation */ + public boolean isFrameToBeShownPortrait() { final int rotation = bufferRotationToCompensateCameraAndPhoneOrientations(); - final boolean isPortrait = (rotation % 180) == 90; - - Log.d("Camera sensor in ", isPortrait? "portrait":"landscape"," orientation."); + + boolean isPortrait; + if (isCameraMountedPortrait()) { + // Nexus S + isPortrait = (rotation % 180) == 0; + } else { + isPortrait = (rotation % 180) == 90; + } + + Log.d("The frame to be shown and sent to remote is ", isPortrait? "portrait":"landscape"," orientation."); return isPortrait; } - - - public boolean isCameraOrientationPortrait() { + + + public boolean isCameraMountedPortrait() { return (cc.getCameraOrientation(cameraId) % 180) == 0; } @@ -296,8 +294,10 @@ public class AndroidCameraRecordManager { final int phoneOrientation = mAlwaysChangingPhoneOrientation; final int cameraOrientation = cc.getCameraOrientation(cameraId); int frontCameraCorrection = 0; - if (cc.isFrontCamera(cameraId)) // TODO: check with other phones (Nexus S, ...) - frontCameraCorrection=180; // hack that "just works" on Galaxy S. + if (cc.isFrontCamera(cameraId)) { + frontCameraCorrection=180; // hack that "just works" on Galaxy S and Nexus S. + // See also magic with mirrors in setParametersFromFilter + } final int rotation = (cameraOrientation + phoneOrientation + frontCameraCorrection) % 360; Log.d("Capture video buffer of cameraId=",cameraId, " will need a rotation of ",rotation, @@ -342,7 +342,7 @@ public class AndroidCameraRecordManager { */ public boolean isOutputOrientationMismatch(LinphoneCore lc) { final boolean currentlyPortrait = lc.getPreferredVideoSize().isPortrait(); - final boolean shouldBePortrait = isOutputPortraitDependingOnCameraAndPhoneOrientations(); + final boolean shouldBePortrait = isFrameToBeShownPortrait(); return currentlyPortrait ^ shouldBePortrait; } diff --git a/src/org/linphone/core/video/VideoUtil.java b/src/org/linphone/core/video/VideoUtil.java new file mode 100644 index 000000000..065557b77 --- /dev/null +++ b/src/org/linphone/core/video/VideoUtil.java @@ -0,0 +1,42 @@ +/* +VideoUtil.java +Copyright (C) 2011 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.video; + +import java.util.ArrayList; +import java.util.List; + +import org.linphone.core.VideoSize; + +import android.hardware.Camera.Size; + +/** + * @author Guillaume Beraudo + */ +final class VideoUtil { + + private VideoUtil() {} + + public static List createList(List supportedVideoSizes) { + List converted = new ArrayList(supportedVideoSizes.size()); + for (Size s : supportedVideoSizes) { + converted.add(new VideoSize(s.width, s.height)); + } + return converted; + } +}