New Mediastreamer Android Camera filter integration

android: new mediastreamer integration

android: new ms2 integration

Video calls minor corrections
This commit is contained in:
Pierre-Eric Pelloux-Prayer 2011-09-14 17:41:17 +02:00
parent 1b0cb40009
commit 77898e052d
31 changed files with 207 additions and 2034 deletions

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry excluding="org/linphone/mediastream/MediastreamerActivity.java" kind="src" path="submodules/linphone/mediastreamer2/java/src"/>
<classpathentry kind="src" path="submodules/linphone/java/j2se"/>
<classpathentry kind="src" path="submodules/linphone/java/common"/>
<classpathentry kind="src" path="gen"/>

View file

@ -4,7 +4,7 @@
android:id="@+id/video_frame" android:orientation="vertical"
android:layout_height="fill_parent" android:layout_width="fill_parent">
<org.linphone.GL2JNIView android:layout_height="fill_parent" android:layout_width="fill_parent" android:id="@+id/video_surface"></org.linphone.GL2JNIView>
<org.linphone.mediastream.video.display.GL2JNIView android:layout_height="fill_parent" android:layout_width="fill_parent" android:id="@+id/video_surface"></org.linphone.mediastream.video.display.GL2JNIView>
<SurfaceView android:layout_height="72dip" android:layout_width="88dip" android:id="@+id/video_capture_surface" android:layout_gravity="right|bottom"
android:layout_margin="15dip"></SurfaceView>
</FrameLayout>

View file

@ -4,7 +4,7 @@
android:id="@+id/video_frame"
android:layout_height="fill_parent" android:layout_width="fill_parent">
<org.linphone.GL2JNIView android:layout_height="fill_parent" android:layout_width="fill_parent" android:id="@+id/video_surface"></org.linphone.GL2JNIView>
<org.linphone.mediastream.video.display.GL2JNIView android:layout_height="fill_parent" android:layout_width="fill_parent" android:id="@+id/video_surface"></org.linphone.mediastream.video.display.GL2JNIView>
<SurfaceView android:layout_height="88dip" android:layout_width="72dip" android:id="@+id/video_capture_surface" android:layout_gravity="right|bottom"
android:layout_margin="15dip"></SurfaceView>
</FrameLayout>

View file

@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:orientation="vertical">
<!--
<LinearLayout android:layout_width="fill_parent"
android:layout_height="wrap_content">
-->
<!--
<Button android:text="TEST" android:id="@+id/videotest_test"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"></Button>
-->
<LinearLayout android:id="@+id/LinearLayout01"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal">
<Button android:text="Start" android:id="@+id/test_video_size"
android:layout_width="fill_parent" android:layout_height="wrap_content" />
<Button android:text="Change camera" android:id="@+id/test_video_camera"
android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
<Button android:text="Change to portrait" android:id="@+id/test_video_orientation"
android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
<TextView android:id="@+id/videotest_debug"
android:layout_weight="1" android:layout_width="fill_parent"
android:layout_height="wrap_content"></TextView>
</LinearLayout>
<SurfaceView android:id="@+id/videotest_surfaceView"
android:layout_width="wrap_content" android:layout_height="wrap_content"></SurfaceView>
</LinearLayout>

View file

@ -44,5 +44,5 @@ ng_floorgain=0.01
[video]
size=qvga-portrait
size=vga

View file

