Captured video always sent in correct orientation.

Temporarily blocked rotation of phone in video call activity.
This commit is contained in:
Guillaume Beraudo 2011-01-03 14:14:22 +01:00
parent 3a5ae54b1d
commit 2f1c9548d2
14 changed files with 324 additions and 130 deletions

View file

@ -10,4 +10,4 @@
# Indicates whether an apk should be generated for each density. # Indicates whether an apk should be generated for each density.
split.density=false split.density=false
# Project target. # Project target.
target=android-8 target=android-9

View file

@ -8,5 +8,5 @@
<item android:id="@+id/videocall_menu_terminate_call" android:title="@string/menu_videocall_terminate_call_title"></item> <item android:id="@+id/videocall_menu_terminate_call" android:title="@string/menu_videocall_terminate_call_title"></item>
<item android:id="@+id/videocall_menu_change_resolution" android:title="@string/menu_videocall_change_resolution_title"></item> <item android:id="@+id/videocall_menu_change_resolution" android:title="@string/menu_videocall_change_resolution_title"></item>
<item android:id="@+id/videocall_menu_back_to_dialer" android:title="@string/menu_videocall_back_to_dialer_title"></item> <item android:id="@+id/videocall_menu_back_to_dialer" android:title="@string/menu_videocall_back_to_dialer_title"></item>
<!--<item android:title="@string/menu_videocall_switch_camera_title" android:id="@+id/videocall_menu_switch_camera"></item>--> <item android:title="@string/menu_videocall_switch_camera_title" android:id="@+id/videocall_menu_switch_camera"></item>
</menu> </menu>

View file

