Fix video on Nexus S.

This commit is contained in:
Guillaume Beraudo 2011-08-04 11:26:19 +02:00
parent a68b03baab
commit 4d47899e2f
9 changed files with 177 additions and 42 deletions

View file

@ -18,8 +18,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/ */
package org.linphone; package org.linphone;
import java.util.List;
import org.linphone.core.LinphoneCallParams; import org.linphone.core.LinphoneCallParams;
import org.linphone.core.LinphoneCore; import org.linphone.core.LinphoneCore;
import org.linphone.core.Log;
import org.linphone.core.VideoSize; import org.linphone.core.VideoSize;
import org.linphone.core.video.AndroidCameraRecordManager; import org.linphone.core.video.AndroidCameraRecordManager;
@ -81,7 +84,10 @@ public class BandwidthManager {
// Setting Linphone Core Preferred Video Size // Setting Linphone Core Preferred Video Size
boolean bandwidthOKForVideo = isVideoPossible(); boolean bandwidthOKForVideo = isVideoPossible();
if (bandwidthOKForVideo) { if (bandwidthOKForVideo) {
VideoSize targetVideoSize = getMaximumVideoSize(); AndroidCameraRecordManager acrm = AndroidCameraRecordManager.getInstance();
boolean isPortrait=acrm.isFrameToBeShownPortrait();
VideoSize targetVideoSize=maxSupportedVideoSize(isPortrait, getMaximumVideoSize(isPortrait),
acrm.supportedVideoSizes());
lc.setPreferredVideoSize(targetVideoSize); lc.setPreferredVideoSize(targetVideoSize);
VideoSize actualVideoSize = lc.getPreferredVideoSize(); VideoSize actualVideoSize = lc.getPreferredVideoSize();
@ -103,6 +109,26 @@ public class BandwidthManager {
} }
private VideoSize maxSupportedVideoSize(boolean isPortrait, VideoSize maximumVideoSize,
List<VideoSize> 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 <sW && selected.height <sH) {
selected=new VideoSize(sW, sH);
Log.d("A better video size has been found: ",selected);
}
}
return selected;
}
private VideoSize maximumVideoSize(int profile, boolean cameraIsPortrait) { private VideoSize maximumVideoSize(int profile, boolean cameraIsPortrait) {
switch (profile) { switch (profile) {
case LOW_RESOLUTION: case LOW_RESOLUTION:
@ -119,7 +145,7 @@ public class BandwidthManager {
return currentProfile != LOW_BANDWIDTH; return currentProfile != LOW_BANDWIDTH;
} }
public VideoSize getMaximumVideoSize() { private VideoSize getMaximumVideoSize(boolean isPortrait) {
return maximumVideoSize(currentProfile, AndroidCameraRecordManager.getInstance().isOutputPortraitDependingOnCameraAndPhoneOrientations()); return maximumVideoSize(currentProfile, isPortrait);
} }
} }

View file

@ -84,7 +84,7 @@ public class VideoCallActivity extends SoftVolumeActivity implements OnCapturing
// Force to display in portrait orientation for old devices // Force to display in portrait orientation for old devices
// as they do not support surfaceView rotation // as they do not support surfaceView rotation
setRequestedOrientation(recordManager.isCameraOrientationPortrait() ? setRequestedOrientation(recordManager.isCameraMountedPortrait() ?
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT :
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
resizeCapturePreview(mVideoCaptureView); resizeCapturePreview(mVideoCaptureView);

View file

@ -18,6 +18,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/ */
package org.linphone.core.video; package org.linphone.core.video;
import java.util.List;
import org.linphone.core.VideoSize;
/** /**
* @author Guillaume Beraudo * @author Guillaume Beraudo
@ -33,6 +37,8 @@ interface AndroidCameraConf {
boolean isFrontCamera(int cameraId); boolean isFrontCamera(int cameraId);
List<VideoSize> getSupportedPreviewSizes(int cameraId);
/** /**
* Default: no front; rear=0; default=rear * Default: no front; rear=0; default=rear
* @author Guillaume Beraudo * @author Guillaume Beraudo

View file

@ -18,8 +18,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/ */
package org.linphone.core.video; package org.linphone.core.video;
import java.util.List;
import org.linphone.core.Hacks; import org.linphone.core.Hacks;
import org.linphone.core.Log; import org.linphone.core.Log;
import org.linphone.core.VideoSize;
import android.hardware.Camera;
class AndroidCameraConf5 implements AndroidCameraConf { class AndroidCameraConf5 implements AndroidCameraConf {
@ -90,6 +95,18 @@ class AndroidCameraConf5 implements AndroidCameraConf {
return false; return false;
} }
public List<VideoSize> 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<VideoSize> sizes=VideoUtil.createList(c.getParameters().getSupportedPreviewSizes());
c.release();
Log.i("Camera opened to retrieve supported video sizes released");
return sizes;
}

View file

@ -18,12 +18,18 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/ */
package org.linphone.core.video; 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.Log;
import org.linphone.core.VideoSize;
import android.hardware.Camera; import android.hardware.Camera;
class AndroidCameraConf9 implements AndroidCameraConf { class AndroidCameraConf9 implements AndroidCameraConf {
private AndroidCameras foundCameras; private AndroidCameras foundCameras;
private Map<Integer,List<VideoSize>> supportedSizes = new HashMap<Integer, List<VideoSize>>();
public AndroidCameras getFoundCameras() {return foundCameras;} public AndroidCameras getFoundCameras() {return foundCameras;}
public AndroidCameraConf9() { public AndroidCameraConf9() {
@ -38,9 +44,19 @@ class AndroidCameraConf9 implements AndroidCameraConf {
} else { } else {
foundCameras.rear = id; foundCameras.rear = id;
} }
supportedSizes.put(id, findSupportedVideoSizes(id));
} }
} }
private List<VideoSize> findSupportedVideoSizes(int id) {
Log.i("Opening camera ",id," to retrieve supported video sizes");
Camera c = Camera.open(id);
List<VideoSize> sizes=VideoUtil.createList(c.getParameters().getSupportedPreviewSizes());
c.release();
Log.i("Camera ",id," opened to retrieve supported video sizes released");
return sizes;
}
public int getNumberOfCameras() { public int getNumberOfCameras() {
return Camera.getNumberOfCameras(); return Camera.getNumberOfCameras();
} }
@ -58,4 +74,8 @@ class AndroidCameraConf9 implements AndroidCameraConf {
return info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT ? true : false; return info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT ? true : false;
} }
public List<VideoSize> getSupportedPreviewSizes(int cameraId) {
return supportedSizes.get(cameraId);
}
} }

