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;
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<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) {
switch (profile) {
case LOW_RESOLUTION:
@ -119,7 +145,7 @@ public class BandwidthManager {
return currentProfile != LOW_BANDWIDTH;
}
public VideoSize getMaximumVideoSize() {
return maximumVideoSize(currentProfile, AndroidCameraRecordManager.getInstance().isOutputPortraitDependingOnCameraAndPhoneOrientations());
private VideoSize getMaximumVideoSize(boolean isPortrait) {
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
// as they do not support surfaceView rotation
setRequestedOrientation(recordManager.isCameraOrientationPortrait() ?
setRequestedOrientation(recordManager.isCameraMountedPortrait() ?
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT :
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
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;
import java.util.List;
import org.linphone.core.VideoSize;
/**
* @author Guillaume Beraudo
@ -33,6 +37,8 @@ interface AndroidCameraConf {
boolean isFrontCamera(int cameraId);
List<VideoSize> getSupportedPreviewSizes(int cameraId);
/**
* Default: no front; rear=0; default=rear
* @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;
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<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;
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<Integer,List<VideoSize>> supportedSizes = new HashMap<Integer, List<VideoSize>>();
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<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() {
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<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;
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 <Size> supportedVideoSizes;
private List <VideoSize> 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<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?
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<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??
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<Size> getSupportedVideoSizes() {
return new ArrayList<Size>(supportedVideoSizes);
public List<VideoSize> getSupportedVideoSizes() {
return supportedVideoSizes;
}

View file

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

View file

@ -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<Size> 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<Size> supportedVideoSizes() {
if (supportedVideoSizes != null) {
return supportedVideoSizes;
}
if (recorder != null) {
supportedVideoSizes = recorder.getSupportedVideoSizes();
if (supportedVideoSizes != null) return supportedVideoSizes;
}
return supportedVideoSizes;
public List<VideoSize> 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;
}

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;
}
}