@ -28,7 +28,6 @@ public class BandwidthManager {
public static final int HIGH_RESOLUTION = 0; public static final int HIGH_RESOLUTION = 0;
public static final int LOW_RESOLUTION = 1; public static final int LOW_RESOLUTION = 1;
public static final int LOW_BANDWIDTH = 2; public static final int LOW_BANDWIDTH = 2;
private static final boolean portraitMode = true; // FIXME: preference?
private static final int[][] bandwidthes = {{256,256}, {128,128}, {80,80}}; private static final int[][] bandwidthes = {{256,256}, {128,128}, {80,80}};
private static BandwidthManager instance; private static BandwidthManager instance;
@ -71,7 +70,7 @@ public class BandwidthManager {
lc.setDownloadBandwidth(bandwidthes[newProfile][1]); lc.setDownloadBandwidth(bandwidthes[newProfile][1]);
if (lc.isIncall()) { if (lc.isIncall()) {
InviteManager.getInstance().reinvite(); CallManager.getInstance().reinvite();
} else { } else {
updateWithProfileSettings(lc, null); updateWithProfileSettings(lc, null);
} }
@ -80,11 +79,9 @@ public class BandwidthManager {
public void updateWithProfileSettings(LinphoneCore lc, LinphoneCallParams callParams) { public void updateWithProfileSettings(LinphoneCore lc, LinphoneCallParams callParams) {
// Setting Linphone Core Preferred Video Size // Setting Linphone Core Preferred Video Size
AndroidCameraRecordManager cameraManager = AndroidCameraRecordManager.getInstance();
boolean bandwidthOKForVideo = isVideoPossible(); boolean bandwidthOKForVideo = isVideoPossible();
if (bandwidthOKForVideo) { if (bandwidthOKForVideo) {
VideoSize targetVideoSize = cameraManager.doYouSupportThisVideoSize(getMaximumVideoSize()); VideoSize targetVideoSize = getMaximumVideoSize();
lc.setPreferredVideoSize(targetVideoSize); lc.setPreferredVideoSize(targetVideoSize);
VideoSize actualVideoSize = lc.getPreferredVideoSize(); VideoSize actualVideoSize = lc.getPreferredVideoSize();
@ -106,12 +103,12 @@ public class BandwidthManager {
} }
private VideoSize maximumVideoSize(int profile) { private VideoSize maximumVideoSize(int profile, boolean cameraIsPortrait) {
switch (profile) { switch (profile) {
case LOW_RESOLUTION: case LOW_RESOLUTION:
return VideoSize.createStandard(VideoSize.QCIF, portraitMode); return VideoSize.createStandard(VideoSize.QCIF, cameraIsPortrait);
case HIGH_RESOLUTION: case HIGH_RESOLUTION:
return VideoSize.createStandard(VideoSize.QVGA, portraitMode); return VideoSize.createStandard(VideoSize.QVGA, cameraIsPortrait);
default: default:
throw new RuntimeException("profile not managed : " + profile); throw new RuntimeException("profile not managed : " + profile);
} }
@ -123,6 +120,6 @@ public class BandwidthManager {
} }
public VideoSize getMaximumVideoSize() { public VideoSize getMaximumVideoSize() {
return maximumVideoSize(currentProfile); return maximumVideoSize(currentProfile, AndroidCameraRecordManager.getInstance().outputIsPortrait());
} }
} }

View file

@ -1,5 +1,5 @@
/* /*
InviteManager.java CallManager.java
Copyright (C) 2010 Belledonne Communications, Grenoble, France Copyright (C) 2010 Belledonne Communications, Grenoble, France
This program is free software; you can redistribute it and/or This program is free software; you can redistribute it and/or
@ -25,13 +25,19 @@ import org.linphone.core.LinphoneCallParams;
import org.linphone.core.LinphoneCore; import org.linphone.core.LinphoneCore;
import org.linphone.core.LinphoneCoreException; import org.linphone.core.LinphoneCoreException;
public class InviteManager { /**
* Handle call updating, reinvites.
*
* @author Guillaume Beraudo
*
*/
public class CallManager {
private static InviteManager instance; private static CallManager instance;
private InviteManager() {} private CallManager() {}
public static final synchronized InviteManager getInstance() { public static final synchronized CallManager getInstance() {
if (instance == null) instance = new InviteManager(); if (instance == null) instance = new CallManager();
return instance; return instance;
} }
@ -107,4 +113,14 @@ public class InviteManager {
lc.updateCall(lCall, params); lc.updateCall(lCall, params);
} }
/**
* Update current call, without reinvite.
*/
public void updateCall() {
LinphoneCore lc = lc();
LinphoneCall lCall = lc.getCurrentCall();
LinphoneCallParams params = lCall.getCurrentParamsCopy();
bm().updateWithProfileSettings(lc, params);
}
} }

View file

@ -168,7 +168,7 @@ public class DialerActivity extends Activity implements LinphoneCoreListener {
mAddVideo.setOnClickListener(new OnClickListener() { mAddVideo.setOnClickListener(new OnClickListener() {
public void onClick(View v) { public void onClick(View v) {
// If no in video call; try to reinvite with video // If no in video call; try to reinvite with video
boolean alreadyInVideoCall = !InviteManager.getInstance().reinviteWithVideo(); boolean alreadyInVideoCall = !CallManager.getInstance().reinviteWithVideo();
if (alreadyInVideoCall) { if (alreadyInVideoCall) {
// In video call; going back to video call activity // In video call; going back to video call activity
startVideoView(VIDEO_VIEW_ACTIVITY); startVideoView(VIDEO_VIEW_ACTIVITY);
@ -364,6 +364,7 @@ public class DialerActivity extends Activity implements LinphoneCoreListener {
if (mWakeLock.isHeld()) mWakeLock.release(); if (mWakeLock.isHeld()) mWakeLock.release();
theDialer=null; theDialer=null;
} }
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
@ -522,6 +523,8 @@ public class DialerActivity extends Activity implements LinphoneCoreListener {
private void resetCameraFromPreferences() { private void resetCameraFromPreferences() {
boolean useFrontCam = mPref.getBoolean(getString(R.string.pref_video_use_front_camera_key), false); boolean useFrontCam = mPref.getBoolean(getString(R.string.pref_video_use_front_camera_key), false);
AndroidCameraRecordManager.getInstance().setUseFrontCamera(useFrontCam); AndroidCameraRecordManager.getInstance().setUseFrontCamera(useFrontCam);
final int phoneOrientation = 90 * getWindowManager().getDefaultDisplay().getOrientation();
AndroidCameraRecordManager.getInstance().setPhoneOrientation(phoneOrientation);
} }
private void exitCallMode() { private void exitCallMode() {
@ -605,7 +608,7 @@ public class DialerActivity extends Activity implements LinphoneCoreListener {
boolean prefVideoEnable = mPref.getBoolean(getString(R.string.pref_video_enable_key), false); boolean prefVideoEnable = mPref.getBoolean(getString(R.string.pref_video_enable_key), false);
boolean prefInitiateWithVideo = mPref.getBoolean(getString(R.string.pref_video_initiate_call_with_video_key), false); boolean prefInitiateWithVideo = mPref.getBoolean(getString(R.string.pref_video_initiate_call_with_video_key), false);
resetCameraFromPreferences(); resetCameraFromPreferences();
InviteManager.getInstance().inviteAddress(lAddress, prefVideoEnable && prefInitiateWithVideo); CallManager.getInstance().inviteAddress(lAddress, prefVideoEnable && prefInitiateWithVideo);
} catch (LinphoneCoreException e) { } catch (LinphoneCoreException e) {
Toast toast = Toast.makeText(DialerActivity.this Toast toast = Toast.makeText(DialerActivity.this

View file

@ -54,7 +54,6 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo; import android.net.NetworkInfo;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.PowerManager;
import android.os.Vibrator; import android.os.Vibrator;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.util.Log; import android.util.Log;

View file

@ -22,12 +22,12 @@ package org.linphone;
import org.linphone.core.AndroidCameraRecordManager; import org.linphone.core.AndroidCameraRecordManager;
import org.linphone.core.LinphoneCore; import org.linphone.core.LinphoneCore;
import org.linphone.core.Version;
import org.linphone.core.VideoSize; import org.linphone.core.VideoSize;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.PowerManager.WakeLock; import android.os.PowerManager.WakeLock;
@ -47,7 +47,8 @@ public class VideoCallActivity extends Activity {
private WakeLock mWakeLock; private WakeLock mWakeLock;
private static final int capturePreviewLargestDimension = 150; private static final int capturePreviewLargestDimension = 150;
// private static final float similarRatio = 0.1f; // private static final float similarRatio = 0.1f;
private static final int version = Integer.parseInt(Build.VERSION.SDK); private int previousPhoneOrientation;
private int phoneOrientation;
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
launched = true; launched = true;
@ -61,9 +62,10 @@ public class VideoCallActivity extends Activity {
mVideoCaptureView = (SurfaceView) findViewById(R.id.video_capture_surface); mVideoCaptureView = (SurfaceView) findViewById(R.id.video_capture_surface);
final int rotation = getWindowManager().getDefaultDisplay().getOrientation(); previousPhoneOrientation = AndroidCameraRecordManager.getInstance().getPhoneOrientation();
phoneOrientation = 90 * getWindowManager().getDefaultDisplay().getOrientation();
recordManager = AndroidCameraRecordManager.getInstance(); recordManager = AndroidCameraRecordManager.getInstance();
recordManager.setSurfaceView(mVideoCaptureView, rotation); recordManager.setSurfaceView(mVideoCaptureView, phoneOrientation);
mVideoCaptureView.setZOrderOnTop(true); mVideoCaptureView.setZOrderOnTop(true);
if (!recordManager.isMuted()) sendStaticImage(false); if (!recordManager.isMuted()) sendStaticImage(false);
@ -71,15 +73,32 @@ public class VideoCallActivity extends Activity {
mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK|PowerManager.ON_AFTER_RELEASE,"Linphone"); mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK|PowerManager.ON_AFTER_RELEASE,"Linphone");
mWakeLock.acquire(); mWakeLock.acquire();
if (version < 8) { if (Version.sdkBelow(8)) {
// 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(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); setRequestedOrientation(recordManager.isCameraOrientationPortrait() ?
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT :
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} }
// Handle the fact that the preferred size may have a ratio /an orientation different from the one // Base capture frame on streamed dimensions and orientation.
// in the videocall.xml as the front camera on Samsung captures in landscape. resizeCapturePreview(mVideoCaptureView, lc.getPreferredVideoSize());
resizeCapturePreviewForOldPhones(mVideoCaptureView, lc.getPreferredVideoSize()); }
private void updateCallIfOrientationChanged() {
if (Version.sdkAbove(8) && previousPhoneOrientation != phoneOrientation) {
CallManager.getInstance().updateCall();
// camera will be restarted when mediastreamer chain is recreated and setParameters is called
// Base capture frame on streamed dimensions and orientation.
resizeCapturePreview(mVideoCaptureView, LinphoneService.getLc().getPreferredVideoSize());
}
}
@Override
protected void onResume() {
updateCallIfOrientationChanged();
super.onResume();
} }
@ -109,6 +128,9 @@ public class VideoCallActivity extends Activity {
rewriteToggleCameraItem(menu.findItem(R.id.videocall_menu_toggle_camera)); rewriteToggleCameraItem(menu.findItem(R.id.videocall_menu_toggle_camera));
rewriteChangeResolutionItem(menu.findItem(R.id.videocall_menu_change_resolution)); rewriteChangeResolutionItem(menu.findItem(R.id.videocall_menu_change_resolution));
if (!recordManager.hasSeveralCameras()) {
menu.findItem(R.id.videocall_menu_switch_camera).setVisible(false);
}
return true; return true;
} }
@ -123,9 +145,7 @@ public class VideoCallActivity extends Activity {
* @param sv capture surface view to resize the layout * @param sv capture surface view to resize the layout
* @param vs video size from which to calculate the dimensions * @param vs video size from which to calculate the dimensions
*/ */
private void resizeCapturePreviewForOldPhones(SurfaceView sv, VideoSize vs) { private void resizeCapturePreview(SurfaceView sv, VideoSize vs) {
if (version >= 8) return;
LayoutParams lp = sv.getLayoutParams(); LayoutParams lp = sv.getLayoutParams();
float newRatio = ratioWidthHeight(vs); float newRatio = ratioWidthHeight(vs);
@ -158,7 +178,7 @@ public class VideoCallActivity extends Activity {
// Resize preview frame // Resize preview frame
VideoSize newVideoSize = LinphoneService.getLc().getPreferredVideoSize(); VideoSize newVideoSize = LinphoneService.getLc().getPreferredVideoSize();
resizeCapturePreviewForOldPhones(mVideoCaptureView, newVideoSize); resizeCapturePreview(mVideoCaptureView, newVideoSize);
break; break;
case R.id.videocall_menu_terminate_call: case R.id.videocall_menu_terminate_call:
LinphoneCore lc = LinphoneService.getLc(); LinphoneCore lc = LinphoneService.getLc();
@ -171,13 +191,17 @@ public class VideoCallActivity extends Activity {
sendStaticImage(recordManager.toggleMute()); sendStaticImage(recordManager.toggleMute());
rewriteToggleCameraItem(item); rewriteToggleCameraItem(item);
break; break;
/* case R.id.videocall_menu_switch_camera: case R.id.videocall_menu_switch_camera:
recordManager.stopVideoRecording(); recordManager.stopVideoRecording();
sendStaticImage(true);
recordManager.toggleUseFrontCamera(); recordManager.toggleUseFrontCamera();
InviteManager.getInstance().reinvite(); CallManager.getInstance().updateCall();
// camera will be restarted when mediastreamer chain is recreated and setParameters is called // camera will be restarted when mediastreamer chain is recreated and setParameters is called
// Base capture frame on streamed dimensions and orientation.
resizeCapturePreview(mVideoCaptureView, LinphoneService.getLc().getPreferredVideoSize());
break; break;
*/ default: default:
Log.e(LinphoneService.TAG, "Unknown menu item ["+item+"]"); Log.e(LinphoneService.TAG, "Unknown menu item ["+item+"]");
break; break;
} }

View file

@ -39,14 +39,12 @@ public abstract class AndroidCameraRecord {
private PreviewCallback storedPreviewCallback; private PreviewCallback storedPreviewCallback;
private boolean previewStarted; private boolean previewStarted;
protected int displayOrientation;
protected static final String tag="Linphone"; protected static final String tag="Linphone";
private List <Size> supportedVideoSizes; private List <Size> supportedVideoSizes;
private Size currentPreviewSize; private Size currentPreviewSize;
public AndroidCameraRecord(RecorderParams parameters) { public AndroidCameraRecord(RecorderParams parameters) {
this.params = parameters; this.params = parameters;
setDisplayOrientation(parameters.rotation);
} }
protected List<Size> getSupportedPreviewSizes(Camera.Parameters parameters) { protected List<Size> getSupportedPreviewSizes(Camera.Parameters parameters) {
@ -67,7 +65,7 @@ public abstract class AndroidCameraRecord {
} }
camera=Camera.open(); camera = openCamera(params.cameraId);
camera.setErrorCallback(new ErrorCallback() { camera.setErrorCallback(new ErrorCallback() {
public void onError(int error, Camera camera) { public void onError(int error, Camera camera) {
Log.e(tag, "Camera error : " + error); 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); parameters.setPreviewSize(params.width, params.height);
} else { } else {
// invert height and width
parameters.setPreviewSize(params.height, params.width); parameters.setPreviewSize(params.height, params.width);
} }
parameters.setPreviewFrameRate(Math.round(params.fps)); 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) {} protected void onSettingCameraParameters(Parameters parameters) {}
/** /**
@ -164,24 +167,8 @@ public abstract class AndroidCameraRecord {
protected abstract void lowLevelSetPreviewCallback(Camera camera, PreviewCallback cb); 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 static class RecorderParams {
public float fps; public float fps;
public int height; public int height;
@ -191,7 +178,6 @@ public abstract class AndroidCameraRecord {
public int cameraId; public int cameraId;
public int rotation; public int rotation;
public SurfaceView surfaceView; public SurfaceView surfaceView;
public boolean videoDimensionsInverted;
public RecorderParams(long ptr) { public RecorderParams(long ptr) {
filterDataNativePtr = ptr; filterDataNativePtr = ptr;
@ -200,7 +186,6 @@ public abstract class AndroidCameraRecord {
public boolean isStarted() { public boolean isStarted() {
return previewStarted; return previewStarted;
} }
@ -215,4 +200,5 @@ public abstract class AndroidCameraRecord {
return currentPreviewSize.width * currentPreviewSize.height * 3 /2; return currentPreviewSize.width * currentPreviewSize.height * 3 /2;
} }
} }

View file

@ -26,9 +26,9 @@ import android.hardware.Camera.Size;
import android.util.Log; import android.util.Log;
public class AndroidCameraRecordImplAPI5 extends AndroidCameraRecordImpl { public class AndroidCameraRecord5Impl extends AndroidCameraRecordImpl {
public AndroidCameraRecordImplAPI5(RecorderParams parameters) { public AndroidCameraRecord5Impl(RecorderParams parameters) {
super(parameters); super(parameters);
} }

View file

@ -30,10 +30,10 @@ import android.util.Log;
* @author Guillaume Beraudo * @author Guillaume Beraudo
* *
*/ */
public class AndroidCameraRecordAPI8Impl extends AndroidCameraRecordImplAPI5 { public class AndroidCameraRecord8Impl extends AndroidCameraRecord5Impl {
public AndroidCameraRecordAPI8Impl(RecorderParams parameters) { public AndroidCameraRecord8Impl(RecorderParams parameters) {
super(parameters); super(parameters);
} }
@ -66,10 +66,6 @@ public class AndroidCameraRecordAPI8Impl extends AndroidCameraRecordImplAPI5 {
protected void onSettingCameraParameters(Parameters parameters) { protected void onSettingCameraParameters(Parameters parameters) {
super.onSettingCameraParameters(parameters); super.onSettingCameraParameters(parameters);
// Only on v8 hardware // Only on v8 hardware
camera.setDisplayOrientation(90 * getPreviewCaptureRotation()); camera.setDisplayOrientation(rotation);
}
private int getPreviewCaptureRotation() {
return (4 + 1 - displayOrientation) % 4;
} }
} }

View file

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

View file

@ -34,19 +34,19 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev
private double timeElapsedBetweenFrames = 0; private double timeElapsedBetweenFrames = 0;
private long lastFrameTime = 0; private long lastFrameTime = 0;
private final double expectedTimeBetweenFrames; private final double expectedTimeBetweenFrames;
private boolean sizesInverted; protected final int rotation;
public AndroidCameraRecordImpl(RecorderParams parameters) { public AndroidCameraRecordImpl(RecorderParams parameters) {
super(parameters); super(parameters);
expectedTimeBetweenFrames = 1d / Math.round(parameters.fps); expectedTimeBetweenFrames = 1d / Math.round(parameters.fps);
filterCtxPtr = parameters.filterDataNativePtr; filterCtxPtr = parameters.filterDataNativePtr;
sizesInverted = parameters.videoDimensionsInverted; rotation = parameters.rotation;
storePreviewCallBack(this); 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) { public void onPreviewFrame(byte[] data, Camera camera) {
@ -69,7 +69,7 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev
long curTime = System.currentTimeMillis(); long curTime = System.currentTimeMillis();
if (lastFrameTime == 0) { if (lastFrameTime == 0) {
lastFrameTime = curTime; lastFrameTime = curTime;
putImage(filterCtxPtr, data, rotateCapturedFrame(), sizesInverted); putImage(filterCtxPtr, data, rotation);
return; return;
} }
@ -82,7 +82,7 @@ public class AndroidCameraRecordImpl extends AndroidCameraRecord implements Prev
timeElapsedBetweenFrames = currentTimeElapsed; timeElapsedBetweenFrames = currentTimeElapsed;
// Log.d("onPreviewFrame: ", Integer.toString(data.length)); // 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); camera.setPreviewCallback(cb);
} }
} }

View file

@ -22,6 +22,7 @@ import java.util.List;
import org.linphone.core.AndroidCameraRecord.RecorderParams; import org.linphone.core.AndroidCameraRecord.RecorderParams;
import android.hardware.Camera;
import android.hardware.Camera.Size; import android.hardware.Camera.Size;
import android.os.Build; import android.os.Build;
import android.util.Log; import android.util.Log;
@ -38,14 +39,9 @@ import android.view.SurfaceHolder.Callback;
* *
*/ */
public class AndroidCameraRecordManager { public class AndroidCameraRecordManager {
private static final int version = Integer.parseInt(Build.VERSION.SDK);
private static final String tag = "Linphone"; private static final String tag = "Linphone";
private static AndroidCameraRecordManager instance; private static AndroidCameraRecordManager instance;
// singleton
private AndroidCameraRecordManager() {}
/** /**
* @return instance * @return instance
*/ */
@ -59,31 +55,76 @@ public class AndroidCameraRecordManager {
private AndroidCameraRecord.RecorderParams parameters; private AndroidCameraRecord.RecorderParams parameters;
private SurfaceView surfaceView; private SurfaceView surfaceView;
private boolean muted; private boolean muted;
private int cameraId;
private AndroidCameraRecord recorder; private AndroidCameraRecord recorder;
private List<Size> supportedVideoSizes; private List<Size> 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) { public void setUseFrontCamera(boolean value) {
if (useFrontCamera == value) return; if (isFrontCamera() == value) return; // already OK
this.useFrontCamera = value;
toggleUseFrontCamera();
}
public boolean isUseFrontCamera() {return isFrontCamera();}
public boolean toggleUseFrontCamera() {
boolean previousUseFront = isFrontCamera();
cameraId = previousUseFront ? rearCameraId : frontCameraId;
if (parameters != null) { if (parameters != null) {
parameters.cameraId = cameraId(); parameters.cameraId = cameraId;
if (isRecording()) { if (isRecording()) {
stopVideoRecording(); stopVideoRecording();
tryToStartVideoRecording(); tryToStartVideoRecording();
} }
} }
}
public boolean isUseFrontCamera() {return useFrontCamera;} return !previousUseFront;
public boolean toggleUseFrontCamera() {
setUseFrontCamera(!useFrontCamera);
return useFrontCamera;
} }
@ -94,16 +135,14 @@ public class AndroidCameraRecordManager {
p.fps = fps; p.fps = fps;
p.width = width; p.width = width;
p.height = height; p.height = height;
p.cameraId = cameraId(); p.cameraId = cameraId;
p.videoDimensionsInverted = width < height;
// width and height will be inverted in Recorder on startPreview
parameters = p; parameters = p;
tryToStartVideoRecording(); tryToStartVideoRecording();
} }
public final void setSurfaceView(final SurfaceView sv, final int rotation) { public final void setSurfaceView(final SurfaceView sv, final int phoneOrientation) {
this.rotation = useFrontCamera ? 1 : rotation; this.phoneOrientation = phoneOrientation;
SurfaceHolder holder = sv.getHolder(); SurfaceHolder holder = sv.getHolder();
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
@ -152,12 +191,15 @@ public class AndroidCameraRecordManager {
private void tryToStartVideoRecording() { private void tryToStartVideoRecording() {
if (muted || surfaceView == null || parameters == null) return; if (muted || surfaceView == null || parameters == null) return;
parameters.rotation = rotation; parameters.rotation = bufferRotationForCorrectImageOrientation();
parameters.surfaceView = surfaceView; parameters.surfaceView = surfaceView;
if (version >= 8) { if (Version.sdkAbove(9)) {
recorder = new AndroidCameraRecordAPI8Impl(parameters); recorder = new AndroidCameraRecord9Impl(parameters);
} else if (version >= 5) { } else if (Version.sdkAbove(8)) {
recorder = new AndroidCameraRecordImplAPI5(parameters); recorder = new AndroidCameraRecord8Impl(parameters);
} else if (Version.sdkAbove(5)) {
recorder = new AndroidCameraRecord5Impl(parameters);
} else { } else {
recorder = new AndroidCameraRecordImpl(parameters); recorder = new AndroidCameraRecordImpl(parameters);
} }
@ -188,8 +230,8 @@ public class AndroidCameraRecordManager {
if (supportedVideoSizes != null) return supportedVideoSizes; if (supportedVideoSizes != null) return supportedVideoSizes;
} }
if (version >= 5) { if (Version.sdkAbove(5)) {
supportedVideoSizes = AndroidCameraRecordImplAPI5.oneShotSupportedVideoSizes(); supportedVideoSizes = AndroidCameraRecord5Impl.oneShotSupportedVideoSizes();
} }
// eventually null // eventually null
@ -212,34 +254,86 @@ public class AndroidCameraRecordManager {
parameters = null; parameters = null;
} }
/** public boolean outputIsPortrait() {
* Naive simple version. final int rotation = bufferRotationForCorrectImageOrientation();
* @param askedSize final boolean isPortrait = (rotation % 180) == 90;
* @return
*/ Log.d(tag, "Camera sensor in portrait orientation ?" + isPortrait);
public VideoSize doYouSupportThisVideoSize(VideoSize askedSize) { return isPortrait;
Log.d(tag, "Asking camera if it supports size "+askedSize);
if (useFrontCamera && askedSize.isPortrait()) {
return askedSize.createInverted(); // only landscape supported
} else {
return askedSize;
}
} }
private VideoSize closestVideoSize(VideoSize vSize, int defaultSizeCode, boolean defaultIsPortrait) {
VideoSize testSize = vSize.isPortrait() ? vSize.createInverted() : vSize;
public static int getNumberOfCameras() {
for (Size s : AndroidCameraRecordManager.getInstance().supportedVideoSizes()) { if (Version.sdkAbove(9)) return getNumberOfCameras9();
if (s.height == testSize.height && s.width == testSize.width) {
return vSize; // Use hacks to guess the number of cameras
} if (Build.DEVICE.startsWith("GT-I9000")) {
} // Galaxy S has 2 cameras
return 2;
return VideoSize.createStandard(defaultSizeCode, defaultIsPortrait); } else
return 1;
} }
private static final int rearCamId() {return 1;} private static int getNumberOfCameras9() {
private static final int frontCamId() {return 2;} return Camera.getNumberOfCameras();
private final int cameraId() {return useFrontCamera? frontCamId() : rearCamId(); } }
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;
}
} }

View file

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