View file

@ -18,12 +18,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/ */
package org.linphone.core.video; package org.linphone.core.video;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.linphone.core.Log; import org.linphone.core.Log;
import org.linphone.core.Version; import org.linphone.core.Version;
import org.linphone.core.VideoSize;
import android.hardware.Camera; import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback; import android.hardware.Camera.AutoFocusCallback;
@ -44,7 +44,7 @@ public abstract class AndroidCameraRecord implements AutoFocusCallback {
private PreviewCallback storedPreviewCallback; private PreviewCallback storedPreviewCallback;
private boolean previewStarted; private boolean previewStarted;
private List <Size> supportedVideoSizes; private List <VideoSize> supportedVideoSizes;
private Size currentPreviewSize; private Size currentPreviewSize;
public AndroidCameraRecord(RecorderParams parameters) { public AndroidCameraRecord(RecorderParams parameters) {
@ -55,6 +55,23 @@ public abstract class AndroidCameraRecord implements AutoFocusCallback {
return Collections.emptyList(); return Collections.emptyList();
} }
private int[] findClosestFpsRange(int expectedFps, List<int[]> 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? public synchronized void startPreview() { // FIXME throws exception?
if (previewStarted) { if (previewStarted) {
Log.w("Already started"); Log.w("Already started");
@ -83,12 +100,12 @@ public abstract class AndroidCameraRecord implements AutoFocusCallback {
Camera.Parameters parameters=camera.getParameters(); Camera.Parameters parameters=camera.getParameters();
if (Version.sdkStrictlyBelow(9)) { if (Version.sdkStrictlyBelow(Version.API09_GINGERBREAD_23)) {
parameters.set("camera-id",params.cameraId); parameters.set("camera-id",params.cameraId);
} }
if (supportedVideoSizes == null) { if (supportedVideoSizes == null) {
supportedVideoSizes = new ArrayList<Size>(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?? // should setParameters and get again to have the real one??
currentPreviewSize = parameters.getPreviewSize(); 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); onSettingCameraParameters(parameters);
@ -223,8 +246,8 @@ public abstract class AndroidCameraRecord implements AutoFocusCallback {
return previewStarted; return previewStarted;
} }
public List<Size> getSupportedVideoSizes() { public List<VideoSize> getSupportedVideoSizes() {
return new ArrayList<Size>(supportedVideoSizes); return supportedVideoSizes;
} }

View file

@ -135,4 +135,5 @@ class AndroidCameraRecord5 extends AndroidCameraRecord implements PreviewCallbac
camera.setPreviewCallback(cb); camera.setPreviewCallback(cb);
} }
} }

View file

@ -20,14 +20,13 @@ package org.linphone.core.video;
import java.util.List; import java.util.List;
import org.linphone.core.LinphoneCore; import org.linphone.core.LinphoneCore;
import org.linphone.core.Log; import org.linphone.core.Log;
import org.linphone.core.Version; import org.linphone.core.Version;
import org.linphone.core.VideoSize;
import org.linphone.core.video.AndroidCameraRecord.RecorderParams; import org.linphone.core.video.AndroidCameraRecord.RecorderParams;
import android.content.Context; import android.content.Context;
import android.hardware.Camera.Size;
import android.view.OrientationEventListener; import android.view.OrientationEventListener;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
import android.view.SurfaceView; import android.view.SurfaceView;
@ -63,7 +62,6 @@ public class AndroidCameraRecordManager {
private int cameraId; private int cameraId;
private AndroidCameraRecord recorder; private AndroidCameraRecord recorder;
private List<Size> supportedVideoSizes;
private int mAlwaysChangingPhoneOrientation=0; private int mAlwaysChangingPhoneOrientation=0;
@ -126,11 +124,14 @@ public class AndroidCameraRecordManager {
p.cameraId = cameraId; p.cameraId = cameraId;
p.isFrontCamera = isUseFrontCamera(); p.isFrontCamera = isUseFrontCamera();
parameters = p; parameters = p;
// Mirror the sent frames in order to make them readable
// (otherwise it is mirrored and thus unreadable)
if (p.isFrontCamera) { if (p.isFrontCamera) {
if (!isCameraOrientationPortrait()) { if (!isCameraMountedPortrait()) {
// Code for Nexus S: to be tested // Code for Nexus S
p.mirror = RecorderParams.MirrorType.CENTRAL; if (isFrameToBeShownPortrait())
p.mirror = RecorderParams.MirrorType.CENTRAL;
} else { } else {
// Code for Galaxy S like: camera mounted landscape when phone hold portrait // Code for Galaxy S like: camera mounted landscape when phone hold portrait
p.mirror = RecorderParams.MirrorType.HORIZONTAL; p.mirror = RecorderParams.MirrorType.HORIZONTAL;
@ -236,20 +237,9 @@ public class AndroidCameraRecordManager {
/** public List<VideoSize> supportedVideoSizes() {
* FIXME select right camera Log.d("Using supportedVideoSizes of camera ",cameraId);
*/ return cc.getSupportedPreviewSizes(cameraId);
public List<Size> supportedVideoSizes() {
if (supportedVideoSizes != null) {
return supportedVideoSizes;
}
if (recorder != null) {
supportedVideoSizes = recorder.getSupportedVideoSizes();
if (supportedVideoSizes != null) return supportedVideoSizes;
}
return supportedVideoSizes;
} }
@ -267,20 +257,28 @@ public class AndroidCameraRecordManager {
parameters = null; 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 int rotation = bufferRotationToCompensateCameraAndPhoneOrientations();
final boolean isPortrait = (rotation % 180) == 90;
boolean isPortrait;
Log.d("Camera sensor in ", isPortrait? "portrait":"landscape"," orientation."); 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; return isPortrait;
} }
public boolean isCameraOrientationPortrait() {
public boolean isCameraMountedPortrait() {
return (cc.getCameraOrientation(cameraId) % 180) == 0; return (cc.getCameraOrientation(cameraId) % 180) == 0;
} }
@ -296,8 +294,10 @@ public class AndroidCameraRecordManager {
final int phoneOrientation = mAlwaysChangingPhoneOrientation; final int phoneOrientation = mAlwaysChangingPhoneOrientation;
final int cameraOrientation = cc.getCameraOrientation(cameraId); final int cameraOrientation = cc.getCameraOrientation(cameraId);
int frontCameraCorrection = 0; int frontCameraCorrection = 0;
if (cc.isFrontCamera(cameraId)) // TODO: check with other phones (Nexus S, ...) if (cc.isFrontCamera(cameraId)) {
frontCameraCorrection=180; // hack that "just works" on Galaxy S. 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; final int rotation = (cameraOrientation + phoneOrientation + frontCameraCorrection) % 360;
Log.d("Capture video buffer of cameraId=",cameraId, Log.d("Capture video buffer of cameraId=",cameraId,
" will need a rotation of ",rotation, " will need a rotation of ",rotation,
@ -342,7 +342,7 @@ public class AndroidCameraRecordManager {
*/ */
public boolean isOutputOrientationMismatch(LinphoneCore lc) { public boolean isOutputOrientationMismatch(LinphoneCore lc) {
final boolean currentlyPortrait = lc.getPreferredVideoSize().isPortrait(); final boolean currentlyPortrait = lc.getPreferredVideoSize().isPortrait();
final boolean shouldBePortrait = isOutputPortraitDependingOnCameraAndPhoneOrientations(); final boolean shouldBePortrait = isFrameToBeShownPortrait();
return currentlyPortrait ^ shouldBePortrait; return currentlyPortrait ^ shouldBePortrait;
} }

View file

@ -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<VideoSize> createList(List<Size> supportedVideoSizes) {
List<VideoSize> converted = new ArrayList<VideoSize>(supportedVideoSizes.size());
for (Size s : supportedVideoSizes) {
converted.add(new VideoSize(s.width, s.height));
}
return converted;
}
}