@ -18,14 +18,8 @@ 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.Version;
import org.linphone.core.VideoSize;
import org.linphone.core.video.AndroidCameraRecordManager;
public class BandwidthManager {
@ -82,24 +76,9 @@ public class BandwidthManager {
public void updateWithProfileSettings(LinphoneCore lc, LinphoneCallParams callParams) {
// Setting Linphone Core Preferred Video Size
boolean bandwidthOKForVideo = isVideoPossible();
if (bandwidthOKForVideo && Version.isVideoCapable()) {
AndroidCameraRecordManager acrm = AndroidCameraRecordManager.getInstance();
boolean isPortrait=acrm.isFrameToBeShownPortrait();
VideoSize targetVideoSize=maxSupportedVideoSize(isPortrait, getMaximumVideoSize(isPortrait),
acrm.supportedVideoSizes());
lc.setPreferredVideoSize(targetVideoSize);
VideoSize actualVideoSize = lc.getPreferredVideoSize();
if (!targetVideoSize.equals(actualVideoSize)) {
lc.setPreferredVideoSize(VideoSize.createStandard(VideoSize.QCIF, targetVideoSize.isPortrait()));
}
}
if (callParams != null) { // in call
// Update video parm if
if (!bandwidthOKForVideo) { // NO VIDEO
if (!isVideoPossible()) { // NO VIDEO
callParams.setVideoEnabled(false);
callParams.setAudioBandwidth(40);
} else {
@ -109,44 +88,7 @@ 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:
return VideoSize.createStandard(VideoSize.QCIF, cameraIsPortrait);
case HIGH_RESOLUTION:
return VideoSize.createStandard(VideoSize.QVGA, cameraIsPortrait);
default:
throw new RuntimeException("profile not managed : " + profile);
}
}
public boolean isVideoPossible() {
return currentProfile != LOW_BANDWIDTH;
}
private VideoSize getMaximumVideoSize(boolean isPortrait) {
return maximumVideoSize(currentProfile, isPortrait);
}
}

View file

@ -24,7 +24,6 @@ import org.linphone.core.LinphoneCallParams;
import org.linphone.core.LinphoneCore;
import org.linphone.core.LinphoneCoreException;
import org.linphone.core.Log;
import org.linphone.core.video.AndroidCameraRecordManager;
/**
@ -33,7 +32,7 @@ import org.linphone.core.video.AndroidCameraRecordManager;
* @author Guillaume Beraudo
*
*/
class CallManager {
public class CallManager {
private static CallManager instance;
@ -43,9 +42,6 @@ class CallManager {
return instance;
}
private AndroidCameraRecordManager videoManager() {
return AndroidCameraRecordManager.getInstance();
}
private BandwidthManager bm() {
return BandwidthManager.getInstance();
}
@ -60,7 +56,6 @@ class CallManager {
bm().updateWithProfileSettings(lc, params);
if (videoEnabled && params.getVideoEnabled()) {
videoManager().setMuted(false);
params.setVideoEnabled(true);
} else {
params.setVideoEnabled(false);
@ -98,7 +93,6 @@ class CallManager {
// Not yet in video call: try to re-invite with video
params.setVideoEnabled(true);
videoManager().setMuted(false);
lc.updateCall(lCall, params);
return true;
}
@ -125,7 +119,7 @@ class CallManager {
* Update current call, without reinvite.
* The camera will be restarted when mediastreamer chain is recreated and setParameters is called.
*/
void updateCall() {
public void updateCall() {
LinphoneCore lc = LinphoneManager.getLc();
LinphoneCall lCall = lc.getCurrentCall();
if (lCall == null) {

View file

@ -22,11 +22,10 @@ import org.linphone.LinphoneManager.NewOutgoingCallUiListener;
import org.linphone.LinphoneService.LinphoneGuiListener;
import org.linphone.core.CallDirection;
import org.linphone.core.LinphoneCall;
import org.linphone.core.LinphoneCall.State;
import org.linphone.core.LinphoneCore;
import org.linphone.core.Log;
import org.linphone.core.Version;
import org.linphone.core.video.AndroidCameraRecordManager;
import org.linphone.core.LinphoneCall.State;
import org.linphone.ui.AddVideoButton;
import org.linphone.ui.AddressAware;
import org.linphone.ui.AddressText;
@ -327,7 +326,6 @@ public class DialerActivity extends SoftVolumeActivity implements LinphoneGuiLis
int key = R.string.pref_video_automatically_share_my_video_key;
boolean prefAutoShareMyCamera = mPref.getBoolean(getString(key), false);
boolean videoMuted = !(prefVideoEnable && prefAutoShareMyCamera);
AndroidCameraRecordManager.getInstance().setMuted(videoMuted);
LinphoneManager.getLc().getCurrentCall().enableCamera(prefAutoShareMyCamera);
}
@ -394,20 +392,35 @@ public class DialerActivity extends SoftVolumeActivity implements LinphoneGuiLis
/* we are certainly exiting, ignore then.*/
return;
}
if (state == LinphoneCall.State.OutgoingInit) {
enterIncallMode(lc);
} else if (state == LinphoneCall.State.IncomingReceived) {
callPending(call);
} else if (state == LinphoneCall.State.Connected) {
if (call.getDirection() == CallDirection.Incoming) {
switch (state) {
case OutgoingInit:
enterIncallMode(lc);
}
} else if (state == LinphoneCall.State.Error) {
if (mWakeLock.isHeld()) mWakeLock.release();
showToast(R.string.call_error, message);
exitCallMode();
} else if (state == LinphoneCall.State.CallEnd) {
exitCallMode();
if (!LinphoneManager.getInstance().shareMyCamera())
call.enableCamera(false);
LinphoneActivity.instance().startOrientationSensor();
break;
case IncomingReceived:
callPending(call);
if (!LinphoneManager.getInstance().shareMyCamera())
call.enableCamera(false);
LinphoneActivity.instance().startOrientationSensor();
break;
case Connected:
if (call.getDirection() == CallDirection.Incoming) {
enterIncallMode(lc);
}
break;
case Error:
if (mWakeLock.isHeld()) mWakeLock.release();
showToast(R.string.call_error, message);
exitCallMode();
LinphoneActivity.instance().stopOrientationSensor();
break;
case CallEnd:
exitCallMode();
LinphoneActivity.instance().stopOrientationSensor();
break;
}
}

View file

@ -39,6 +39,7 @@ import org.linphone.core.Log;
import org.linphone.core.Version;
import org.linphone.core.LinphoneCore.EcCalibratorStatus;
import org.linphone.core.LinphoneCore.RegistrationState;
import org.linphone.mediastream.video.AndroidVideoWindowImpl;
import android.app.AlertDialog;
import android.app.TabActivity;
@ -68,7 +69,7 @@ import android.widget.TextView;
import android.widget.Toast;
import android.widget.TabHost.TabSpec;
public class LinphoneActivity extends TabActivity {
public class LinphoneActivity extends TabActivity implements SensorEventListener {
public static final String DIALER_TAB = "dialer";
public static final String PREF_FIRST_LAUNCH = "pref_first_launch";
private static final int video_activity = 100;
@ -82,6 +83,8 @@ public class LinphoneActivity extends TabActivity {
private FrameLayout mMainFrame;
private SensorManager mSensorManager;
private Sensor mAccelerometer;
private int previousRotation = -1;
private static SensorEventListener mSensorEventListener;
private static final String SCREEN_IS_HIDDEN = "screen_is_hidden";
@ -283,9 +286,30 @@ public class LinphoneActivity extends TabActivity {
Toast.makeText(this, getString(R.string.dialer_null_on_new_intent), Toast.LENGTH_LONG).show();
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
@Override
public void onSensorChanged(SensorEvent event) {
if (event==null || event.sensor == mAccelerometer) {
// WARNING : getRotation() is SDK > 5
int rot = AndroidVideoWindowImpl.rotationToAngle(getWindowManager().getDefaultDisplay().getRotation());
if (rot != previousRotation) {
Log.d("New device rotation: ", rot);
// Returning rotation FROM ITS NATURAL ORIENTATION
LinphoneManager.getLc().setDeviceRotation(rot);
previousRotation = rot;
}
}
}
@Override
protected void onPause() {
super.onPause();
if (isFinishing()) {
//restore audio settings
LinphoneManager.getInstance().routeAudioToReceiver();
@ -368,6 +392,22 @@ public class LinphoneActivity extends TabActivity {
}
}
public synchronized void startOrientationSensor() {
if (mSensorManager!=null) {
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
}
/* init LC orientation value on orientation sensor startup */
previousRotation = -1;
onSensorChanged(null);
}
public synchronized void stopOrientationSensor() {
if (mSensorManager!=null)
mSensorManager.unregisterListener(this, mAccelerometer);
}
protected synchronized void stopProxymitySensor() {
if (mSensorManager!=null) {
mSensorManager.unregisterListener(mSensorEventListener);

View file

@ -30,7 +30,6 @@ import static org.linphone.R.string.pref_codec_amr_key;
import static org.linphone.R.string.pref_codec_ilbc_key;
import static org.linphone.R.string.pref_codec_speex16_key;
import static org.linphone.R.string.pref_codec_speex32_key;
import static org.linphone.R.string.pref_echo_cancellation_key;
import static org.linphone.R.string.pref_video_enable_key;
import static org.linphone.core.LinphoneCall.State.CallEnd;
import static org.linphone.core.LinphoneCall.State.Error;
@ -43,7 +42,6 @@ import java.io.InputStream;
import java.util.Timer;
import java.util.TimerTask;
import org.linphone.core.Hacks;
import org.linphone.core.LinphoneAddress;
import org.linphone.core.LinphoneAuthInfo;
import org.linphone.core.LinphoneCall;
@ -63,7 +61,10 @@ import org.linphone.core.LinphoneCore.FirewallPolicy;
import org.linphone.core.LinphoneCore.GlobalState;
import org.linphone.core.LinphoneCore.RegistrationState;
import org.linphone.core.LinphoneCore.Transports;
import org.linphone.core.video.AndroidCameraRecordManager;
import org.linphone.mediastream.video.capture.AndroidVideoApi5JniWrapper;
import org.linphone.mediastream.video.capture.hwconf.AndroidCameraConfiguration;
import org.linphone.mediastream.video.capture.hwconf.Hacks;
import org.linphone.mediastream.video.capture.hwconf.AndroidCameraConfiguration.AndroidCamera;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@ -211,9 +212,8 @@ public final class LinphoneManager implements LinphoneCoreListener {
instance = new LinphoneManager(c);
instance.serviceListener = listener;
instance.startLibLinphone(c);
if (Version.isVideoCapable()) {
AndroidCameraRecordManager.getInstance().startOrientationSensor(c.getApplicationContext());
}
AndroidVideoApi5JniWrapper.setAndroidSdkVersion(Build.VERSION.SDK_INT);
return instance;
}
@ -261,7 +261,6 @@ public final class LinphoneManager implements LinphoneCoreListener {
boolean prefVideoEnable = isVideoEnabled();
int key = R.string.pref_video_initiate_call_with_video_key;
boolean prefInitiateWithVideo = mPref.getBoolean(mR.getString(key), false);
resetCameraFromPreferences();
CallManager.getInstance().inviteAddress(lAddress, prefVideoEnable && prefInitiateWithVideo);
} else {
CallManager.getInstance().inviteAddress(lAddress, false);
@ -274,12 +273,19 @@ public final class LinphoneManager implements LinphoneCoreListener {
}
}
public void resetCameraFromPreferences() {
boolean useFrontCam = mPref.getBoolean(mR.getString(R.string.pref_video_use_front_camera_key), false);
AndroidCameraRecordManager.getInstance().setUseFrontCamera(useFrontCam);
int camId = 0;
AndroidCamera[] cameras = AndroidCameraConfiguration.retrieveCameras();
for (AndroidCamera androidCamera : cameras) {
if (androidCamera.frontFacing == useFrontCam)
camId = androidCamera.id;
}
LinphoneManager.getLc().setVideoDevice(camId);
}
public static interface AddressType {
void setText(CharSequence s);
CharSequence getText();
@ -294,6 +300,11 @@ public final class LinphoneManager implements LinphoneCoreListener {
public void onAlreadyInCall();
}
public void toggleEnableCamera() {
if (mLc.isIncall()) {
mLc.getCurrentCall().enableCamera(!mLc.getCurrentCall().cameraEnabled());
}
}
public void sendStaticImage(boolean send) {
if (mLc.isIncall()) {
@ -324,20 +335,6 @@ public final class LinphoneManager implements LinphoneCoreListener {
}
}
/**
* Camera will be restarted when mediastreamer chain is recreated and setParameters is called.
*/
public void switchCamera() {
AndroidCameraRecordManager.getInstance().stopVideoRecording();
AndroidCameraRecordManager.getInstance().toggleUseFrontCamera();
CallManager.getInstance().updateCall();
}
public void toggleCameraMuting() {
AndroidCameraRecordManager rm = AndroidCameraRecordManager.getInstance();
sendStaticImage(rm.toggleMute());
}
private synchronized void startLibLinphone(final Context context) {
try {
copyAssetsFromPackage(context);
@ -417,7 +414,7 @@ public final class LinphoneManager implements LinphoneCoreListener {
public void initFromConf(Context context) throws LinphoneConfigException {
//traces
boolean lIsDebug = mPref.getBoolean(getString(R.string.pref_debug_key), false);
boolean lIsDebug = true;//mPref.getBoolean(getString(R.string.pref_debug_key), false);
LinphoneCoreFactory.instance().setDebugMode(lIsDebug);
if (initialTransports == null)
@ -844,6 +841,10 @@ public final class LinphoneManager implements LinphoneCoreListener {
return mPref.getBoolean(getString(R.string.pref_video_enable_key), false);
}
public boolean shareMyCamera() {
return mPref.getBoolean(getString(R.string.pref_video_automatically_share_my_video), false);
}
public void setAudioModeIncallForGalaxyS() {
stopRinging();
mAudioManager.setMode(MODE_IN_CALL);

View file

@ -32,12 +32,12 @@ import java.util.Arrays;
import java.util.List;
import org.linphone.LinphoneManager.EcCalibrationListener;
import org.linphone.core.Hacks;
import org.linphone.core.LinphoneCoreException;
import org.linphone.core.Log;
import org.linphone.core.Version;
import org.linphone.core.LinphoneCore.EcCalibratorStatus;
import org.linphone.core.video.AndroidCameraRecordManager;
import org.linphone.mediastream.video.capture.hwconf.AndroidCameraConfiguration;
import org.linphone.mediastream.video.capture.hwconf.Hacks;
import android.content.SharedPreferences;
import android.os.Bundle;
@ -98,7 +98,7 @@ public class LinphonePreferencesActivity extends PreferenceActivity implements E
// No video
if (!Version.isVideoCapable()) {
uncheckAndDisableCheckbox(pref_video_enable_key);
} else if (!AndroidCameraRecordManager.getInstance().hasFrontCamera()) {
} else if (!AndroidCameraConfiguration.hasFrontCamera()) {
uncheckDisableAndHideCheckbox(R.string.pref_video_use_front_camera_key);
}
if (prefs().getBoolean(LinphoneActivity.PREF_FIRST_LAUNCH,true)) {

View file

@ -22,13 +22,13 @@ import java.io.IOException;
import org.linphone.LinphoneManager.LinphoneServiceListener;
import org.linphone.LinphoneManager.NewOutgoingCallUiListener;
import org.linphone.core.Hacks;
import org.linphone.core.LinphoneCall;
import org.linphone.core.Log;
import org.linphone.core.Version;
import org.linphone.core.LinphoneCall.State;
import org.linphone.core.LinphoneCore.GlobalState;
import org.linphone.core.LinphoneCore.RegistrationState;
import org.linphone.mediastream.video.capture.hwconf.Hacks;
import android.app.Notification;
import android.app.NotificationManager;

View file

@ -18,7 +18,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.linphone;
import org.linphone.core.Hacks;
import org.linphone.mediastream.video.capture.hwconf.Hacks;
import android.app.Activity;
import android.view.KeyEvent;

View file

@ -20,39 +20,32 @@ package org.linphone;
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.AndroidCameraRecordManager;
import org.linphone.core.video.AndroidCameraRecordManager.OnCapturingStateChangedListener;
import org.linphone.mediastream.video.AndroidVideoWindowImpl;
import org.linphone.mediastream.video.capture.hwconf.AndroidCameraConfiguration;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup.LayoutParams;
/**
* For Android SDK >= 5
* @author Guillaume Beraudo
*
*/
public class VideoCallActivity extends SoftVolumeActivity implements OnCapturingStateChangedListener {
public class VideoCallActivity extends SoftVolumeActivity {
private SurfaceView mVideoView;
private SurfaceView mVideoCaptureView;
private AndroidCameraRecordManager recordManager;
public static boolean launched = false;
private WakeLock mWakeLock;
private static final int capturePreviewLargestDimension = 150;
private Handler handler = new Handler();
AndroidVideoWindowImpl androidVideoWindowImpl;
public void onCreate(Bundle savedInstanceState) {
launched = true;
@ -61,48 +54,63 @@ public class VideoCallActivity extends SoftVolumeActivity implements OnCapturing
setContentView(R.layout.videocall);
mVideoView = (SurfaceView) findViewById(R.id.video_surface);
LinphoneCore lc = LinphoneManager.getLc();
lc.setVideoWindow(mVideoView);
mVideoCaptureView = (SurfaceView) findViewById(R.id.video_capture_surface);
mVideoCaptureView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
recordManager = AndroidCameraRecordManager.getInstance();
recordManager.setOnCapturingStateChanged(this);
recordManager.setSurfaceView(mVideoCaptureView);
/* force surfaces Z ordering */
mVideoView.setZOrderOnTop(false);
mVideoCaptureView.setZOrderOnTop(true);
if (!recordManager.isMuted()) LinphoneManager.getInstance().sendStaticImage(false);
androidVideoWindowImpl = new AndroidVideoWindowImpl(mVideoView, mVideoCaptureView);
androidVideoWindowImpl.setListener(new AndroidVideoWindowImpl.VideoWindowListener() {
@Override
public void onVideoRenderingSurfaceReady(AndroidVideoWindowImpl vw) {
LinphoneManager.getLc().setVideoWindow(vw);
}
@Override
public void onVideoRenderingSurfaceDestroyed(AndroidVideoWindowImpl vw) {
LinphoneManager.getLc().setVideoWindow(null);
}
@Override
public void onVideoPreviewSurfaceReady(AndroidVideoWindowImpl vw) {
LinphoneManager.getLc().setPreviewWindow(mVideoCaptureView);
}
@Override
public void onVideoPreviewSurfaceDestroyed(AndroidVideoWindowImpl vw) {
}
});
androidVideoWindowImpl.init();
// When changing phone orientation _DURING_ a call, VideoCallActivity is destroyed then recreated
// In this case, the following sequence happen:
// * onDestroy -> sendStaticImage(true) => destroy video graph
// * onCreate -> sendStaticImage(false) => recreate the video graph.
// Before creating the graph, the orientation must be known to LC => this is done here
LinphoneManager.getLc().setDeviceRotation(AndroidVideoWindowImpl.rotationToAngle(getWindowManager().getDefaultDisplay().getRotation()));
if (LinphoneManager.getInstance().shareMyCamera())
LinphoneManager.getInstance().sendStaticImage(false);
PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK|PowerManager.ON_AFTER_RELEASE,Log.TAG);
mWakeLock.acquire();
fixScreenOrientationForOldDevices();
}
private void fixScreenOrientationForOldDevices() {
if (Version.sdkAboveOrEqual(Version.API08_FROYO_22)) return;
// Force to display in portrait orientation for old devices
// as they do not support surfaceView rotation
setRequestedOrientation(recordManager.isCameraMountedPortrait() ?
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT :
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
resizeCapturePreview(mVideoCaptureView);
}
@Override
protected void onResume() {
if (Version.sdkAboveOrEqual(8) && recordManager.isOutputOrientationMismatch(LinphoneManager.getLc())) {
Log.i("Phone orientation has changed: updating call.");
CallManager.getInstance().updateCall();
// resizeCapturePreview by callback when recording started
}
super.onResume();
}
private void rewriteToggleCameraItem(MenuItem item) {
if (recordManager.isRecording()) {
if (LinphoneManager.getLc().getCurrentCall().cameraEnabled()) {
item.setTitle(getString(R.string.menu_videocall_toggle_camera_disable));
} else {
item.setTitle(getString(R.string.menu_videocall_toggle_camera_enable));
@ -128,38 +136,12 @@ public class VideoCallActivity extends SoftVolumeActivity implements OnCapturing
rewriteToggleCameraItem(menu.findItem(R.id.videocall_menu_toggle_camera));
rewriteChangeResolutionItem(menu.findItem(R.id.videocall_menu_change_resolution));
if (!recordManager.hasSeveralCameras()) {
if (!AndroidCameraConfiguration.hasSeveralCameras()) {
menu.findItem(R.id.videocall_menu_switch_camera).setVisible(false);
}
return true;
}
/**
* Base capture frame on streamed dimensions and orientation.
* @param sv capture surface view to resize the layout
* @param vs video size from which to calculate the dimensions
*/
private void resizeCapturePreview(SurfaceView sv) {
LayoutParams lp = sv.getLayoutParams();
VideoSize vs = LinphoneManager.getLc().getPreferredVideoSize();
float newRatio = (float) vs.width / vs.height;
if (vs.isPortrait()) {
lp.height = capturePreviewLargestDimension;
lp.width = Math.round(lp.height * newRatio);
} else {
lp.width = capturePreviewLargestDimension;
lp.height = Math.round(lp.width / newRatio);
}
sv.setLayoutParams(lp);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
@ -168,30 +150,37 @@ public class VideoCallActivity extends SoftVolumeActivity implements OnCapturing
break;
case R.id.videocall_menu_change_resolution:
LinphoneManager.getInstance().changeResolution();
// previous call will cause graph reconstruction -> regive preview window
rewriteChangeResolutionItem(item);
break;
case R.id.videocall_menu_terminate_call:
LinphoneManager.getInstance().terminateCall();
break;
case R.id.videocall_menu_toggle_camera:
LinphoneManager.getInstance().toggleCameraMuting();
LinphoneManager.getInstance().toggleEnableCamera();
rewriteToggleCameraItem(item);
// previous call will cause graph reconstruction -> regive preview window
LinphoneManager.getLc().setPreviewWindow(mVideoCaptureView);
break;
case R.id.videocall_menu_switch_camera:
LinphoneManager.getInstance().switchCamera();
fixScreenOrientationForOldDevices();
int id = LinphoneManager.getLc().getVideoDevice();
id = (id + 1) % AndroidCameraConfiguration.retrieveCameras().length;
LinphoneManager.getLc().setVideoDevice(id);
CallManager.getInstance().updateCall();
// previous call will cause graph reconstruction -> regive preview window
LinphoneManager.getLc().setPreviewWindow(mVideoCaptureView);
break;
default:
Log.e("Unknown menu item [",item,"]");
break;
}
return true;
}
@Override
protected void onDestroy() {
androidVideoWindowImpl.release();
launched = false;
super.onDestroy();
}
@ -203,16 +192,4 @@ public class VideoCallActivity extends SoftVolumeActivity implements OnCapturing
if (mWakeLock.isHeld()) mWakeLock.release();
super.onPause();
}
public void captureStarted() {
handler.post(new Runnable() {
public void run() {
resizeCapturePreview(mVideoCaptureView);
}
});
}
public void captureStopped() {
}
}

View file

@ -1,162 +0,0 @@
package org.linphone.core;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import org.linphone.OpenGLESDisplay;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Bitmap.Config;
import android.opengl.GLSurfaceView;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceHolder.Callback;
public class AndroidVideoWindowImpl {
private boolean useGLrendering;
private Bitmap mBitmap;
private SurfaceView mView;
private Surface mSurface;
private VideoWindowListener mListener;
private Renderer renderer;
public static interface VideoWindowListener{
void onSurfaceReady(AndroidVideoWindowImpl vw);
void onSurfaceDestroyed(AndroidVideoWindowImpl vw);
};
public AndroidVideoWindowImpl(SurfaceView view){
useGLrendering = (view instanceof GLSurfaceView);
mView=view;
mBitmap=null;
mSurface=null;
mListener=null;
view.getHolder().addCallback(new Callback(){
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
Log.i("Surface is being changed.");
if (!useGLrendering) {
synchronized(AndroidVideoWindowImpl.this){
mBitmap=Bitmap.createBitmap(width,height,Config.RGB_565);
mSurface=holder.getSurface();
}
}
if (mListener!=null) mListener.onSurfaceReady(AndroidVideoWindowImpl.this);
Log.w("Video display surface changed");
}
public void surfaceCreated(SurfaceHolder holder) {
Log.w("Video display surface created");
}
public void surfaceDestroyed(SurfaceHolder holder) {
if (!useGLrendering) {
synchronized(AndroidVideoWindowImpl.this){
mSurface=null;
mBitmap=null;
}
}
if (mListener!=null)
mListener.onSurfaceDestroyed(AndroidVideoWindowImpl.this);
Log.d("Video display surface destroyed");
}
});
if (useGLrendering) {
renderer = new Renderer();
((GLSurfaceView)mView).setRenderer(renderer);
((GLSurfaceView)mView).setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
}
static final int LANDSCAPE=0;
static final int PORTRAIT=1;
public void requestOrientation(int orientation){
//Surface.setOrientation(0, orientation==LANDSCAPE ? 1 : 0);
//Log.d("Orientation changed.");
}
public void setListener(VideoWindowListener l){
mListener=l;
}
public Surface getSurface(){
if (useGLrendering)
Log.e("View class does not match Video display filter used (you must use a non-GL View)");
return mView.getHolder().getSurface();
}
public Bitmap getBitmap(){
if (useGLrendering)
Log.e("View class does not match Video display filter used (you must use a non-GL View)");
return mBitmap;
}
public void setOpenGLESDisplay(int ptr) {
if (!useGLrendering)
Log.e("View class does not match Video display filter used (you must use a GL View)");
renderer.setOpenGLESDisplay(ptr);
}
public void requestRender() {
((GLSurfaceView)mView).requestRender();
}
//Called by the mediastreamer2 android display filter
public synchronized void update(){
if (mSurface!=null){
try {
Canvas canvas=mSurface.lockCanvas(null);
canvas.drawBitmap(mBitmap, 0, 0, null);
mSurface.unlockCanvasAndPost(canvas);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (OutOfResourcesException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private static class Renderer implements GLSurfaceView.Renderer {
int ptr;
boolean initPending;
int width=-1, height=-1;
public Renderer() {
ptr = 0;
initPending = false;
}
public void setOpenGLESDisplay(int ptr) {
this.ptr = ptr;
// if dimension are set, we are recreating MS2 graph without
// recreating the surface => need to force init
if (width > 0 && height > 0) {
initPending = true;
}
}
public void onDrawFrame(GL10 gl) {
if (ptr == 0)
return;
if (initPending) {
OpenGLESDisplay.init(ptr, width, height);
initPending = false;
}
OpenGLESDisplay.render(ptr);
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
/* delay init until ptr is set */
this.width = width;
this.height = height;
initPending = true;
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
}
}
}

View file

@ -1,146 +0,0 @@
/*
Hacks.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;
import android.media.AudioManager;
import android.os.Build;
public final class Hacks {
private Hacks() {}
public static boolean isGalaxySOrTabWithFrontCamera() {
return isGalaxySOrTab() && !isGalaxySOrTabWithoutFrontCamera();
}
private static boolean isGalaxySOrTabWithoutFrontCamera() {
return isSC02B() || isSGHI896();
}
public static boolean isGalaxySOrTab() {
return isGalaxyS() || isGalaxyTab();
}
public static boolean isGalaxyTab() {
return isGTP1000();
}
private static boolean isGalaxyS() {
return isGT9000() || isSC02B() || isSGHI896() || isSPHD700();
}
public static final boolean hasTwoCamerasRear0Front1() {
return isSPHD700() || isADR6400();
}
// HTC
private static final boolean isADR6400() {
return Build.MODEL.startsWith("ADR6400") || Build.DEVICE.startsWith("ADR6400");
} // HTC Thunderbolt
// Galaxy S variants
private static final boolean isSPHD700() {return Build.DEVICE.startsWith("SPH-D700");} // Epic
private static boolean isSGHI896() {return Build.DEVICE.startsWith("SGH-I896");} // Captivate
private static boolean isGT9000() {return Build.DEVICE.startsWith("GT-I9000");} // Galaxy S
private static boolean isSC02B() {return Build.DEVICE.startsWith("SC-02B");} // Docomo
private static boolean isGTP1000() {return Build.DEVICE.startsWith("GT-P1000");} // Tab
/* private static final boolean log(final String msg) {
Log.d(msg);
return true;
}*/
/* Not working as now
* Calling from Galaxy S to PC is "usable" even with no hack; other side is not even with this one*/
public static void galaxySSwitchToCallStreamUnMuteLowerVolume(AudioManager am) {
// Switch to call audio channel (Galaxy S)
am.setSpeakerphoneOn(false);
sleep(200);
// Lower volume
am.setStreamVolume(AudioManager.STREAM_VOICE_CALL, 1, 0);
// Another way to select call channel
am.setMode(AudioManager.MODE_NORMAL);
sleep(200);
// Mic is muted if not doing this
am.setMicrophoneMute(true);
sleep(200);
am.setMicrophoneMute(false);
sleep(200);
}
public static final void sleep(int time) {
try {
Thread.sleep(time);
} catch(InterruptedException ie){}
}
public static void dumpDeviceInformation() {
StringBuilder sb = new StringBuilder(" ==== Phone information dump ====\n");
sb.append("DEVICE=").append(Build.DEVICE).append("\n");
sb.append("MODEL=").append(Build.MODEL).append("\n");
//MANUFACTURER doesn't exist in android 1.5.
//sb.append("MANUFACTURER=").append(Build.MANUFACTURER).append("\n");
sb.append("SDK=").append(Build.VERSION.SDK);
Log.i(sb.toString());
}
public static boolean needSoftvolume() {
return isGalaxySOrTab();
}
public static boolean needRoutingAPI() {
return Version.sdkStrictlyBelow(5) /*<donut*/;
}
public static boolean needGalaxySAudioHack() {
return isGalaxySOrTab() && !isSC02B();
}
public static boolean needPausingCallForSpeakers() {
// return false;
return isGalaxySOrTab() && !isSC02B();
}
public static boolean hasTwoCameras() {
return isSPHD700() || isGalaxySOrTabWithFrontCamera();
}
public static boolean hasCamera() {
if (Version.sdkAboveOrEqual(Version.API09_GINGERBREAD_23)) {
int nb = 0;
try {
nb = (Integer) Camera.class.getMethod("getNumberOfCameras", (Class[])null).invoke(null);
} catch (Exception e) {
Log.e("Error getting number of cameras");
}
return nb > 0;
}
Log.i("Hack: considering there IS a camera.\n"
+ "If it is not the case, report DEVICE and MODEL to linphone-users@nongnu.org");
return true;
}
}

View file

@ -31,6 +31,7 @@ class LinphoneCallImpl implements LinphoneCall {
native private int getState(long nativePtr);
private native long getCurrentParamsCopy(long nativePtr);
private native void enableCamera(long nativePtr, boolean enabled);
private native boolean cameraEnabled(long nativePtr);
private native void enableEchoCancellation(long nativePtr,boolean enable);
private native boolean isEchoCancellationEnabled(long nativePtr) ;
private native void enableEchoLimiter(long nativePtr,boolean enable);
@ -79,6 +80,10 @@ class LinphoneCallImpl implements LinphoneCall {
public void enableCamera(boolean enabled) {
enableCamera(nativePtr, enabled);
}
@Override
public boolean cameraEnabled() {
return cameraEnabled(nativePtr);
}
public boolean equals(Object call) {
return nativePtr == ((LinphoneCallImpl)call).nativePtr;
}

View file

@ -22,7 +22,8 @@ import java.io.File;
import java.io.IOException;
import java.util.Vector;
import android.view.SurfaceView;
import org.linphone.LinphoneManager;
import org.linphone.mediastream.video.AndroidVideoWindowImpl;
class LinphoneCoreImpl implements LinphoneCore {
@ -68,8 +69,7 @@ class LinphoneCoreImpl implements LinphoneCore {
private native void stopDtmf(long nativePtr);
private native void setVideoWindowId(long nativePtr, Object wid);
private native void setPreviewWindowId(long nativePtr, Object wid);
private AndroidVideoWindowImpl mVideoWindow;
private AndroidVideoWindowImpl mPreviewWindow;
private native void setDeviceRotation(long nativePtr, int rotation);
private native void addFriend(long nativePtr,long friend);
private native void setPresenceInfo(long nativePtr,int minute_away, String alternative_contact,int status);
private native long createChatRoom(long nativePtr,String to);
@ -104,6 +104,8 @@ class LinphoneCoreImpl implements LinphoneCore {
private native void setDownloadPtime(long nativePtr, int ptime);
private native void setZrtpSecretsCache(long nativePtr, String file);
private native void enableEchoLimiter(long nativePtr2, boolean val);
private native int setVideoDevice(long nativePtr2, int id);
private native int getVideoDevice(long nativePtr2);
LinphoneCoreImpl(LinphoneCoreListener listener, File userConfig,File factoryConfig,Object userdata) throws IOException {
mListener=listener;
@ -327,33 +329,15 @@ class LinphoneCoreImpl implements LinphoneCore {
return new LinphoneChatRoomImpl(createChatRoom(nativePtr,to));
}
public synchronized void setPreviewWindow(Object w) {
if (mPreviewWindow!=null)
mPreviewWindow.setListener(null);
mPreviewWindow=new AndroidVideoWindowImpl((SurfaceView)w);
mPreviewWindow.setListener(new AndroidVideoWindowImpl.VideoWindowListener(){
public void onSurfaceDestroyed(AndroidVideoWindowImpl vw) {
setPreviewWindowId(nativePtr,null);
}
public void onSurfaceReady(AndroidVideoWindowImpl vw) {
setPreviewWindowId(nativePtr,vw);
}
});
setPreviewWindowId(nativePtr,w);
}
public synchronized void setVideoWindow(Object w) {
if (mVideoWindow!=null)
mVideoWindow.setListener(null);
mVideoWindow=new AndroidVideoWindowImpl((SurfaceView) w);
mVideoWindow.setListener(new AndroidVideoWindowImpl.VideoWindowListener(){
public void onSurfaceDestroyed(AndroidVideoWindowImpl vw) {
setVideoWindowId(nativePtr,null);
}
public void onSurfaceReady(AndroidVideoWindowImpl vw) {
setVideoWindowId(nativePtr,vw);
}
});
setVideoWindowId(nativePtr, w);
}
public synchronized void setDeviceRotation(int rotation) {
setDeviceRotation(nativePtr, rotation);
}
public synchronized void enableVideo(boolean vcap_enabled, boolean display_enabled) {
enableVideo(nativePtr,vcap_enabled, display_enabled);
}
@ -507,5 +491,16 @@ class LinphoneCoreImpl implements LinphoneCore {
public void enableEchoLimiter(boolean val) {
enableEchoLimiter(nativePtr,val);
}
@Override
public void setVideoDevice(int id) {
Log.i("Setting camera id :", id);
if (setVideoDevice(nativePtr, id) != 0) {
Log.e("Failed to set video device to id:", id);
}
}
@Override
public int getVideoDevice() {
return getVideoDevice(nativePtr);
}
}

View file

@ -18,6 +18,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.linphone.core;
import org.linphone.mediastream.video.capture.hwconf.Hacks;
import android.os.Build;
/**

View file

@ -1,87 +0,0 @@
/*
JavaCameraRecordImpl.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.tutorials;
import org.linphone.core.Log;
import org.linphone.core.video.AndroidCameraRecord;
import android.hardware.Camera;
import android.hardware.Camera.PreviewCallback;
import android.hardware.Camera.Size;
import android.widget.TextView;
/**
* This is an helper class, not a test activity.
*
* @author Guillaume Beraudo
*
*/
class JavaCameraRecordImpl extends AndroidCameraRecord implements PreviewCallback {
private TextView debug;
private long count = 0;
private float averageCalledRate;
private long startTime;
private long endTime;
private int fps;
public JavaCameraRecordImpl(AndroidCameraRecord.RecorderParams parameters) {
super(parameters);
storePreviewCallBack(this);
fps = Math.round(parameters.fps);
}
public void setDebug(TextView debug) {
this.debug = debug;
}
public void onPreviewFrame(byte[] data, Camera camera) {
Size s = camera.getParameters().getPreviewSize();
int expectedBuffLength = s.width * s.height * 3 /2;
if (expectedBuffLength != data.length) {
Log.e("onPreviewFrame called with bad buffer length ", data.length,
" whereas expected is ", expectedBuffLength, " don't calling putImage");
return;
}
if ((count % 2 * fps) == 0) {
endTime = System.currentTimeMillis();
averageCalledRate = (100000 * 2 * fps) / (endTime - startTime);
averageCalledRate /= 100f;
startTime = endTime;
}
count++;
String msg = "Frame " + count + ": " + data.length + "bytes (avg="+averageCalledRate+"frames/s)";
if (debug != null) debug.setText(msg);
Log.d("onPreviewFrame:", msg);
}
@Override
protected void lowLevelSetPreviewCallback(Camera camera, PreviewCallback cb) {
camera.setPreviewCallback(cb);
}
}

View file

@ -1,194 +0,0 @@
/*
TutorialHelloWorldActivity.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.tutorials;
import static org.linphone.core.VideoSize.CIF;
import static org.linphone.core.VideoSize.HVGA;
import static org.linphone.core.VideoSize.QCIF;
import static org.linphone.core.VideoSize.QVGA;
import java.util.Stack;
import org.linphone.R;
import org.linphone.core.Log;
import org.linphone.core.VideoSize;
import org.linphone.core.video.AndroidCameraRecord;
import android.app.Activity;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.SurfaceHolder.Callback;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.TextView;
/**
* Activity for displaying and starting the HelloWorld example on Android phone.
*
* @author Guillaume Beraudo
*
*/
public class TestVideoActivity extends Activity implements Callback, OnClickListener {
private SurfaceView surfaceView;
private static final int rate = 7;
private JavaCameraRecordImpl recorder;
private TextView debugView;
private Button nextSize;
private Button changeCamera;
private Button changeOrientation;
private AndroidCameraRecord.RecorderParams params;
private Stack<VideoSize> videoSizes = createSizesToTest();
private int currentCameraId = 2;
private boolean currentOrientationIsPortrait = false;
private int width;
private int height;
private boolean started;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.videotest);
surfaceView=(SurfaceView)findViewById(R.id.videotest_surfaceView);
nextSize = (Button) findViewById(R.id.test_video_size);
nextSize.setOnClickListener(this);
changeCamera = (Button) findViewById(R.id.test_video_camera);
changeCamera.setText("Cam"+otherCameraId(currentCameraId));
changeCamera.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
changeCamera.setText("Cam"+currentCameraId);
currentCameraId = otherCameraId(currentCameraId);
updateRecording();
}
});
changeOrientation = (Button) findViewById(R.id.test_video_orientation);
changeOrientation.setText(orientationToString(!currentOrientationIsPortrait));
changeOrientation.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
currentOrientationIsPortrait = !currentOrientationIsPortrait;
changeOrientation.setText(orientationToString(!currentOrientationIsPortrait));
if (width == 0 || height == 0) return;
int newWidth = currentOrientationIsPortrait? Math.min(height, width) : Math.max(height, width);
int newHeight = currentOrientationIsPortrait? Math.max(height, width) : Math.min(height, width);
changeSurfaceViewLayout(newWidth, newHeight); // will change width and height on surfaceChanged
}
});
SurfaceHolder holder = surfaceView.getHolder();
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
holder.addCallback(this);
debugView = (TextView) findViewById(R.id.videotest_debug);
}
protected void updateRecording() {
if (width == 0 || height == 0) return;
if (recorder != null) recorder.stopPreview();
params = new AndroidCameraRecord.RecorderParams(0);
params.surfaceView = surfaceView;
params.width = width;
params.height = height;
params.fps = rate;
params.cameraId = currentCameraId;
recorder = new JavaCameraRecordImpl(params);
// recorder.setDebug(debugView);
debugView.setText(orientationToString(currentOrientationIsPortrait)
+ " w="+width + " h="+height+ " cam"+currentCameraId);
recorder.startPreview();
}
private String orientationToString(boolean orientationIsPortrait) {
return orientationIsPortrait? "Por" : "Lan";
}
private int otherCameraId(int currentId) {
return (currentId == 2) ? 1 : 2;
}
public void onClick(View v) {
nextSize.setText("Next");
started=true;
if (videoSizes.isEmpty()) {
videoSizes = createSizesToTest();
}
VideoSize size = videoSizes.pop();
changeSurfaceViewLayout(size.width, size.height);
// on surface changed the recorder will be restarted with new values
// and the surface will be resized
}
private void changeSurfaceViewLayout(int width, int height) {
LayoutParams params = surfaceView.getLayoutParams();
params.height = height;
params.width = width;
surfaceView.setLayoutParams(params);
}
private Stack<VideoSize> createSizesToTest() {
Stack<VideoSize> stack = new Stack<VideoSize>();
stack.push(VideoSize.createStandard(QCIF, false));
stack.push(VideoSize.createStandard(CIF, false));
stack.push(VideoSize.createStandard(QVGA, false));
stack.push(VideoSize.createStandard(HVGA, false));
stack.push(new VideoSize(640,480));
stack.push(new VideoSize(800,480));
return stack;
}
public void surfaceDestroyed(SurfaceHolder holder) {
surfaceView = null;
Log.d("Video capture surface destroyed");
if (recorder != null) recorder.stopPreview();
}
public void surfaceCreated(SurfaceHolder holder) {
Log.d("Video capture surface created");
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (!started) return;
if (recorder != null) recorder.stopPreview();
this.width = width;
this.height = height;
updateRecording();
}
}

View file

@ -1,66 +0,0 @@
/*
AndroidCameraConf.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.List;
import org.linphone.core.VideoSize;
/**
* @author Guillaume Beraudo
*
*/
interface AndroidCameraConf {
AndroidCameras getFoundCameras();
int getNumberOfCameras();
int getCameraOrientation(int cameraId);
boolean isFrontCamera(int cameraId);
List<VideoSize> getSupportedPreviewSizes(int cameraId);
/**
* Default: no front; rear=0; default=rear
* @author Guillaume Beraudo
*
*/
class AndroidCameras {
Integer front;
Integer rear = 0;
Integer defaultC = rear;
boolean hasFrontCamera() { return front != null; }
boolean hasRearCamera() { return rear != null; }
boolean hasSeveralCameras() { return front != rear && front != null; }
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Cameras :");
if (rear != null) sb.append(" rear=").append(rear);
if (front != null) sb.append(" front=").append(front);
if (defaultC != null) sb.append(" default=").append(defaultC);
return sb.toString();
}
}
}

View file

@ -1,113 +0,0 @@
/*
AndroidCameraConf.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.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 {
private AndroidCameras foundCameras;
public AndroidCameras getFoundCameras() {return foundCameras;}
public AndroidCameraConf5() {
// Defaults
foundCameras = new AndroidCameras();
if (Hacks.isGalaxySOrTab()) {
Log.d("Hack Galaxy S : has one or more cameras");
if (Hacks.isGalaxySOrTabWithFrontCamera()) {
Log.d("Hack Galaxy S : HAS a front camera with id=2");
foundCameras.front = 2;
} else {
Log.d("Hack Galaxy S : NO front camera");
}
Log.d("Hack Galaxy S : HAS a rear camera with id=1");
foundCameras.rear = 1;
foundCameras.defaultC = foundCameras.rear;
} else if (Hacks.hasTwoCamerasRear0Front1()) {
Log.d("Hack SPHD700 has 2 cameras a rear with id=0 and a front with id=1");
foundCameras.front = 1;
}
}
public int getNumberOfCameras() {
Log.i("Detecting the number of cameras");
if (Hacks.hasTwoCamerasRear0Front1() || Hacks.isGalaxySOrTabWithFrontCamera()) {
Log.d("Hack: we know this model has 2 cameras");
return 2;
} else
return 1;
}
public int getCameraOrientation(int cameraId) {
// Use hacks to guess orientation of the camera
if (Hacks.isGalaxySOrTab() && isFrontCamera(cameraId)) {
Log.d("Hack Galaxy S : front camera mounted landscape");
// mounted in landscape for a portrait phone orientation
// |^^^^^^^^|
// | ____ |
// | |____| |
// | |
// | |
// | Phone |
// |________|
return 180;
}
return 90;
}
public boolean isFrontCamera(int cameraId) {
// Use hacks to guess facing of the camera
if (cameraId == 2 && Hacks.isGalaxySOrTab()) {
Log.d("Hack Galaxy S : front camera has id=2");
return true;
} else if (cameraId == 1 && Hacks.hasTwoCamerasRear0Front1()) {
Log.d("Hack SPHD700 : front camera has id=1");
return true;
}
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

@ -1,81 +0,0 @@
/*
AndroidCameraConf9.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.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() {
foundCameras = new AndroidCameras();
for (int id=0; id < getNumberOfCameras(); id++) {
if (foundCameras.defaultC == null)
foundCameras.defaultC = id;
if (isFrontCamera(id)) {
foundCameras.front = id;
} 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();
}
public int getCameraOrientation(int cameraId) {
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
Log.d("Camera info for ",cameraId,": orientation=",info.orientation);
return info.orientation;
}
public boolean isFrontCamera(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;
}
public List<VideoSize> getSupportedPreviewSizes(int cameraId) {
return supportedSizes.get(cameraId);
}
}

View file

@ -1,279 +0,0 @@
/*
AndroidCameraRecordImpl.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.video;
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;
import android.hardware.Camera.ErrorCallback;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PreviewCallback;
import android.hardware.Camera.Size;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
public abstract class AndroidCameraRecord implements AutoFocusCallback {
protected Camera camera;
private RecorderParams params;
private PreviewCallback storedPreviewCallback;
private boolean previewStarted;
private List <VideoSize> supportedVideoSizes;
private Size currentPreviewSize;
public AndroidCameraRecord(RecorderParams parameters) {
this.params = parameters;
}
protected List<Size> getSupportedPreviewSizes(Camera.Parameters parameters) {
return Collections.emptyList();
}
private int[] findClosestEnclosingFpsRange(int expectedFps, List<int[]> fpsRanges) {
Log.d("Searching for closest fps range from ",expectedFps);
int measure = Integer.MAX_VALUE;
int[] closestRange = fpsRanges.get(0);
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");
throw new RuntimeException("Video recorder already started");
// return
}
if (params.surfaceView.getVisibility() != SurfaceView.VISIBLE) {
// Illegal state
Log.e("Illegal state: video capture surface view is not visible");
return;
}
Log.d("Trying to open camera with id ", params.cameraId);
if (camera != null) {
Log.e("Camera is not null, ?already open? : aborting");
return;
}
camera = openCamera(params.cameraId);
camera.setErrorCallback(new ErrorCallback() {
public void onError(int error, Camera camera) {
Log.e("Camera error : ", error);
}
});
Camera.Parameters parameters=camera.getParameters();
if (Version.sdkStrictlyBelow(Version.API09_GINGERBREAD_23)) {
parameters.set("camera-id",params.cameraId);
}
if (supportedVideoSizes == null) {
supportedVideoSizes = VideoUtil.createList(getSupportedPreviewSizes(parameters));
}
if (params.width >= params.height) {
parameters.setPreviewSize(params.width, params.height);
} else {
// invert height and width
parameters.setPreviewSize(params.height, params.width);
}
// should setParameters and get again to have the real one??
currentPreviewSize = parameters.getPreviewSize();
// Frame rate
if (Version.sdkStrictlyBelow(Version.API09_GINGERBREAD_23)) {
// Select the supported fps just faster than the target rate
List<Integer> supportedFrameRates=parameters.getSupportedPreviewFrameRates();
if (supportedFrameRates != null && supportedFrameRates.size() > 0) {
Collections.sort(supportedFrameRates);
int selectedRate = -1;
for (Integer rate : supportedFrameRates) {
selectedRate=rate;
if (rate >= params.fps) break;
}
parameters.setPreviewFrameRate(selectedRate);
}
} else {
List<int[]> supportedRanges = parameters.getSupportedPreviewFpsRange();
int[] range=findClosestEnclosingFpsRange((int)(1000*params.fps), supportedRanges);
parameters.setPreviewFpsRange(range[0], range[1]);
}
onSettingCameraParameters(parameters);
camera.setParameters(parameters);
SurfaceHolder holder = params.surfaceView.getHolder();
try {
camera.setPreviewDisplay(holder);
}
catch (Throwable t) {
Log.e(t,"Exception in Video capture setPreviewDisplay()");
}
try {
camera.startPreview();
} catch (Throwable e) {
Log.e("Error, can't start camera preview. Releasing camera!");
camera.release();
camera = null;
return;
}
previewStarted = true;
// Activate autofocus
if (Camera.Parameters.FOCUS_MODE_AUTO.equals(parameters.getFocusMode())) {
OnClickListener svClickListener = new OnClickListener() {
public void onClick(View v) {
Log.i("Auto focus requested");
camera.autoFocus(AndroidCameraRecord.this);
}
};
params.surfaceView.setOnClickListener(svClickListener);
// svClickListener.onClick(null);
} else {
params.surfaceView.setOnClickListener(null);
}
// Register callback to get capture buffer
lowLevelSetPreviewCallback(camera, storedPreviewCallback);
onPreviewStarted(camera);
}
protected Camera openCamera(int cameraId) {
return Camera.open();
}
protected void onSettingCameraParameters(Parameters parameters) {}
/**
* Hook.
* @param camera
*/
public void onPreviewStarted(Camera camera) {}
public void storePreviewCallBack(PreviewCallback cb) {
this.storedPreviewCallback = cb;
if (camera == null) {
Log.w("Capture camera not ready, storing preview callback");
return;
}
lowLevelSetPreviewCallback(camera, cb);
}
public void stopPreview() {
if (!previewStarted) return;
lowLevelSetPreviewCallback(camera, null);
camera.stopPreview();
camera.release();
camera=null;
Log.d("Camera released");
currentPreviewSize = null;
previewStarted = false;
}
public void stopCaptureCallback() {
if (camera != null) {
lowLevelSetPreviewCallback(camera, null);
}
}
protected abstract void lowLevelSetPreviewCallback(Camera camera, PreviewCallback cb);
public static class RecorderParams {
public static enum MirrorType {NO, HORIZONTAL, CENTRAL, VERTICAL};
public float fps;
public int height;
public int width;
final long filterDataNativePtr;
public int cameraId;
public boolean isFrontCamera;
public int rotation;
public SurfaceView surfaceView;
public MirrorType mirror = MirrorType.NO;
public int phoneOrientation;
public RecorderParams(long ptr) {
filterDataNativePtr = ptr;
}
}
public boolean isStarted() {
return previewStarted;
}
public List<VideoSize> getSupportedVideoSizes() {
return supportedVideoSizes;
}
protected int getExpectedBufferLength() {
if (currentPreviewSize == null) return -1;
return currentPreviewSize.width * currentPreviewSize.height * 3 /2;
}
public void onAutoFocus(boolean success, Camera camera) {
if (success) Log.i("Autofocus success");
else Log.i("Autofocus failure");
}
public int getStoredPhoneOrientation() {
return params.phoneOrientation;
}
}

View file

@ -1,139 +0,0 @@
/*
AndroidCameraRecordImpl.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.video;
import java.util.Arrays;
import java.util.List;
import org.linphone.core.Log;
import org.linphone.core.video.AndroidCameraRecord.RecorderParams.MirrorType;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PreviewCallback;
import android.hardware.Camera.Size;
/**
* Record from Android camera.
* Android >= 5 (2.0) version.
*
* @author Guillaume Beraudo
*
*/
class AndroidCameraRecord5 extends AndroidCameraRecord implements PreviewCallback {
private long filterCtxPtr;
private double timeElapsedBetweenFrames = 0;
private long lastFrameTime = 0;
private final double expectedTimeBetweenFrames;
protected final int rotation;
private MirrorType mirror;
// private boolean isUsingFrontCamera;
public AndroidCameraRecord5(RecorderParams parameters) {
super(parameters);
expectedTimeBetweenFrames = 1d / Math.round(parameters.fps);
filterCtxPtr = parameters.filterDataNativePtr;
rotation = parameters.rotation;
mirror = parameters.mirror;
// isUsingFrontCamera = parameters.isFrontCamera;
storePreviewCallBack(this);
}
private native void putImage(long filterCtxPtr, byte[] buffer, int rotate, int mirror);
public void onPreviewFrame(byte[] data, Camera camera) {
if (data == null) {
Log.e("onPreviewFrame Called with null buffer");
return;
}
if (filterCtxPtr == 0l) {
Log.e("onPreviewFrame Called with no filterCtxPtr set");
return;
}
int expectedBuffLength = getExpectedBufferLength();
if (expectedBuffLength != data.length) {
Log.e("onPreviewFrame called with bad buffer length ", data.length,
" whereas expected is ", expectedBuffLength, " don't calling putImage");
return;
}
long curTime = System.currentTimeMillis();
if (lastFrameTime == 0) {
lastFrameTime = curTime;
putImage(filterCtxPtr, data, rotation, mirror.ordinal());
return;
}
double currentTimeElapsed = 0.8 * (curTime - lastFrameTime) / 1000 + 0.2 * timeElapsedBetweenFrames;
if (currentTimeElapsed < expectedTimeBetweenFrames) {
// Log.d("Clipping frame ", Math.round(1 / currentTimeElapsed), " > ", fps);
return;
}
lastFrameTime = curTime;
timeElapsedBetweenFrames = currentTimeElapsed;
// Log.d("onPreviewFrame: ", Integer.toString(data.length));
putImage(filterCtxPtr, data, rotation, mirror.ordinal());
}
protected String selectFocusMode(final List<String> supportedFocusModes) {/*
if (isUsingFrontCamera && supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_FIXED)) {
return Camera.Parameters.FOCUS_MODE_FIXED;
}
if (!isUsingFrontCamera && supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_INFINITY)) {
return Camera.Parameters.FOCUS_MODE_INFINITY;
}*/
if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
return Camera.Parameters.FOCUS_MODE_AUTO;
}
return null; // Should not occur?
}
@Override
protected void onSettingCameraParameters(Parameters parameters) {
super.onSettingCameraParameters(parameters);
List<String> supportedFocusModes = parameters.getSupportedFocusModes();
String focusMode = selectFocusMode(supportedFocusModes);
if (focusMode != null) {
Log.w("Selected focus mode: ", focusMode);
parameters.setFocusMode(focusMode);
} else {
Log.i("No suitable focus mode found in : ", Arrays.toString(supportedFocusModes.toArray()));
}
}
@Override
protected List<Size> getSupportedPreviewSizes(Parameters parameters) {
return parameters.getSupportedPreviewSizes();
}
@Override
protected void lowLevelSetPreviewCallback(Camera camera, PreviewCallback cb) {
camera.setPreviewCallback(cb);
}
}

View file

@ -1,82 +0,0 @@
/*
AndroidCameraRecord8Impl.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.video;
import java.util.List;
import org.linphone.core.Log;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PreviewCallback;
import android.hardware.Camera.Size;
/**
*
* Android >= 8 (2.2) version.
* @author Guillaume Beraudo
*
*/
class AndroidCameraRecord8 extends AndroidCameraRecord5 {
public AndroidCameraRecord8(RecorderParams parameters) {
super(parameters);
}
@Override
protected void lowLevelSetPreviewCallback(Camera camera, PreviewCallback cb) {
if (cb != null) {
Log.d("Setting optimized callback with buffer (Android >= 8). Remember to manage the pool of buffers!!!");
}
camera.setPreviewCallbackWithBuffer(cb);
}
@Override
public void onPreviewStarted(Camera camera) {
super.onPreviewStarted(camera);
Size s = camera.getParameters().getPreviewSize();
int wishedBufferSize = s.height * s.width * 3 / 2;
camera.addCallbackBuffer(new byte[wishedBufferSize]);
camera.addCallbackBuffer(new byte[wishedBufferSize]);
}
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
super.onPreviewFrame(data, camera);
camera.addCallbackBuffer(data);
}
@Override
protected void onSettingCameraParameters(Parameters parameters) {
super.onSettingCameraParameters(parameters);
// Only on v8 hardware
camera.setDisplayOrientation(rotation);
}
@Override
protected String selectFocusMode(final List<String> supportedFocusModes) {
if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_EDOF)) {
return Camera.Parameters.FOCUS_MODE_EDOF;
} else
return super.selectFocusMode(supportedFocusModes);
}
}

View file

@ -1,50 +0,0 @@
/*
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.video;
import java.util.List;
import android.hardware.Camera;
/**
*
* Android >= 9 (2.3) version.
* @author Guillaume Beraudo
*
*/
class AndroidCameraRecord9 extends AndroidCameraRecord8 {
public AndroidCameraRecord9(RecorderParams parameters) {
super(parameters);
}
@Override
protected Camera openCamera(int cameraId) {
return Camera.open(cameraId);
}
@Override
protected String selectFocusMode(final List<String> supportedFocusModes) {
if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
return Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO;
} else
return super.selectFocusMode(supportedFocusModes);
}
}

View file

@ -1,362 +0,0 @@
/*
AndroidCameraRecordManager.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.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.view.OrientationEventListener;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.SurfaceHolder.Callback;
/**
* Manage the video capture, only one for all cameras.
*
* @author Guillaume Beraudo
*
*/
public class AndroidCameraRecordManager {
private static AndroidCameraRecordManager instance;
private OrientationEventListener orientationEventListener;
private OnCapturingStateChangedListener capturingStateChangedListener;
/**
* @return instance
*/
public static final synchronized AndroidCameraRecordManager getInstance() {
if (instance == null) {
instance = new AndroidCameraRecordManager();
}
return instance;
}
private AndroidCameraRecord.RecorderParams parameters;
private final AndroidCameraConf cc;
private SurfaceView surfaceView;
private boolean muted;
private int cameraId;
private AndroidCameraRecord recorder;
private int mAlwaysChangingPhoneOrientation=0;
// singleton
private AndroidCameraRecordManager() {
if (!Version.isVideoCapable()) { // imply sdk>=5
throw new RuntimeException("AndroidCameraRecordManager: hardware is not video capable");
}
cc = Version.sdkAboveOrEqual(9) ? new AndroidCameraConf9() : new AndroidCameraConf5();
Log.i("=== Detected " + cc.getFoundCameras()+ " ===");
cameraId = cc.getFoundCameras().defaultC;
}
public boolean hasSeveralCameras() {
return cc.getFoundCameras().hasSeveralCameras();
}
public boolean hasFrontCamera() {
return cc.getFoundCameras().front != null;
}
public void setUseFrontCamera(boolean value) {
if (!hasFrontCamera()) {
Log.e("setUseFrontCamera(true) while no front camera detected on device: using rear");
value = false;
}
if (cc.isFrontCamera(cameraId) == value) return; // already OK
toggleUseFrontCamera();
}
public boolean isUseFrontCamera() {return cc.isFrontCamera(cameraId);}
public boolean toggleUseFrontCamera() {
boolean previousUseFront = cc.isFrontCamera(cameraId);
cameraId = previousUseFront ? cc.getFoundCameras().rear : cc.getFoundCameras().front;
if (parameters != null) {
parameters.cameraId = cameraId;
parameters.isFrontCamera = !previousUseFront;
if (isRecording()) {
stopVideoRecording();
tryToStartVideoRecording();
}
}
return !previousUseFront;
}
public void setParametersFromFilter(long filterDataPtr, int height, int width, float fps) {
if (recorder != null) {
Log.w("Recorder should not be running");
stopVideoRecording();
}
RecorderParams p = new RecorderParams(filterDataPtr);
p.fps = fps;
p.width = width;
p.height = height;
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 (!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;
}
}
tryToStartVideoRecording();
}
public final void setSurfaceView(final SurfaceView sv) {
SurfaceHolder holder = sv.getHolder();
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
holder.addCallback(new Callback() {
public void surfaceDestroyed(SurfaceHolder holder) {
surfaceView = null;
Log.d("Video capture surface destroyed");
stopVideoRecording();
}
public void surfaceCreated(SurfaceHolder holder) {
surfaceView = sv;
Log.d("Video capture surface created");
tryToStartVideoRecording();
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
Log.d("Video capture surface changed");
}
});
}
/**
* @param muteState
* @return true if mute state changed
*/
public boolean setMuted(boolean muteState) {
if (muteState == muted) return false;
muted = muteState;
if (muted) {
stopVideoRecording();
} else {
tryToStartVideoRecording();
}
return true;
}
public boolean toggleMute() {
setMuted(!muted);
return muted;
}
public boolean isMuted() {
return muted;
}
public void tryResumingVideoRecording() {
if (isRecording()) return;
tryToStartVideoRecording();
}
private synchronized void tryToStartVideoRecording() {
if (orientationEventListener == null) {
throw new RuntimeException("startOrientationSensor was not called");
}
if (muted || surfaceView == null || parameters == null) return;
if (recorder != null) {
Log.e("Recorder already present");
stopVideoRecording();
}
parameters.rotation = bufferRotationToCompensateCameraAndPhoneOrientations();
parameters.surfaceView = surfaceView;
if (Version.sdkAboveOrEqual(9)) {
recorder = new AndroidCameraRecord9(parameters);
} else if (Version.sdkAboveOrEqual(8)) {
recorder = new AndroidCameraRecord8(parameters);
} else if (Version.sdkAboveOrEqual(5)) {
recorder = new AndroidCameraRecord5(parameters);
} else {
throw new RuntimeException("SDK version unsupported " + Version.sdk());
}
recorder.startPreview();
if (capturingStateChangedListener != null) {
capturingStateChangedListener.captureStarted();
}
}
public synchronized void stopVideoRecording() {
if (recorder != null) {
recorder.stopPreview();
recorder = null;
if (capturingStateChangedListener != null) {
capturingStateChangedListener.captureStopped();
}
}
}
public List<VideoSize> supportedVideoSizes() {
Log.d("Using supportedVideoSizes of camera ",cameraId);
return cc.getSupportedPreviewSizes(cameraId);
}
public boolean isRecording() {
if (recorder != null) {
return recorder.isStarted();
}
return false;
}
public void invalidateParameters() {
stopVideoRecording();
parameters = null;
}
/** Depends on currently selected camera, camera mounted portrait/landscape, current phone orientation */
public boolean isFrameToBeShownPortrait() {
final int rotation = bufferRotationToCompensateCameraAndPhoneOrientations();
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 isCameraMountedPortrait() {
return (cc.getCameraOrientation(cameraId) % 180) == 0;
}
private int bufferRotationToCompensateCameraAndPhoneOrientations() {
if (Version.sdkStrictlyBelow(Version.API08_FROYO_22)) {
// Don't perform any rotation
// Phone screen should use fitting orientation
return 0;
}
final int phoneOrientation = mAlwaysChangingPhoneOrientation;
final int cameraOrientation = cc.getCameraOrientation(cameraId);
int frontCameraCorrection = 0;
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,
" degrees: camera_orientation=",cameraOrientation,
" phone_orientation=", phoneOrientation);
return rotation;
}
/**
* Register a sensor to track phoneOrientation changes
*/
public void startOrientationSensor(Context c) {
if (orientationEventListener == null) {
orientationEventListener = new LocalOrientationEventListener(c);
orientationEventListener.enable();
}
}
private class LocalOrientationEventListener extends OrientationEventListener {
public LocalOrientationEventListener(Context context) {
super(context);
}
@Override
public void onOrientationChanged(final int o) {
if (o == OrientationEventListener.ORIENTATION_UNKNOWN) return;
int degrees=270;
if (o < 45 || o >315) degrees=0;
else if (o<135) degrees=90;
else if (o<225) degrees=180;
if (mAlwaysChangingPhoneOrientation == degrees) return;
Log.i("Phone orientation changed to ", degrees);
mAlwaysChangingPhoneOrientation = degrees;
}
}
/**
* @return true if linphone core configured to send a A buffer while phone orientation induces !A buffer (A=landscape or portrait)
*/
public boolean isOutputOrientationMismatch(LinphoneCore lc) {
final boolean currentlyPortrait = lc.getPreferredVideoSize().isPortrait();
final boolean shouldBePortrait = isFrameToBeShownPortrait();
return currentlyPortrait ^ shouldBePortrait;
}
public void setOnCapturingStateChanged(OnCapturingStateChangedListener listener) {
this.capturingStateChangedListener=listener;
}
public static interface OnCapturingStateChangedListener {
void captureStarted();
void captureStopped();
}
}

View file

@ -1,5 +1,4 @@
include $(root-dir)/submodules/externals/build/ffmpeg/Android_libavutil.mk
include $(root-dir)/submodules/externals/build/ffmpeg/Android_libavcodec.mk
include $(root-dir)/submodules/externals/build/ffmpeg/Android_libswscale.mk
include $(root-dir)/submodules/externals/build/ffmpeg/Android_libavcore.mk
include $(linphone-root-dir)/submodules/externals/build/ffmpeg/Android_libavutil.mk
include $(linphone-root-dir)/submodules/externals/build/ffmpeg/Android_libavcodec.mk
include $(linphone-root-dir)/submodules/externals/build/ffmpeg/Android_libswscale.mk
include $(linphone-root-dir)/submodules/externals/build/ffmpeg/Android_libavcore.mk

@ -1 +1 @@
Subproject commit 09bf68a5f14d756872bb28a9a860c853bf9ad06a
Subproject commit 9db25e99e4ba2997c20ed7aab7301f772facda81